我们的调查表明,并非所有浏览器都以统一的方式尊重HTTP缓存指令。
出于安全原因,我们不希望应用程序中的某些页面被web浏览器缓存。这必须至少适用于以下浏览器:
Internet Explorer 6+Firefox 1.5+Safari 3+歌剧9+铬
我们的要求来自安全测试。从我们的网站注销后,您可以按后退按钮并查看缓存的页面。
我们的调查表明,并非所有浏览器都以统一的方式尊重HTTP缓存指令。
出于安全原因,我们不希望应用程序中的某些页面被web浏览器缓存。这必须至少适用于以下浏览器:
Internet Explorer 6+Firefox 1.5+Safari 3+歌剧9+铬
我们的要求来自安全测试。从我们的网站注销后,您可以按后退按钮并查看缓存的页面。
当前回答
当使用浏览器的后退按钮时,BalusC提供的答案中的标题不会阻止Safari 5(以及可能更旧的版本)显示浏览器缓存中的内容。防止这种情况的一种方法是在body标记中添加一个空onunload事件处理程序属性:
<body onunload="">
这个黑客显然破坏了Safari中的前向缓存:当单击后退按钮时,是否存在跨浏览器加载事件?
其他回答
介绍
适用于所有提到的客户端(和代理)的正确的最小标头集:
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
缓存控制符合客户端和代理的HTTP 1.1规范(某些客户端在Expires旁边隐式要求)。Pragma符合史前客户端的HTTP 1.0规范。对于客户端和代理,Expires符合HTTP 1.0和1.1规范。在HTTP 1.1中,缓存控制优先于过期,因此它毕竟只适用于HTTP 1.0代理。
如果您不关心IE6及其在HTTPS上仅提供无存储的页面时损坏的缓存,那么您可以忽略缓存控制:无缓存。
Cache-Control: no-store, must-revalidate
Pragma: no-cache
Expires: 0
如果您不关心IE6或HTTP 1.0客户端(1997年引入了HTTP 1.1),那么可以省略Pragma。
Cache-Control: no-store, must-revalidate
Expires: 0
如果您也不关心HTTP1.0代理,那么可以忽略Expires。
Cache-Control: no-store, must-revalidate
另一方面,如果服务器自动包含有效的Date标头,那么理论上也可以省略CacheControl,只依赖Expires。
Date: Wed, 24 Aug 2016 18:32:02 GMT
Expires: 0
但是,如果最终用户操纵操作系统日期,而客户端软件依赖该日期,则这可能会失败。
如果指定了上述缓存控制参数,则其他缓存控制参数(如最大年龄)无关紧要。这里大多数其他答案中包含的LastModified标头只有在您确实想要缓存请求时才有意思,因此根本不需要指定它。
如何设置?
使用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.
使用RubyonRails:
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/Pyramid:
request.response.headerlist.extend(
(
('Cache-Control', 'no-cache, no-store, must-revalidate'),
('Pragma', 'no-cache'),
('Expires', '0')
)
)
使用Go:
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响应标头
需要知道的是,当HTML页面通过HTTP连接提供服务,并且HTTP响应标头和HTML<meta-HTTP-equiv>标记中都存在标头时,HTTP响应标头中指定的标头将优先于HTML元标记。HTML元标记仅在通过file://URL从本地磁盘文件系统查看页面时使用。另请参见W3 HTML规范第5.2.2章。当您不以编程方式指定它们时,请注意这一点,因为Web服务器可以包括一些默认值。
通常,最好不要指定HTML元标记,以避免初学者混淆,并依赖硬HTTP响应头。此外,特别是那些<meta-http-equiv>标签在HTML5中是无效的。仅允许HTML5规范中列出的http equiv值。
验证实际HTTP响应标头
要验证两者,您可以在web浏览器开发人员工具集的HTTP流量监视器中查看/调试它们。您可以通过在Chrome/Firefox23+/IE9+中按F12,然后打开“网络”或“网络”选项卡面板,然后单击感兴趣的HTTP请求以了解有关HTTP请求和响应的所有详细信息。以下截图来自Chrome:
我也想在文件下载时设置这些标题
首先,这个问题和答案针对的是“网页”(HTML页面),而不是“文件下载”(PDF、zip、Excel等)。您最好将它们缓存起来,并使用URI路径或查询字符串中的某个文件版本标识符强制重新下载已更改的文件。无论如何,当在文件下载上应用这些无缓存头时,当通过HTTPS而不是HTTP提供文件下载时,请注意IE7/8错误。有关详细信息,请参阅IE无法下载foo.jsf。IE无法打开此网站。请求的站点不可用或找不到。
这些指令不会减轻任何安全风险。它们实际上是为了迫使UA更新不稳定的信息,而不是阻止UA保留信息。看到这个类似的问题。至少,不能保证任何路由器、代理等也不会忽略缓存指令。
更积极的是,有关计算机物理访问、软件安装等方面的政策将使您在安全方面领先于大多数公司。如果这些信息的消费者是公众,你唯一能做的就是帮助他们明白,一旦信息到达他们的机器,那台机器就是他们的责任,而不是你的责任。
我只想指出,如果有人想阻止只缓存动态内容,那么应该以编程方式添加这些额外的头。
我编辑了项目的配置文件,不附加缓存头,但这也禁用了缓存静态内容,这通常是不可取的。修改代码中的响应头可以确保缓存图像和样式文件。
这很明显,但仍值得一提。
还有一个警告。请小心使用HttpResponse类中的ClearHeaders方法。如果你鲁莽地使用它,它可能会给你一些瘀伤。就像它给我的。
重定向ActionFilterAttribute事件后,清除所有标头的后果是丢失所有会话数据和TempData存储中的数据。从Action重定向或在重定向时不清除标头更安全。
再次考虑后,我不鼓励所有人使用ClearHeaders方法。最好单独删除标题。为了正确设置缓存控制头,我使用了以下代码:
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.AppendCacheExtension("no-store, must-revalidate");
我用这种方式解决了。
2个注意事项:
1) 服务器端事件不是在后退单击时触发的,而是javascript。
2) 我有两个javascript来读/写cookie
function setCookie(name, value, days)
{
var expires = "";
if (days)
{
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
function getCookie(name)
{
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = ca.length - 1; i >= 0; i--)
{
var c = ca[i];
while (c.charAt(0) == ' ')
{
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) == 0)
{
return c.substring(nameEQ.length, c.length);
}
}
return null;
}
在我的Page_Load中,我插入了以下内容:(这不是在单击后启动的)
protected void Page_Load(object sender, EventArgs e)
{
Page.RegisterClientScriptBlock("", "<script>setCookie('" + Session.SessionID + "', '" + Login + "', '100');</script>");
}
其中“Login”是我的id值,注销后为-1(可以使用其他值,例如布尔值)。
然后在我的页面中添加了以下内容:(这是在单击后启动的)
<script type="text/javascript">
if (getCookie('<%= Session.SessionID %>') < 0)
{
if (history.length > 0)
{
history.go(+1);
}
}
</script>
没有别的。
使用此解决方案,仅在同一浏览器上的每个页面注销后,才在每个页面上启用反向单击。
如果您面临通过SSL和cache:MS Office文件的IE6-IE8下载问题,则可以在POST请求时使用cache:private、no store header和return file。它起作用了。