如何跨所有浏览器控制网页缓存? 技术背景 在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" ); header ("Pragma: no-cache" ); header ("Expires: 0" );
Java Servlet或Node.js 1 2 3 response.setHeader("Cache-Control" , "no-cache, no-store, must-revalidate" ); response.setHeader("Pragma" , "no-cache" ); response.setHeader("Expires" , "0" );
ASP.NET-MVC 1 2 3 4 Response.Cache.SetCacheability(HttpCacheability.NoCache); Response.Cache.AppendCacheExtension("no-store, must-revalidate" ); Response.AppendHeader("Pragma" , "no-cache" ); Response.AppendHeader("Expires" , "0" );
ASP.NET Web API 1 2 3 4 5 6 7 8 9 10 11 response.Headers.CacheControl = new CacheControlHeaderValue { NoCache = true , NoStore = true , MustRevalidate = true }; response.Headers.Pragma.ParseAdd("no-cache" ); response.Content?.Headers.TryAddWithoutValidation("Expires" , 0. ToString());
ASP.NET 1 2 3 Response.AppendHeader("Cache-Control" , "no-cache, no-store, must-revalidate" ); Response.AppendHeader("Pragma" , "no-cache" ); Response.AppendHeader("Expires" , "0" );
ASP.NET Core v3 1 2 3 4 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" Response.addHeader "Pragma" , "no-cache" Response.addHeader "Expires" , "0"
Ruby on Rails 1 2 3 headers["Cache-Control" ] = "no-cache, no-store, must-revalidate" headers["Pragma" ] = "no-cache" headers["Expires" ] = "0"
Python/Flask 1 2 3 4 response = make_response(render_template(...)) response.headers["Cache-Control" ] = "no-cache, no-store, must-revalidate" response.headers["Pragma" ] = "no-cache" response.headers["Expires" ] = "0"
Python/Django 1 2 3 response["Cache-Control" ] = "no-cache, no-store, must-revalidate" response["Pragma" ] = "no-cache" response["Expires" ] = "0"
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" ) responseWriter.Header().Set("Pragma" , "no-cache" ) responseWriter.Header().Set("Expires" , "0" )
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-store
、Cache-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=0
、Cache-Control: no-cache
、Cache-Control: no-store
、Cache-Control: must-revalidate
+ Expires: 0
、Cache-Control: must-revalidate
+ Expires: Sat, 12 Oct 1991 05:00:00 GMT
、Pragma: 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浏览器在使用后退按钮时显示缓存内容。
某些浏览器仍然缓存页面 如果某些浏览器仍然缓存页面,可以检查服务器是否自动包含了默认的缓存头信息,或者是否存在其他中间代理服务器对缓存进行了处理。此外,还可以尝试在不同的场景下调整缓存头信息,以确保在所有浏览器中都能达到预期的效果。