一尘不染

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

javascript

我们的调查表明,并非所有浏览器都以统一的方式尊重 HTTP 缓存指令。

出于安全原因,我们不希望应用程序中的某些页面Web 浏览器缓存。这必须至少适用于以下浏览器:

  • Internet Explorer 6+
  • Firefox 1.5+
  • Safari 3+
  • Opera 9+
  • Chrome

我们的要求来自安全测试。从我们的网站注销后,您可以按返回按钮并查看缓存页面。


阅读 143

收藏
2022-02-16

共1个答案

一尘不染

适用于所有提及的客户端(和代理)的正确最小标头集:

Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0

Cache-Control是针对客户端和代理的 HTTP 1.1 规范(并且旁边的某些客户端隐式要求Expires)。这Pragma是针对史前客户端的 HTTP 1.0 规范。这Expires是针对客户端和代理的 HTTP 1.0 和 1.1 规范。在 HTTP 1.1 中,Cache-Control优先于Expires,所以它毕竟只适用于 HTTP 1.0 代理。

如果您不关心 IE6 及其在仅使用 HTTPS 提供页面时损坏的缓存no-store,那么您可以省略Cache-Control: no-cache.

Cache-Control: no-store, must-revalidate
Pragma: no-cache
Expires: 0

如果您不关心 IE6 或 HTTP 1.0 客户端(HTTP 1.1 于 1997 年推出),那么您可以省略Pragma.

Cache-Control: no-store, must-revalidate
Expires: 0

如果您也不关心 HTTP 1.0 代理,那么您可以省略Expires.

Cache-Control: no-store, must-revalidate

另一方面,如果服务器自动包含一个有效的Date标头,那么理论上您也可以省略Cache-Control并仅依赖Expires

Date: Wed, 24 Aug 2016 18:32:02 GMT
Expires: 0

但是,如果最终用户操纵操作系统日期并且客户端软件依赖它,这可能会失败。

如果指定了上述参数,则其他Cache-Control参数如不相关。此处大多数其他答案中包含的标头仅在您确实要缓存请求时才有意义,因此您根本不需要指定它。max-age``Cache-ControlLast-Modified

如何设置?

使用 PHP:

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:

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

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:

// `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:

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

// 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:

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:

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:

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:

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

使用 Python/金字塔:

request.response.headerlist.extend(
    (
        ('Cache-Control', 'no-cache, no-store, must-revalidate'),
        ('Pragma', 'no-cache'),
        ('Expires', '0')
    )
)

使用围棋:

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):

(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文件:

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

使用 HTML:

<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">

HTML 元标记与 HTTP 响应标头

重要的是要知道,当通过 HTTP 连接提供 HTML 页面时,并且HTTP 响应标头和 HTML标记中<meta http-equiv>存在标头,那么 HTTP 响应标头中指定的标头将优先于 HTML 元标记。file://仅当通过URL从本地磁盘文件系统查看页面时,才会使用 HTML 元标记。另见W3 HTML 规范第 5.2.2 章。当您不以编程方式指定它们时请注意这一点,因为网络服务器可以包含一些默认值。

通常,您最好不要指定 HTML 元标记,以避免初学者混淆并依赖硬 HTTP 响应标头。此外,特别是那些<meta http-equiv>标签在 HTML5中是无效的。仅允许HTML5 规范http-equiv中列出的值。

验证实际的 HTTP 响应标头

要验证其中一个,您可以在 Web 浏览器的开发人员工具集的 HTTP 流量监视器中查看/调试它们。您可以在 Chrome/Firefox23+/IE9+ 中按 F12,然后打开“网络”或“网络”选项卡面板,然后单击感兴趣的 HTTP 请求以查看有关 HTTP 请求和响应的所有详细信息。以下屏幕截图来自 Chrome:

Chrome 开发者工具集 HTTP 流量监控器在 stackoverflow.com 上显示 HTTP 响应标头

我也想在文件下载中设置这些标题

首先,这个问答针对的是“网页”(HTML页面),而不是“文件下载”(PDF、zip、Excel等)。您最好将它们缓存起来,并在 URI 路径或查询字符串中的某处使用某些文件版本标识符来强制重新下载已更改的文件。无论如何,在文件下载上应用那些无缓存标头时,当通过 HTTPS 而不是 HTTP 提供文件下载时,请注意 IE7/8 错误。

2022-02-16