在搭建Apache反向代理并开启磁盘缓存之后,不少站点会面临一个现实问题:缓存把本该受限访问的响应也存了下来,后续任意来访者只要知道URL就能读到内容。要解决这个问题,不能只靠后端鉴权,因为缓存命中时请求可能根本没转发到后端。我们需要在Apache代理缓存的链路上插入访问控制ACL,让缓存模块在返回缓存之前先检查请求者是否有权限。

理解Apache代理缓存与ACL的执行顺序
Apache处理请求时,模块按固定阶段运行。代理缓存由mod_cache接管,而访问控制通常由mod_authz_core及配套的授权模块(如mod_authz_host)在授权阶段完成。很多人误以为缓存一旦命中就会跳过所有后续处理,其实mod_cache在设计上会在读取缓存对象后,仍然走一遍授权检查,前提是你在相关作用域里配置了Require类指令。换句话说,缓存层并不会天然绕过ACL,只是默认配置下你没有给它设置任何访问限制,它才显得“来者不拒”。
从请求流程看,当启用CacheQuickHandler且设置为On时,Apache会尝试在最早的处理器阶段直接返回缓存,这时很多模块包括授权模块都还没运行,这是最容易出现越权缓存的原因。因此做访问控制ACL的第一原则,是把CacheQuickHandler关掉,或者把ACL规则写到能够覆盖缓存读取之后的上下文。只有理解了这个顺序,后面写的Require ip、Require env等规则才能真正拦在缓存命中之前或之后生效。
另外要注意,代理缓存的ACL和后端应用的权限是两层东西。后端拦住未登录用户返回401,这本该被缓存模块记为不可缓存,但如果后端错误配置了Cache-Control: public,Apache就会把401也存起来发给所有人。所以ACL不只是写Apache规则,还要配合后端响应头,才能形成闭环。
基于Location和Require的缓存访问控制配置
最常见的做法是利用<Location>块针对特定URL路径设置代理缓存,并同时写授权指令。比如我们只允许内网网段读取某个缓存接口,其他外部IP即使碰巧命中缓存也必须被拒绝。下面这段配置关闭了快速处理器,确保授权能介入:
<Location "/api/cached/">
CacheQuickHandler off
ProxyPass "http://127.0.0.1:8080/api/cached/"
ProxyPassReverse "http://127.0.0.1:8080/api/cached/"
CacheEnable disk
CacheRoot "/var/cache/apache2"
<RequireAny>
Require ip 10.0.0.0/8
Require ip 192.168.0.1
Require env ALLOW_CACHE_FETCH
</RequireAny>
</Location>
在上面例子里,<RequireAny>表示满足任一条件即可。如果来访者IP不在内网,也没有被前置脚本设上ALLOW_CACHE_FETCH环境变量,那么即便磁盘里已有缓存,Apache也会因为授权失败返回403,而不会把缓存体吐出去。这就实现了“缓存存在但特定人才能读”的ACL效果。
如果希望更细粒度,比如只允许带特定请求头的客户端读取缓存,可以用mod_headers配合RewriteRule设环境变量,再用Require env判断。例如仅当请求头X-Internal-Token存在时才允许命中缓存,其他情况转给后端重新鉴权。这种写法比单纯限制IP更灵活,适合多租户网关场景。但要注意,环境变量名不要和后端业务冲突,且需要在代理之前用RequestHeader或RewriteRule提前设置好。
避免缓存穿透与误命中内部接口的实操策略
配置ACL时还有一个坑:拒绝缓存访问后,Apache默认可能直接返回403,但某些客户端会反复重试,导致后端压力不减。更好的策略是用ErrorDocument把403导向一个轻量校验脚本,或者结合CacheIgnoreHeaders让敏感头不被缓存,从根源减少敏感内容落盘。例如对含有Set-Cookie的响应直接不缓存,避免会话被串。
CacheIgnoreHeaders Set-Cookie Authorization
<Location "/internal/">
CacheEnable disk
CacheQuickHandler off
Require ip 127.0.0.1
Require ip 10.0.0.0/8
</Location>
上面的CacheIgnoreHeaders确保携带Authorization的响应不被存为公共缓存,而/internal/路径只允许本机和内网,外网请求连缓存文件都看不到。这样可以防止运维监控接口被代理缓存暴露到公网。
最后建议用curl做验证:从外部IP请求应得403,从内网IP请求得200且首部出现X-Cache: HIT,说明ACL和缓存协作正常。若发现外部也能HIT,优先检查CacheQuickHandler是否忘记关,以及Require是否写在了正确的作用域。只有把配置、响应头、网络环境三者对齐,Apache代理缓存的访问控制ACL才算真正落地。