自建 CDN 缓存方案指南:Varnish 与 Nginx 反向代理实战
并不是所有网站都适合直接用商业 CDN。当流量规模、合规要求或成本结构需要更可控的方案时,"自建 CDN 缓存层"是一个常见选择:在源站前架一层反向代理缓存,把静态内容留在本地缓存,减少源站压力并加速访问。
一、先判断:要不要自建
自建 CDN 缓存层适合以下场景:
- 源站已有多机房,希望通过本地缓存层降低回源链路压力;
- 需要精细控制缓存逻辑,如针对内部系统、动态接口做特殊处理;
- 合规或数据本地化要求,数据不便经过第三方 CDN;
- 成本敏感且流量可控,不想承担按流量计费的商业 CDN 费用。
不适合的场景:需要全球多节点覆盖、需要抗超大 DDoS、需要极低延迟的边缘接入。这些仍是CDN 网络商业服务的强项,自建方案很难复刻其节点规模。选型时可参考CDN 服务商选择指南。
二、主流自建方案对比
| 方案 | 定位 | 缓存能力 | 特点 |
|---|---|---|---|
| Nginx | Web 服务器/反向代理 | proxy_cache 模块 | 轻量、易上手、与现有 Nginx 配置无缝衔接 |
| OpenResty | Nginx + Lua | proxy_cache + Lua 脚本 | 可在缓存层做灵活逻辑(鉴权、限流、动态缓存键) |
| Varnish | 高性能 HTTP 缓存 | VCL 策略 | 缓存性能极强,VCL 配置灵活,适合纯缓存场景 |
| Apache Traffic Server | 企业级缓存/边缘代理 | 原生缓存 + 插件 | 节点规模大,适合承载大流量与多源站管理 |
三、Nginx 代理缓存实战
Nginx 是自建方案中门槛最低、最常见的选择。其缓存基于 proxy_cache 模块,把后端响应缓存到本地磁盘。
3.1 基础配置
http {
# 定义缓存目录与共享内存区
proxy_cache_path /data/nginx/cache keys_zone=mycache:10m max_size=10g;
server {
proxy_cache mycache;
location / {
proxy_pass http://127.0.0.1:8000;
# 200/302 缓存 10 分钟,404 缓存 1 分钟
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
# 自定义缓存键(默认含域名与请求 URI)
proxy_cache_key "$scheme$request_method$host$request_uri";
}
}
}
要点:
keys_zone定义共享内存区,用于存放缓存条目元数据;max_size限制磁盘缓存总量,超出后由 cache manager 按 LRU 清理;proxy_cache_valid按响应码设置有效期;- 启动时 cache loader 会把磁盘元数据逐步载入内存,避免启动抖动。
3.1.1 验证缓存是否生效
配置完成后,用 curl -I 查看响应头就能确认缓存状态。Nginx 默认不输出缓存命中标记,需要先在配置里追加一行:
add_header X-Cache-Status $upstream_cache_status;
然后连续请求两次同一个 URL:
curl -I https://example.com/static/app.css
第一次响应里 X-Cache-Status: MISS,第二次应为 X-Cache-Status: HIT;如果出现 EXPIRED、BYPASS 或 STALE,说明命中逻辑没按预期工作,要回去检查缓存键和有效期设置。这一行在排查「改了页面却还是老内容」这类常见故障时尤其有用。
3.2 缓存刷新
Nginx 通过 HTTP PURGE 方法实现缓存清理:
map $request_method $purge_method {
PURGE 1;
default 0;
}
# 在 location 中:proxy_cache_purge $purge_method;
curl -X PURGE "https://www.example.com/*"
务必用 geo 限制允许执行 PURGE 的 IP,防止缓存被恶意清空。
3.3 与其他方案协同
Nginx 缓存通常部署在源站前作为"本地 CDN 缓存层",与SSL/TLS 配置、Nginx 性能优化配合,能显著提升单机吞吐。更多 Nginx 站点配置见Nginx 站点配置。
四、Varnish 与 OpenResty 的进阶用法
4.1 Varnish
Varnish 专为 HTTP 缓存设计,性能极高,通过 VCL(Varnish Configuration Language)控制缓存策略,支持缓存键改写、条件缓存、与后端健康检查联动。适合作为独立缓存层置于应用服务器之前。
一段极简的 VCL 大致长这样:
vcl 4.1;
backend default {
.host = "127.0.0.1";
.port = "8080";
}
sub vcl_recv {
# 登录态区域不缓存
if (req.url ~ "^/account") {
return (pass);
}
# 把语言 Cookie 写进缓存键
if (req.http.Cookie ~ "lang=") {
set req.http.X-Lang = regsub(req.http.Cookie, "^.*lang=([^;]+);?.*$", "\1");
set req.hash += req.http.X-Lang;
}
}
这个例子覆盖了 VCL 最常见的两类需求:对动态路径直接放行(pass),以及把语言 Cookie 折叠进缓存键,避免中文版和英文版互相串缓存。
4.2 OpenResty
OpenResty 在 Nginx 基础上内置 Lua,可以在缓存层直接实现鉴权、限流、A/B 分流、按用户维度定制缓存键等逻辑,是"反代 + 缓存 + 业务逻辑"一体化的选择。
五、16IDC 观察:自建方案的落地建议
在 16IDC 服务的中小网站中,自建缓存层的典型形态是"单机 Nginx 代理缓存"或"少量节点 + 简单 DNS 调度",很少需要一上来就上 Varnish/ATS 集群。几点建议:
- 从 Nginx 起步:用最少的改动验证收益,再考虑引入更复杂的组件;
- 缓存层之外仍要 CDN:自建缓存解决"源站与机房间"的加速,全球分发仍建议配合商业 CDN;
- 做好缓存键设计:参考CDN 缓存策略与缓存键策略,避免误缓存动态内容;
- 配套监控:缓存命中率、回源量、磁盘占用都要纳入监控,否则问题难以定位。
自建方案的核心价值在于"可控",代价在于"要自己运维"。对大多数用户而言,先想清楚CDN 加速目标与预算,再决定是商业 CDN、自建缓存还是两者结合,才是更务实的路径。
六、常见问题
自建缓存层能替代商业 CDN 吗? 不能完全替代。自建缓存解决的是「源站与机房之间的加速」,通常只部署在少数几个点;商业 CDN 的价值在于上百个边缘节点的就近分发。两者其实是互补关系,不少网站采用「商业 CDN 兜底全球 + 自建缓存承接高频回源」的组合。
缓存目录越来越大怎么办? 先确认缓存键是否过细——把同一份资源按 Cookie 或时间戳拆成大量副本——再用 proxy_cache_path 的 max_size 配合 LRU 让 cache manager 自动清理。如果命中率持续偏低,问题多半出在缓存键或有效期设计,而不是磁盘空间。
如何量化缓存收益? 盯三个指标:缓存命中率(静态资源建议 90% 以上)、回源流量、源站平均响应时间。用 nginx-module-prometheus 或 Varnish 自带的 varnishstat 都能拿到实时命中率曲线。
原文来源:https://docs.nginx.com/nginx/admin-guide/content-cache/content-caching/
参考:Varnish 官方文档:https://varnish-cache.org/docs/