自建 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;如果出现 EXPIREDBYPASSSTALE,说明命中逻辑没按预期工作,要回去检查缓存键和有效期设置。这一行在排查「改了页面却还是老内容」这类常见故障时尤其有用。

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_pathmax_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/