Apache的mod_deflate不支持自动发送预压缩.gz文件,需配合mod_rewrite和mod_headers手动实现:检查.gz存在性、重写路径、设置Content-Encoding与Content-Type,并禁用FileETag以保证缓存一致。

mod_deflate 不会自动压缩已存在的 .gz 文件
Apache 的 mod_deflate 默认只对动态响应(如 PHP 输出)或未压缩的静态文件实时压缩,它不会检查同目录下是否已有 file.js.gz 并直接发送——这是常见误解。你看到的“预压缩生效”,往往是因为浏览器发了带 Accept-Encoding: gzip 的请求,而服务器恰好配置了 mod_headers + mod_rewrite 做了手动路由判断。
- 必须显式启用
mod_rewrite和mod_headers,仅靠mod_deflate无法实现“有 .gz 就发 .gz,没有就 fallback 到实时压缩” - 静态文件需提前用
gzip -k -9 file.js生成同名.gz文件,且权限需与原文件一致(否则 403) - Apache 不会校验
.gz文件是否过期或与源文件不一致——更新file.js后必须手动重生成file.js.gz
用 RewriteRule 匹配并设置 Content-Encoding
核心逻辑是:收到请求 /static/app.js 时,先检查 /static/app.js.gz 是否存在;存在则改写路径、添加响应头,让 Apache 当作 gzip 内容返回。
<ifmodule mod_rewrite.c>
RewriteEngine On
# 检查 .gz 文件是否存在,且客户端支持 gzip
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*)\.([^.]+)$ $1.$2.gz [QSA,L]
<h1>设置 Content-Encoding 和 Vary 头</h1>
<p><ifmodule mod_headers.c>
Header append Vary Accept-Encoding
<filesmatch>
Header set Content-Encoding gzip
Header set Content-Type "text/javascript; charset=utf-8"
</filesmatch></ifmodule></p></ifmodule>
-
FilesMatch "\.gz$"中的Content-Type必须按原始文件类型手动指定,Apache 不会自动推断——app.js.gz返回text/css会导致 JS 解析失败 - 路径重写后,Apache 默认不再触发
mod_mime的类型识别,所以Content-Type必须硬编码或通过AddType配合FilesMatch补全 - 如果使用
Alias或DocumentRoot外的路径(如符号链接),-f条件可能因路径解析失败而跳过,建议用ScriptAlias或确保FollowSymLinks已启用
gzip_static on 在 Nginx 里才原生支持
很多用户查资料时混淆了 Apache 和 Nginx 的能力边界。gzip_static on 是 Nginx 的指令,Apache 没有等价内置指令——试图在 Apache 配置里写 gzip_static on 会直接导致启动失败,错误日志显示 Invalid command 'gzip_static'。
- 别被中文教程误导,Apache 实现等效功能只能靠
RewriteCond + RewriteRule + Header组合 - 若项目已用 Nginx 反向代理 Apache,可在 Nginx 层做
gzip_static,Apache 后端保持纯静态服务,更轻量也更可靠 - 某些第三方模块(如
mod_gzip)曾提供类似功能,但早已停止维护,不兼容 Apache 2.4+,强行编译风险高
ETag 和 Last-Modified 容易错乱
预压缩后,app.js 和 app.js.gz 是两个独立文件,它们的修改时间(mtime)和 inode 通常不同。Apache 默认基于文件系统属性生成 ETag,这会导致同一个逻辑资源出现两个不同 ETag,破坏缓存一致性。
- 必须禁用基于文件属性的 ETag:
FileETag None,改用Header set ETag "W/"v123""手动统一 -
Last-Modified头默认取.gz文件的 mtime,但浏览器实际加载的是 JS 逻辑,应与源文件保持一致——可通过Header edit Last-Modified ".*" "Wed, 01 Jan 2025 00:00:00 GMT"固定,或用脚本同步两文件时间戳 - CDN(如 Cloudflare)可能忽略你设的
ETag,仍按源站响应体哈希生成新值,此时需在 CDN 后台关闭 ETag 透传或强制覆盖
预压缩真正省下的不是 CPU,而是避免每次请求都走 deflate 压缩流水线;但代价是构建流程多一步、缓存头要多管一层、文件同步不能漏——这些细节不显眼,却决定上线后能不能稳定扛住流量峰值。










