如何跨所有浏览器控制网页缓存?

如何跨所有浏览器控制网页缓存?

技术背景

在Web开发中,网页缓存可以提高网站性能和响应速度,但在某些情况下,如页面内容频繁更新、涉及用户敏感信息等,需要禁用缓存以确保用户每次访问都能获取最新的页面内容。不同浏览器对缓存的处理方式存在差异,因此需要设置一系列合适的HTTP头信息来实现跨浏览器的缓存控制。

实现步骤

确定最小必要的HTTP头信息

为了在所有客户端(包括旧版本浏览器和代理服务器)中实现禁用缓存,推荐使用以下一组HTTP头信息:

  • Cache-Control: no-cache, no-store, must-revalidate:遵循HTTP 1.1规范,用于客户端和代理服务器。no-cache表示在使用缓存副本之前必须先向服务器验证;no-store表示禁止缓存任何副本;must-revalidate表示一旦资源过期,必须重新向服务器验证。
  • Pragma: no-cache:遵循HTTP 1.0规范,用于旧版本浏览器。
  • Expires: 0:遵循HTTP 1.0和1.1规范,用于客户端和代理服务器,将过期时间设置为过去的时间,强制浏览器每次都向服务器请求新的资源。

根据不同场景调整头信息

  • 如果不关心IE6在HTTPS协议下仅使用no-store时的缓存问题,可以省略Cache-Control: no-cache
  • 如果不关心IE6和HTTP 1.0客户端,可以省略Pragma
  • 如果不关心HTTP 1.0代理服务器,可以省略Expires
  • 如果服务器自动包含有效的Date头信息,理论上可以仅依赖Expires而省略Cache-Control,但如果用户修改操作系统日期,可能会导致缓存失效。

在不同技术栈中设置头信息

PHP

1
2
3
header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
header("Pragma: no-cache"); // HTTP 1.0.
header("Expires: 0"); // Proxies.

Java Servlet或Node.js

1
2
3
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setHeader("Expires", "0"); // Proxies.

ASP.NET-MVC

1
2
3
4
Response.Cache.SetCacheability(HttpCacheability.NoCache);  // HTTP 1.1.
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.

ASP.NET Web API

1
2
3
4
5
6
7
8
9
10
11
// `response` is an instance of System.Net.Http.HttpResponseMessage
response.Headers.CacheControl = new CacheControlHeaderValue
{
NoCache = true,
NoStore = true,
MustRevalidate = true
};
response.Headers.Pragma.ParseAdd("no-cache");
// We can't use `response.Content.Headers.Expires` directly
// since it allows only `DateTimeOffset?` values.
response.Content?.Headers.TryAddWithoutValidation("Expires", 0.ToString());

ASP.NET

1
2
3
Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.

ASP.NET Core v3

1
2
3
4
// using Microsoft.Net.Http.Headers
Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate";
Response.Headers[HeaderNames.Expires] = "0";
Response.Headers[HeaderNames.Pragma] = "no-cache";

ASP

1
2
3
Response.addHeader "Cache-Control", "no-cache, no-store, must-revalidate" ' HTTP 1.1.
Response.addHeader "Pragma", "no-cache" ' HTTP 1.0.
Response.addHeader "Expires", "0" ' Proxies.

Ruby on Rails

1
2
3
headers["Cache-Control"] = "no-cache, no-store, must-revalidate" # HTTP 1.1.
headers["Pragma"] = "no-cache" # HTTP 1.0.
headers["Expires"] = "0" # Proxies.

Python/Flask

1
2
3
4
response = make_response(render_template(...))
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" # HTTP 1.1.
response.headers["Pragma"] = "no-cache" # HTTP 1.0.
response.headers["Expires"] = "0" # Proxies.

Python/Django

1
2
3
response["Cache-Control"] = "no-cache, no-store, must-revalidate" # HTTP 1.1.
response["Pragma"] = "no-cache" # HTTP 1.0.
response["Expires"] = "0" # Proxies.

Python/Pyramid

1
2
3
4
5
6
7
request.response.headerlist.extend(
(
('Cache-Control', 'no-cache, no-store, must-revalidate'),
('Pragma', 'no-cache'),
('Expires', '0')
)
)

Go

1
2
3
responseWriter.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
responseWriter.Header().Set("Pragma", "no-cache") // HTTP 1.0.
responseWriter.Header().Set("Expires", "0") // Proxies.

Clojure(需要Ring utils)

1
2
3
4
5
(require '[ring.util.response :as r])
(-> response
(r/header "Cache-Control" "no-cache, no-store, must-revalidate")
(r/header "Pragma" "no-cache")
(r/header "Expires" 0))

Apache .htaccess文件

1
2
3
4
5
<IfModule mod_headers.c>
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires 0
</IfModule>

Firebase Hosting firebase.json

1
2
3
4
5
"headers": [
{ "key": "Cache-Control", "value": "no-cache, no-store, must-revalidate" },
{ "key": "Pragma", "value": "no-cache" },
{ "key": "Expires", "value": "0" }
]

HTML

1
2
3
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

最佳实践

优先使用HTTP响应头

当HTTP响应头和HTML的<meta http-equiv>标签同时存在时,HTTP响应头的优先级更高。因此,建议优先使用HTTP响应头来控制缓存,避免使用HTML的<meta http-equiv>标签,因为在HTML5中,部分<meta http-equiv>标签是无效的。

验证HTTP响应头

可以使用浏览器的开发者工具来验证实际的HTTP响应头。在Chrome、Firefox 23+、IE 9+中,按下F12打开开发者工具,然后切换到“Network”或“Net”选项卡,点击感兴趣的HTTP请求,即可查看详细的HTTP请求和响应信息。

处理文件下载的缓存问题

对于文件下载,建议对其进行缓存,并在URI路径或查询字符串中使用文件版本标识符,以便在文件更新时强制重新下载。如果要对文件下载应用无缓存头信息,需要注意IE7/8在HTTPS协议下的缓存问题。

处理浏览器后退按钮的缓存问题

要确保在使用浏览器后退按钮时也能获取最新的页面内容,可以使用Cache-Control: no-store, must-revalidate,并且页面必须通过HTTPS协议传输。不同浏览器对缓存指令的支持有所不同,以下是一些常见浏览器的解决方案:

  • Chrome(v28.0.1500.95 m):Cache-Control: no-store
  • Firefox(v23.0.1):Cache-Control: no-storeCache-Control: no-cache(仅HTTPS)、Pragma: no-cache(仅HTTPS)、Vary: *(仅HTTPS)
  • Opera(v12.15):Cache-Control: must-revalidate(仅HTTPS)
  • Safari(v5.1.7, 7534.57.2):Cache-Control: no-store<body onunload="">(或仅在HTTPS下使用Cache-Control: no-store
  • IE8(v8.0.6001.18702IC):Cache-Control: must-revalidate, max-age=0Cache-Control: no-cacheCache-Control: no-storeCache-Control: must-revalidate + Expires: 0Cache-Control: must-revalidate + Expires: Sat, 12 Oct 1991 05:00:00 GMTPragma: no-cache(仅HTTPS)、Vary: *(仅HTTPS)

综合以上情况,Cache-Control: no-store, must-revalidate(仅HTTPS)可以在Chrome 28、Firefox 23、IE8、Safari 5.1.7和Opera 12.15中生效。如果无法使用HTTPS并准备忽略Opera浏览器,可以使用Cache-Control: no-store + <body onunload="">

常见问题

HTML的<meta http-equiv>标签不起作用

当页面通过HTTP连接提供时,HTTP响应头的优先级高于HTML的<meta http-equiv>标签。因此,如果HTTP响应头中已经设置了缓存相关信息,HTML的<meta http-equiv>标签将被忽略。此外,在HTML5中,部分<meta http-equiv>标签是无效的。

IIS7+无法正确设置缓存头信息

在IIS7+中,使用Response.Cache.SetCacheability(HttpCacheability.NoCache)Response.Cache.AppendCacheExtension("no-store, must-revalidate")可以正确设置Cache-Control头信息,同时使用Response.AppendHeader("Pragma", "no-cache")Response.AppendHeader("Expires", "-1")设置其他头信息。

Safari浏览器在使用后退按钮时仍然显示缓存内容

可以在<body>标签中添加一个空的onunload事件处理程序属性,如<body onunload="">,以防止Safari浏览器在使用后退按钮时显示缓存内容。

某些浏览器仍然缓存页面

如果某些浏览器仍然缓存页面,可以检查服务器是否自动包含了默认的缓存头信息,或者是否存在其他中间代理服务器对缓存进行了处理。此外,还可以尝试在不同的场景下调整缓存头信息,以确保在所有浏览器中都能达到预期的效果。


如何跨所有浏览器控制网页缓存?
https://119291.xyz/posts/how-to-control-web-page-caching-across-all-browsers/
作者
ww
发布于
2025年5月26日
许可协议