站群缓存不是每个站单独配一遍,搞懂Nginx include、Redis前缀隔离和CDN API这三层,50个站的缓存规则半小时搞定
上个月接手了一个30个站的矩阵,打开服务器一看,每个站点目录下面都单独放着一套Nginx配置,缓存相关的参数更是五花八门——有的开了fastcgi_cache,有的没开,有的缓存时间设了10分钟,有的设了24小时,还有三个站连缓存目录都没建,跑了两年的站点一直在裸奔。整理这套东西花了我整整一天,改完以后整个服务器的CPU负载从平均65%降到了18%,页面打开速度中位数从2.8秒降到了0.9秒。
这事的本质不是"要不要开缓存"——没有人会说不开。问题出在站群规模一大,手工逐站配置的时间成本高到不现实,而且配置不一致带来的性能差异、缓存冲突、Redis数据串站这些坑,比不开缓存还麻烦。这篇文章就把三层缓存(Nginx层、应用层Redis、CDN边缘层)的批量配置逻辑讲清楚,重点不是每个参数的详细含义,而是怎么用一套模板管住几十上百个站。
站群缓存三层批量配置核心逻辑
| 1 | Nginx层:用include指令+变量化路径,一套缓存模板覆盖所有站点,每个站自动隔离缓存目录,不用逐站复制配置 |
| 2 | 应用层Redis:不同站点用不同前缀(WP_REDIS_PREFIX),同一台Redis实例可以服务几十个站,互不串数据 |
| 3 | CDN边缘层:Cloudflare/又拍云等CDN的API支持批量配置缓存规则和批量清除,不用逐站登录后台点来点去 |
一、为什么逐站配缓存这件事本身就不对
先算一笔时间账。假设你有20个站,每个站要配的东西包括:Nginx的fastcgi_cache(大约8行配置)、Redis object cache(wp-config.php里加3行)、浏览器缓存头(Nginx里加5行)、CDN缓存规则(后台点来点去)。手工做的话,一个站从打开配置文件到改完测试,保守估计15分钟。20个站就是5个小时,还不算中途配置不一致需要排查的时间。
但真正的问题不是时间,是一致性。20个站手工配完,大概率会出现以下情况:有两个站的缓存时间写错了(10分钟写成100分钟),有一个站忘了配Redis前缀导致数据串了,还有三个站的浏览器缓存头配得不一致导致CDN回源率不一样。这种不一致对站群的杀伤力比少配一两个站更大——搜索引擎蜘蛛抓取不同站点的响应速度差异太大,本身就是一种关联信号。
正确的做法是:三层缓存各用一套模板,改站点变量就自动适配。新增一个站不需要重新写缓存配置,只需要在模板里加一行域名映射。
二、Nginx层:include模板+变量化路径,一个文件管所有站
Nginx的fastcgi_cache是最基础也最容易被忽略的一层。它直接缓存PHP生成的HTML页面,命中以后根本不走PHP-FPM,对服务器资源的节省立竿见影。但站群场景下最大的问题是缓存目录隔离——不能所有站共用一个缓存目录,否则A站的首页缓存可能被当成B站的返回出去。
解决思路分两步。第一步,在nginx.conf的http块里定义一个或多个缓存路径:

http {# 定义缓存路径:目录 /tmp/nginx_cache,两级子目录,内存zone 100MB,最大磁盘1GBproxy_cache_path /tmp/nginx_cache levels=1:2keys_zone=WP_CACHE:100m max_size=1g inactive=60m use_temp_path=off;# 如果站点多,可以分多个zoneproxy_cache_path /tmp/nginx_cache_site2 levels=1:2keys_zone=WP_CACHE2:100m max_size=1g inactive=60m use_temp_path=off;}第二步,也是最关键的一步——用include把缓存配置抽成独立文件,每个server块引用同一份配置。新建一个文件叫/etc/nginx/conf.d/cache-common.conf:
# 用 $host 变量让每个站自动使用不同的缓存key前缀set $cache_key "$scheme$host$request_uri";# 不缓存的后台和登录相关页面set $skip_cache 0;if ($request_uri ~* "(wp-admin|wp-login|xmlrpc|checkout|cron)") {set $skip_cache 1;}if ($http_cookie ~* "wordpress_logged_in") {set $skip_cache 1;}location ~ \.php$ {# 引用之前定义的缓存zoneproxy_cache WP_CACHE;proxy_cache_key $cache_key;proxy_cache_valid 200 60m;proxy_cache_valid 404 1m;proxy_cache_bypass $skip_cache;proxy_no_cache $skip_cache;proxy_cache_use_stale error timeout updating http_500 http_502;add_header X-Cache-Status $upstream_cache_status;# 你的fastcgi_pass等配置...}然后在每个站点的server块里只需要一行:
server {server_name site1.example.com;root /var/www/site1;include /etc/nginx/conf.d/cache-common.conf;# 其他配置...}新增站点时,缓存配置一行不用改。如果要统一调整缓存时间(比如从60分钟改成120分钟),只改cache-common.conf一个文件,nginx -s reload全部生效。这就是include模板的核心价值——变一个地方,所有站同步更新。
站点数超过30个时的升级方案
如果一台服务器上的站点数太多(30+),建议按业务类型拆分成多个缓存zone,避免单个zone过大导致内存查找效率下降。比如商城站一个zone、内容站一个zone、工具站一个zone,在cache-common.conf里根据$host判断使用哪个zone。
三、Redis Object Cache:前缀隔离是站群场景的命门
WordPress用Redis做object cache能极大减少数据库查询次数——页面渲染涉及的options、posts、post_meta、terms等数据全部走内存,不再每次都查MySQL。但站群场景下有一个新手必踩的坑:多个WordPress站点共用一台Redis,不加前缀隔离,A站的文章缓存会覆盖B站的。
WordPress的Redis Object Cache插件(比如Redis Object Cache这个插件,安装量200万+)默认用wp_作为缓存键前缀。如果两个站都用默认配置,它们会在Redis里用同一组键名,后写入的覆盖先写入的。表现就是:打开站点B的首页,显示的是站点A的内容——这比没缓存还吓人。
解决办法是在每个站点的wp-config.php里设置独立的缓存前缀:
// 站点1define('WP_REDIS_PREFIX', 'site1_');define('WP_REDIS_DATABASE', 0); // 也可以用不同数据库编号隔离// 站点2define('WP_REDIS_PREFIX', 'site2_');define('WP_REDIS_DATABASE', 0);// 站点3define('WP_REDIS_PREFIX', 'site3_');define('WP_REDIS_DATABASE', 1); // 前缀或数据库编号二选一即可两个容易忽略的点
第一,Redis默认有16个数据库(0-15),用不同编号可以物理隔离,但要注意Redis Cluster模式下不支持多数据库,只能靠前缀隔离。第二,如果用了W3 Total Cache或WP Rocket这类全能缓存插件,它们内部也可能用Redis,要确保它们的Redis前缀也和object cache的前缀保持一致或至少不冲突。
批量部署的技巧:写一个Shell脚本遍历所有站点目录,在每个wp-config.php里追加前缀配置。站点名从目录名自动提取,不需要手工一个个改。核心逻辑大约15行:
#!/bin/bash# 批量设置Redis前缀SITES_DIR="/var/www"for site_dir in "$SITES_DIR"/*/; dosite_name=$(basename "$site_dir")config_file="$site_dir/wp-config.php"if [ -f "$config_file" ]; then# 检查是否已经设置过if ! grep -q "WP_REDIS_PREFIX" "$config_file"; then# 在 /* That's all, stop editing! */ 这行之前插入sed -i "/That's all, stop editing/i\define('WP_REDIS_PREFIX', '${site_name}_');" "$config_file"echo "已设置 $site_name 的Redis前缀为 ${site_name}_"elseecho "$site_name 已有Redis前缀,跳过"fifidoneecho "全部完成"redis-cli FLUSHALL # 清空旧缓存,避免旧键名残留四、CDN层:API批量管理才是正确姿势
到了CDN这一层,很多人的操作方式是:打开Cloudflare后台→选站点→缓存规则→添加规则→保存→下一个站→重复。20个站做完,手已经酸了,而且下次想统一加一条规则(比如新增一个目录不缓存),又得从头来一遍。
Cloudflare提供了完整的API,支持批量管理缓存规则。核心思路是:用脚本遍历站点列表,调用API统一设置缓存规则和缓存级别。不需要登录后台。
#!/bin/bash# Cloudflare API 批量设置缓存规则CF_EMAIL="your@email.com"CF_API_KEY="your_global_api_key"# 站点列表(域名和Zone ID的对应关系)declare -A SITES=(["site1.com"]="zone_id_1"["site2.com"]="zone_id_2"["site3.com"]="zone_id_3")for domain in "${!SITES[@]}"; dozone_id="${SITES[$domain]}"# 设置缓存级别为标准curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$zone_id/settings/cache_level" \-H "X-Auth-Email: $CF_EMAIL" \-H "X-Auth-Key: $CF_API_KEY" \-H "Content-Type: application/json" \--data '{"value":"standard"}'# 添加页面规则:后台不缓存curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zone_id/pagerules" \-H "X-Auth-Email: $CF_EMAIL" \-H "X-Auth-Key: $CF_API_KEY" \-H "Content-Type: application/json" \--data "{\"targets\":[{\"target\":\"url\",\"constraint\":{\"operator\":\"matches\",\"value\":\"${domain}/wp-admin/*\"}}],\"actions\":[{\"id\":\"cache_level\",\"value\":\"bypass\"}],\"priority\":1,\"status\":\"active\"}"echo "已配置 $domain 的CDN缓存规则"done除了Cloudflare,国内常用的又拍云、七牛云也都提供了类似的API。又拍云的缓存刷新API支持按URL前缀批量清除,七牛云支持按目录刷新。选CDN的时候,API完整度应该作为一个重要考量因素——站群规模大了以后,没有API的CDN会让你陷入手工操作的泥潭。
Cloudflare
API最完善,免费套餐包含3条页面规则,Pro套餐20条。支持按URL、按标签、全站清除缓存。国内访问需配合优选IP。
又拍云
国内节点多,API支持按URL前缀批量刷新,单次最多30条。缓存规则通过"缓存配置"API批量下发,适合国内站群。
七牛云
融合CDN,API支持目录刷新和URL刷新,刷新额度每天500条(免费)。适合中小规模站群,国内访问速度好。
阿里云CDN
API功能全面,支持缓存规则批量配置、预热、刷新。按量付费,站群规模大时成本需要核算。与ECS同区域免流量费。
五、缓存时间怎么设才不翻车:按页面类型分层
三层缓存都配好了,还有一个关键参数:缓存时间。设太短,缓存命中率低,服务器压力没降下来;设太长,内容更新了用户看到的是旧页面。站群场景下这个问题更复杂——不同站点的更新频率可能完全不一样,新闻站可能每小时更新,企业站可能一周才改一次。

一个实操性强的方案是按页面类型分层设缓存时间,而不是按站点:
| 页面类型 | Nginx缓存 | Redis TTL | CDN缓存 | 说明 |
|---|---|---|---|---|
| 首页 | 10-30分钟 | 5分钟 | 10分钟 | 更新频繁,需要一定实时性 |
| 文章/详情页 | 1-6小时 | 1小时 | 1小时 | 内容稳定,缓存时间长收益大 |
| 分类/标签页 | 30-60分钟 | 30分钟 | 30分钟 | 有新文章时列表会变 |
| 静态资源 | 7-30天 | - | 7-30天 | CSS/JS/图片,文件名带hash版本号 |
| 后台/wp-admin | 不缓存 | - | 绕过 | 绝对不能缓存 |
| 搜索页 | 不缓存 | - | 绕过 | 参数变化频繁,缓存无意义 |
发布文章后的缓存刷新流程
发完文章后,应该自动触发以下动作:1) 清除该文章的Nginx缓存(按URL精确清除);2) 清除首页和分类页的缓存;3) 调用CDN API刷新对应URL。如果用的是UC建站系统的多站看板,可以在发布文章时配置自动缓存刷新规则,不用每次手动去各站后台点清除缓存。
六、WordPress缓存插件:选一个就行,关键是能批量管理
插件层的缓存(页面缓存+浏览器缓存+数据库缓存+对象缓存),2026年主流的选择就几个。站群场景下的选型逻辑和单站完全不同:单站看重功能和性能,站群看重能否批量配置、能否通过代码/API统一管理。
| 插件 | 价格 | 站群友好度 | 适合场景 |
|---|---|---|---|
| WP Rocket | $59/年/站 | 中等(需手动逐站配置,但可通过wp-config.php预设部分参数) | 10个站以内,追求省心 |
| LiteSpeed Cache | 免费 | 高(支持通过.htaccess和配置文件批量导入导出设置) | 用LiteSpeed服务器的站群,免费+功能全 |
| W3 Total Cache | 免费 | 高(几乎所有配置项都可以通过wp-config.php常量控制) | 站群首选,代码化配置能力最强 |
| FlyingPress | $60/年(无限站) | 中等 | 预算充足且站点数多,无限站点授权有性价比 |
重点说W3 Total Cache,因为它几乎所有配置项都可以通过wp-config.php的常量来覆盖。这意味着你可以写一个Shell脚本,遍历所有站点目录,在每个wp-config.php里统一注入缓存配置,不用登录任何一个WordPress后台:
// 在wp-config.php中统一配置W3 Total Cachedefine('W3TC_PG_CACHE', true); // 开启页面缓存define('W3TC_PG_CACHE_ENGINE', 'file'); // 文件缓存(或redis/memcached)define('W3TC_PG_CACHE_GZIP', true); // 启用Gzipdefine('W3TC_BROWSER_CACHE', true); // 浏览器缓存define('W3TC_BROWSER_CACHE_EXPIRES', 604800); // 7天define('W3TC_DB_CACHE', true); // 数据库缓存define('W3TC_OBJECT_CACHE', true); // 对象缓存(需配合Redis)define('W3TC_MINIFY', true); // HTML/CSS/JS压缩// 排除后台页面define('W3TC_PG_CACHE_REJECT_URI', 'wp-admin|wp-login|xmlrpc');绝对不要做的事
不要在同一个站点同时装两个缓存插件。WP Rocket + W3 Total Cache一起装,两个插件会互相争抢缓存控制权,结果不是"双重加速"而是"双重冲突"——页面可能完全不缓存,或者缓存的内容是乱的。另外,W3 Total Cache开启Minify后如果和CDN的压缩同时生效,可能导致JS/CSS被双重压缩,部分浏览器解析失败。
七、批量配置实战:从0到30个站缓存的完整SOP
前面把各层的技术细节讲清楚了,现在串成一条完整的操作流水线。假设你有一台服务器,上面有30个WordPress站点,目前缓存全没配,目标是半小时内搞定。
第一步
5分钟
创建Nginx缓存模板
写好cache-common.conf,测试一个站点确认缓存命中正常(看响应头X-Cache-Status:HIT),然后批量include到所有server块
第二步
10分钟
Redis前缀批量注入
Shell脚本遍历所有站点wp-config.php,注入WP_REDIS_PREFIX定义,重启PHP-FPM,验证redis-cli keys *看到不同前缀的键
第三步
8分钟
CDN缓存规则批量下发
API脚本遍历所有域名,设置缓存级别、页面规则(后台绕过)、浏览器缓存TTL,确认每个站返回cf-cache-status:HIT
第四步
5分钟
插件配置常量注入

Shell脚本批量追加W3TC常量定义到wp-config.php,清除旧缓存文件,逐个站点快速抽查首页加载时间
整个过程下来,手工操作的只有第一步写模板(一次性的),后面三步全部脚本化。30个站从0到缓存全配好,实际耗时大约半小时,其中20分钟在写脚本和测试,10分钟在等脚本跑完。后续新增站点,只需要在域名列表里加一行,重新跑一遍脚本,一分钟搞定。
八、缓存冲突排查:三个最常见的诡异问题
缓存配好不代表万事大吉。站群场景下,有些问题只在规模大了以后才会暴露,单站测试时完全看不出来。
问题一:同一个IP下A站的缓存页面被B站命中
原因:Nginx的proxy_cache_key只用了$uri,没有带$host,导致两个站同路径(比如都是/index.html)共用同一个缓存键。解决:proxy_cache_key里必须包含$scheme$host$request_uri。
问题二:Redis内存爆了,所有站一起挂
30个站共用一台Redis,每个站缓存50MB,就是1.5GB。如果Redis配置的maxmemory只有1GB,内存一满就会按淘汰策略删数据,结果所有站的object cache同时失效,MySQL瞬间被打爆。解决:设置maxmemory >= 站点数×单站预估缓存量×1.5(留50%余量),配置maxmemory-policy为allkeys-lru。
问题三:CDN缓存了登录态页面,访客看到了别人的购物车
原因:CDN的缓存规则没有排除带cookie的请求。用户登录后,页面内容不同(比如右上角显示用户名),但CDN仍然返回了缓存的未登录版本——更危险的是反过来,已登录的页面被缓存后,下一个访客看到了别人的个人信息。解决:CDN规则里配置Set-Cookie响应头时不缓存,或者按Cookie里的wordpress_logged_in做缓存键变体。
九、比工具更重要的:缓存策略的底线和原则
工具和方法讲完了,说几个容易踩的原则性问题:
原则一:缓存时间不要搞"一刀切"
不是所有页面都适合缓存1小时或1天。首页、列表页、详情页的更新频率完全不同。建议至少分三档:短(首页/列表页,10-30分钟)、中(详情页,1-6小时)、长(静态资源,7-30天)。如果某个站的文章更新频率特别高(比如新闻站),可以把详情页也归到短档。
原则二:缓存预热比缓存配置更重要
刚配好缓存,第一个访客访问时还是慢(因为缓存还没生成,这叫"冷启动")。站群规模大了,可以让脚本在配置完缓存后自动预热——用curl遍历所有站点的首页和热门文章URL,提前生成缓存。这样第一个真实访客看到的就是缓存版本。
原则三:监控缓存命中率,不是配完就不管了
Nginx缓存命中率低于70%说明配置有问题(可能是缓存时间太短或排除规则太宽)。Redis的hit rate应该在95%以上。CDN缓存命中率低于80%要检查回源频率。用UC建站系统的多站看板可以把这些指标聚合到一个面板上,30个站的缓存状态一目了然,不用逐个ssh上去敲命令。
原则四:缓存不能替代代码优化
一个页面渲染需要2秒,开了缓存变成0.1秒,这不叫优化——这叫遮羞布。搜索引擎蜘蛛不是每次都命中缓存(新页面、参数变体、爬虫不带cookie等情况),如果源站响应本身就慢,总有命不中的时候。缓存是锦上添花,代码层面的数据库查询优化、图片压缩、懒加载这些才是基本功。
十、站群缓存配置的四个层次,你现在在第几层
最后用一个层次模型收尾。你可以对照一下自己目前的情况:
| 层次 | 做法 | 站点上限 | 问题 |
|---|---|---|---|
| 第一层 | 每个站单独装缓存插件,后台手工设置 | 5-10个 | 配置不一致,新增站点工作量大,缓存冲突排查困难 |
| 第二层 | Nginx统一配置了fastcgi_cache,但缓存key没带host | 10-20个 | 站间缓存串数据,Redis没隔离,CDN还是手工管理 |
| 第三层 | Nginx include模板 + Redis前缀隔离 + CDN API批量管理 | 20-100个 | 缓存预热和命中率监控还是手工的,出问题发现不及时 |
| 第四层 | 前三层全部自动化 + 缓存命中率监控 + 异常自动告警 + 新增站点零配置 | 100+个 | 基本没有,但需要专人维护脚本和监控面板 |
大多数做站群的人卡在第二层——知道要配缓存,也配了,但用的是单站思维。模板化、变量化、API化这三个词,才是站群缓存配置的关键。
三层缓存配好了,一个站群的整体响应速度能提升3-5倍,服务器资源消耗降低50%以上。而且搜索引擎对响应速度越来越敏感——Core Web Vitals里的LCP(最大内容绘制时间)直接影响排名,缓存是提升LCP最直接的手段之一。30个站,每个站快0.5秒,加起来就是每天几万次页面加载都在省时间,搜索引擎看得见,用户也感觉得到。
如果你现在还没搞缓存,今天花半小时按上面的SOP走一遍,这可能是你做站群以来投入产出比最高的半小时。
