Apache 未发送 304 响应(如果启用了 mod_deflate 和 AddOutputFilterByType)

Posted

技术标签:

【中文标题】Apache 未发送 304 响应(如果启用了 mod_deflate 和 AddOutputFilterByType)【英文标题】:Apache is not sending 304 response (if mod_deflate and AddOutputFilterByType is enabled) 【发布时间】:2010-10-28 04:09:35 【问题描述】:

我在我的 Apache httpd.conf 中添加了以下行:-

AddOutputFilterByType DEFLATE text/html text/css application/javascript application/x-javascript application/json

我有一个包含脚本的 html 文件 (test.html):-

<script type="text/javascript" src="/test.js"></script>

问题是,每次我加载test.html时,test.js也加载了HTTP状态:200。

问题是:为什么不满足条件GET?

如果我注释掉 httpd.conf 中的“AddOutputFilterByType”行,Apache 会发送 304。

如果我在 httpd.conf 中启用 AddOutputFilterByType,请求头是:-

主持人:优化 用户代理:Mozilla/5.0(Windows;U;Windows NT 6.0;en-US;rv:1.9.0.10)Gecko/2009042316 Firefox/3.0.10 GTB5 (.NET CLR 3.5.30729) Firephp/0.2.4 接受: */* 接受语言:en-us,en;q=0.5 接受编码:gzip,放气 接受字符集:ISO-8859-1,utf-8;q=0.7,*;q=0.7 保活:300 连接:保持活动 参考:http://optimize/ 饼干:PHPSESSID=nbq6h0eeahkshkcbc6ctu2j2b4 If-Modified-Since: 2009 年 5 月 19 日星期二 07:06:46 GMT 如果-无匹配:“2000000000717f-2c25a-46a3e8dcc2ad8”-gzip 缓存控制:max-age=0

响应头是:-

日期:格林威治标准时间 2009 年 5 月 22 日星期五 07:03:40 服务器:Apache/2.2.9 (Win32) PHP/5.2.6 最后修改时间:2009 年 5 月 19 日星期二 07:06:46 GMT Etag:“2000000000717f-2c25a-46a3e8dcc2ad8”-gzip 接受范围:字节 变化:接受编码 内容编码:gzip 内容长度:52583 保活:超时=5,最大值=98 连接:保持活动 内容类型:应用程序/javascript

更新:我注意到,如果我禁用 ETag,它可以正常工作。我的意思是它发送 304。

FileETag None

但我真的很想保持 ETag 原样(我知道存在 inode 泄露问题)。

【问题讨论】:

您找到解决方法了吗? Apache 2.4 似乎也在做同样的事情,总是为 gzip 的内容发送 200 响应 有什么理由要保留 ETags?如果您只是提供已经有 Last-Modified 重新验证日期的静态内容,那么拥有它们没有任何意义。当然,取消它们将是目前最简单的符合 RFC 的解决方法。 ETags 需要的。如果您想用旧版本替换内容(例如,需要将 javascript 文件恢复到旧版本而没有引入错误)并且在使用恢复文件的旧日期时,那么简单的日期比较是不够的. 令人着迷的是,这个问题在最初发布近十年后仍然有效 - 在 2019 年的 apache 2.4.35 中偶然发现它。目前在最新的 Debian Stable 上。 @acat 如果我们有多个服务器,我们如何保持 Last-Modified 同步? 【参考方案1】:

这是 Apache 中的一个已知错误。请参阅Apache bug #45023 和summary of Apache 304 etags and mod_deflate。

从 svn 重建将解决此问题。解决方案是恢复将“-gzip”附加到 etag 的更改。但是,存在相关的 HTTP 合规性问题。

如果您无法重建 Apache,错误报告中有建议的运行时配置解决方法:

 RequestHeader  edit "If-None-Match" "^\"(.*)-gzip\"$" "\"$1\""
 Header  edit "ETag" "^\"(.*[^g][^z][^i][^p])\"$" "\"$1-gzip\""

【讨论】:

在 Apache 2.4 上,只有“200 OK”响应——这个解决方案真的有效吗? 这仍然是一个问题,我也无法超越工作(在 Apache 2.4.7 中)。然而,经过更多的挖掘,我也认为 ETags 在 Apache 中并没有那么有用。如果 ETag 是内容的哈希值,它会是最有用的,所以即使时间戳改变了,它仍然可以用来确定内容是否没有改变。对于 Apache,ETag 是 inode、大小和上次修改数据的组合(默认使用大小和上次修改值)。因此,由于它使用文件属性,而不是内容的哈希,我决定将其关闭并改用 Last-Modified。 这似乎还是个问题?我正在使用 Apache 2.4.10 @Inna 你有没有发现这是否仍然是 apache 2.4 的问题? @thatidiotguy 不确定,如果我有同样的问题。 Apache 不会为 .js 和 .css 文档发送 304 标头,图像有 304 正确。我正在代理 nodejs。没有 apache 的 nodejs 正确发送所有 304。【参考方案2】:

“我还认为 ETag 在 Apache 中并没有那么有用。”

错了, 例如,您有一个修改日期设置为 '2016.07.27 05:00:00' 的文件,您将其上传到您的站点,浏览器使用 HTTP 代码 200 获取此文件,然后将其缓存并每次使用 HTTP 304 重新验证。 接下来,您再次上传具有相同文件名的文件,但时间戳较旧'2013.07.27 05:00:00' 和其他内容。 如果在服务器上禁用了 ETag,浏览器将仅使用 If-Modified-Since: 请求来确定服务器上是否更改了文件,因此请求将是 If-Modified-Since: 2016.07.27 05:00:00,但在此日期之后文件不会被修改,所以即使文件已更改,也会返回 HTTP 304。 如果在服务器上启用了 ETag,除了 If-Modified-Since: 之外,还会有一个来自浏览器的 If-None-Match: 标头,它将检测到该文件已更改(默认情况下 - 时间戳不匹配 + 大小不匹配)并且文件将重新下载。

这个问题在 Apache 2.4.23 中仍然存在,所以,我写了一个比上面更好的代码来解决这个问题。逐行展开:

    1) 如果浏览器发送一个结尾有“-gzip”的“If-None-Match”请求,设置变量request_etag=gzip。 2) 编辑请求标头以去除“-gzip”部分。 3) 编辑响应标头以添加“-gzip”部分,但前提是浏览器最初发送了“-gzip”请求或响应内容是 gzip 编码的。

您可以使用负前瞻或负后瞻,正则表达式速度相同,Apache 都支持

\"(.+(?<!-gzip))\"       #using negative lookbehind
\"((?:.(?!-gzip\"))+)\"  #using negative lookahead

测试用例:

    “2e2-5388f9f70c580-afeg” “2e2-5388f9f70c580-gzin” “2e2-5388f9f70c580-gzipd” “2e2-5388f9f70c580-gzip” “2e2-5388f9f70c580gzip”

将此代码复制粘贴到 Apache .conf 中

SetEnvIf           If-None-Match "-gzip\"$" request_etag=gzip
RequestHeader edit If-None-Match "(.+)-gzip\"$" "$1\""
Header edit        ETag     "(.+(?<!-gzip))\"$" "$1-gzip\"" "expr=reqenv('request_etag') == 'gzip' || resp('Content-Encoding') == 'gzip'"

我个人使用以下代码,如果它是 gzip 响应,则最初会去除“-gzip”部分,并且不会重新附加它,因此浏览器将永远不会发送“-gzip”'If-None-Match' 标头。

Header edit ETag "(.+)-gzip\"$" "$1\"" "expr=resp('Content-Encoding') == 'gzip'"

【讨论】:

问题在 Apache 2.4.25 中依然存在。上面的 3 行代码帮助了我,但我需要启用 headers_module【参考方案3】:

我知道这是一个非常古老的问题,但似乎有一个更新的答案。

要让 Apache 不附加 -gzip 后缀,您必须使用值为 NoChangeDeflateAlterETag 指令。

在此处查看相关文档:http://httpd.apache.org/docs/trunk/mod/mod_deflate.html#deflatealteretag

【讨论】:

【参考方案4】:

也许您使用了一个(squid)代理来操纵 HTTP 请求?

【讨论】:

不,我没有使用任何代理。我什至在我的本地主机中尝试。结果相同。

以上是关于Apache 未发送 304 响应(如果启用了 mod_deflate 和 AddOutputFilterByType)的主要内容,如果未能解决你的问题,请参考以下文章

HTTP 304

深入浅出Flask:服务器输出304代表什么

即使使用 django-cors-headers 也得到 304 响应

AEM Dispatcher (4.3.3) 总是返回 200 而不是 304 (Apache 2.4.6)

通过 PHP 缓存图像请求 - If-Modified-Since 未发送

python urllib2.request.add_header 未返回 HTTP 错误 304(未修改)异常