Apache 作为反向代理时,mod_cache 会默认把请求协议、主机名、端口、路径和查询字符串组合成缓存键。同一台服务器如果同时监听 80 和 8080 端口,即使后端内容完全一致,只要端口字段不同,缓存就会拆成两条独立记录。这种设计在多数场景下是合理的,但对于同一业务通过多端口暴露的情况,会带来重复缓存、磁盘浪费和缓存命中率下降。调整缓存键中的端口范围,核心思路是在不影响内容差异性的前提下,把多个前端端口归一化到同一个缓存标识。

一、Apache 缓存键中的端口是如何工作的
mod_cache 是 Apache HTTP Server 的缓存模块,通常与 mod_cache_disk 或 mod_cache_socache 配合使用。启用磁盘缓存后,每个可缓存响应都会根据缓存键生成一个文件或索引条目。默认缓存键包含请求方法、协议、主机、端口以及标准化后的 URL。例如下面两个请求虽然路径相同,但由于端口不一致,会被视为两个完全不同的缓存对象:
http://www.ippipp.com:80/docs/guide.html http://www.ippipp.com:8080/docs/guide.html
这两条请求生成的缓存键会分别包含 :80 和 :8080。如果后端应用对这两个端口的响应内容完全一样,缓存系统中就出现了冗余数据。更要紧的是,同一个客户端每次通过不同端口访问,都可能穿透到后端重新生成缓存副本,降低了代理层的加速效果。
端口参与缓存键的逻辑本身没有问题,因为 HTTP 协议中端口确实是资源定位的一部分。但在实际部署中,很多团队会同时暴露 80、8080、8880 等端口,有些是为了兼容旧系统,有些是为了负载均衡探测。若这些端口都属于同一套业务,就需要手动调整缓存策略,把端口差异从缓存键中剥离或归一化。
二、使用 CacheKeyBaseURL 统一端口范围
Apache 提供了 CacheKeyBaseURL 指令,可以替换默认的缓存键基础 URL。该指令位于 mod_cache 模块中,语法如下:
CacheKeyBaseURL http://backend.internal:8080
设置后,所有经过该虚拟主机或目录的缓存请求,都会使用 http://backend.internal:8080 作为缓存键的协议、主机和端口部分,原始请求中的这些字段不再参与缓存键计算。这样无论客户端访问的是 80、8080 还是 8880,只要请求路径和查询字符串一致,都会命中同一条缓存记录。
完整配置片段可以这样写:
<IfModule mod_cache.c>
CacheEnable disk /
CacheRoot /var/cache/apache2/mod_cache_disk
CacheKeyBaseURL http://backend.internal:8080
</IfModule>
<IfModule mod_proxy.c>
ProxyPass / http://backend.internal:8080/
ProxyPassReverse / http://backend.internal:8080/
</IfModule>
这里 CacheKeyBaseURL 指定的端口不一定要是唯一后端端口,它只是缓存键中的一个固定分量。如果后端本身监听多个端口,但内容相同,也可以统一成一个代表端口。需要注意,该指令会同时覆盖协议和主机名,因此如果同一虚拟主机还需要代理不同后端的 HTTPS 服务,就不能在全局层随意设置,建议放在单独的 <VirtualHost> 或 <Location> 块中,避免误伤其他业务。
使用 CacheKeyBaseURL 最大的优点是配置简单,几行即可完成端口归一化。缺点也很明显:它把所有请求都拍平到同一个基础 URL,如果不同端口本来对应不同内容,就会造成缓存键冲突。因此只适合端口差异不影响业务内容的场景。
三、借助 Rewrite 按端口段动态调整缓存标识
如果端口范围不是简单合并,而是希望按端口段映射到不同缓存分区,可以借助 mod_rewrite 对请求做预处理。典型做法是在代理之前重写 URL,把端口信息转换成路径前缀或统一的后端端口。例如将所有 8000 到 8999 端口的请求统一代理到内部 8080 端口,同时保持缓存键一致:
RewriteEngine On
RewriteCond %{SERVER_PORT} ^8[0-9]{3}$
RewriteRule ^/(.*)$ http://backend.internal:8080/$1 [P]
这个规则会把 8000、8001、8999 等端口全部重写到 8080 后端,并使用 mod_proxy 的 [P] 标志完成代理。经过重写后,缓存键中的端口取决于重写后的目标 URL,因此原本分散的端口段会被统一。这个方法比 CacheKeyBaseURL 更灵活,因为它可以通过正则表达式定义任意端口范围,而不是只能指定一个固定值。
不过需要注意,mod_rewrite 的重写发生在请求处理较早阶段,而 mod_cache 的缓存查询可能早于重写。实际部署中应确保 mod_cache 的 CacheQuickHandler 设置为 Off,让重写规则有机会在缓存键生成前执行。例如:
CacheQuickHandler Off
RewriteEngine On
RewriteCond %{SERVER_PORT} ^8[0-9]{3}$
RewriteRule ^/(.*)$ http://backend.internal:8080/$1 [P]
如果还需要根据端口段设置不同的缓存过期时间,可以结合 CacheDefaultExpire 或 CacheMaxExpire 在不同目录中配置。例如 /api 目录下的缓存时间短一些,/static 目录下的缓存时间长一些。端口段信息也可以被重写成查询参数,帮助后端日志或监控系统识别来源,但这样会改变缓存键,需要慎重评估。
另一种变体是使用 mod_headers 配合环境变量统一 Host 头。先在 rewrite 阶段根据端口设置环境变量,再由 mod_headers 修改请求头:
RewriteCond %{SERVER_PORT} ^8[0-9]{3}$
RewriteRule .* - [E=CACHE_PORT:8080]
RequestHeader set Host backend.internal:%{CACHE_PORT}e
这种方式可以直接影响后端看到的 Host 头,也能间接影响 mod_proxy 的连接目标,但对 mod_cache 缓存键是否生效取决于模块处理顺序。测试时应使用 CacheDetailHeader 查看实际的缓存键内容。
四、验证端口调整效果与排查要点
完成配置后,建议先启用缓存调试头部,观察缓存键是否已经去除端口差异。可以在虚拟主机中添加:
CacheDetailHeader On
然后用 curl 分别请求不同端口:
curl -I http://localhost:80/docs/guide.html curl -I http://localhost:8080/docs/guide.html
观察响应头中的 X-Cache 和 X-Cache-Detail。如果第一次请求返回 MISS,第二次返回 HIT,说明端口差异已经被成功归一化。若两次都是 MISS,则需要检查 CacheKeyBaseURL 是否在正确的上下文中生效,以及 mod_cache 的处理顺序是否早于 rewrite。
常见问题中,最容易忽略的是 HTTPS 与 HTTP 的合并。虽然 CacheKeyBaseURL 可以把协议也覆盖成 http,但 HTTPS 请求还涉及证书、加密连接等前置处理,盲目合并可能造成缓存内容被错误跨协议复用,导致安全风险。因此不要将 443 端口的缓存键强制改成 80 端口,除非明确确认两个协议访问的是完全相同的公开内容。
此外,动态接口、带有用户会话标识的响应、以及设置了 Set-Cookie 的资源不应被缓存。Apache 默认会根据响应头自动判断,但端口调整后可能让一些原本不应缓存的请求意外命中缓存。可以通过 CacheDisable 排除敏感路径,保证安全。
Apache代理缓存缓存端口范围mod_cache修改时间:2026-08-23 02:33:42