同一台服务器,没配缓存时并发300就开始丢请求,加上这三层缓存后并发跑到5000还稳稳的
有个做外贸站群的朋友,6个B2B站点跑在同一台2核4G的轻量云上,平时没事,一碰行业展会那几天Google爬虫加海外客户同时涌进来,CPU直接100%,好几个站轮流打不开。他第一反应是升级配置,花三百多升到4核8G,结果下个月展会又来一次,又挂了。问题的根不在配置上——他全站动态渲染,一个访客打开首页PHP就要查3次数据库,100个访客同时来就是300次查询,瓶颈在IO,加CPU没用。
后来他把缓存配了——页面缓存、对象缓存、CDN边缘缓存三层一套,同一台2核4G机器,同样的6个站点,同样的展会流量,CPU稳定在30%以下。网站缓存配置不是在"优化性能",是在"减少不必要的计算"。这篇文章把浏览器缓存、服务器缓存、CDN缓存、对象缓存四层拆开,讲清楚每种缓存配什么工具、参数怎么设、坑在哪里。
四层缓存的分工:每层拦截不同的请求
| 1 | 浏览器缓存:CSS/JS/图片存在用户本地,同一资源不再请求服务器,一分钱不花 |
| 2 | CDN边缘缓存:全球节点就近返回,伦敦用户不用跨国连广州源站 |
| 3 | 服务器页面缓存:HTML页面存成静态文件,Nginx直接返回,PHP和MySQL一步都不跑 |
| 4 | 对象缓存(Redis):数据库查询结果存内存,同一个查询不反复打MySQL |
一、浏览器缓存:最便宜的一层,90%的站配错了
浏览器缓存的核心是HTTP响应头里的Cache-Control。它告诉浏览器:这个资源你能存多久,过期之前别再找我要。配几行Nginx配置就能生效,一分钱不花,但配错的后果是用户每次刷新页面都要重新下载所有CSS、JS和图片。
Cache-Control常用指令速查
| public, max-age=31536000, immutable | 可被任何缓存存储,1年有效,永不变更。配合hash文件名使用(logo.a3f2b1c.png) |
| no-cache | 可以缓存,但每次使用前向服务器验证。配合ETag,内容没变就返回304不传body。用于HTML页面 |
| no-store | 禁止任何缓存。用于支付页面、用户隐私数据等敏感内容 |
| private, max-age=0 | 只允许浏览器缓存,立即过期。用于需登录才能看的内容 |
这里面最容易翻车的是no-cache和no-store搞混。no-cache不是"不缓存",而是"缓存了但每次用之前问一下服务器这东西变了没",配合ETag或Last-Modified可以做到"内容没变就用缓存(304 Not Modified),变了就重新下载(200)"。而no-store才是真的完全不缓存。很多新手看到no-cache这个词就以为是禁缓存,直接配成no-store,用户每访问一页都重新加载所有资源。
在Nginx里配浏览器缓存很简单:
# 静态资源长缓存(必须配合hash文件名使用)location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf)$ {expires 365d;add_header Cache-Control "public, immutable";}# HTML页面短缓存 + 协商缓存location ~* \.html$ {expires 5m;add_header Cache-Control "public, must-revalidate";}常见翻车现场:把HTML页面也设了max-age=31536000。结果是用户访问首页看到的是去年的内容,发新版了老用户也看不到。immutable指令一定要配合版本化文件名使用(style.a3f2b1c.css),不带hash就开immutable等于给自己挖坑。

二、WordPress缓存插件:五款横评,选对不选贵
浏览器缓存管静态资源,但HTML页面本身还是要经过PHP渲染。WordPress一次页面请求背后是几十次数据库查询,TTFB该慢还是慢。缓存插件就是把PHP渲染好的HTML页面存起来,下次直接返回静态文件。
| 插件 | 页面缓存 | Redis集成 | CSS/JS优化 | 价格 | 最适合 |
|---|---|---|---|---|---|
| WP Rocket | ✅ 强 | 需配合插件 | ✅ 内置 | $59/年 | 不想折腾、预算够的个人站长 |
| LiteSpeed Cache | ✅ 服务器级 | ✅ 内置 | ✅ 内置 | 免费 | LiteSpeed服务器用户 |
| W3 Total Cache | ✅ 强 | ✅ 原生支持 | ✅ 内置 | 免费 | 愿意折腾、喜欢精细控制的老手 |
| WP Super Cache | ✅ 静态HTML | ❌ 不支持 | ❌ 不支持 | 免费 | 只需要基础缓存的小站 |
| WP Fastest Cache | ✅ 简洁 | 需付费版 | 付费版支持 | 免费 / $49起 | 追求操作简单、功能够用就行 |
WP Rocket是开箱即用的标杆——装上去点一下"激活缓存",80%的优化自动完成。CSS/JS合并压缩、延迟加载、预加载缓存、数据库清理全内置。代价是收费,单站$59/年,5个站就是$295/年。LiteSpeed Cache免费且性能最强,但只限LiteSpeed/OpenLiteSpeed服务器。W3 Total Cache免费、功能全,但设置项太多了,新手进去像进了飞机驾驶舱。
一句话选型
LiteSpeed服务器 → 无脑LiteSpeed Cache(免费、服务器级、功能全)
Apache/Nginx + 不想折腾 → WP Rocket(付费但省心)
Apache/Nginx + 多站点 + 愿意折腾 → W3 Total Cache(免费、精细控制)
Apache/Nginx + 只要基础缓存 → WP Super Cache(极简、稳定)
三、Nginx层面缓存:跳过PHP,直接干到文件系统
WordPress缓存插件说到底还是PHP层面的方案——插件运行在WordPress框架内,命中缓存时确实不用查数据库了,但PHP进程还是要启动的,WordPress核心还是要加载的。如果你的并发很高,光是PHP-FPM的进程管理就已经是瓶颈了。
Nginx自带的fastcgi_cache在Web服务器层面就拦截了请求——命中缓存时Nginx直接从磁盘返回静态文件,连PHP-FPM都不经过。请求链路从"Nginx → PHP-FPM → WordPress → MySQL"直接缩短成"Nginx → 磁盘文件"。
# 第一步:http块中定义缓存区域fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WP:100m inactive=60m;# 第二步:server块中启用set $skip_cache 0;if ($http_cookie ~* "wordpress_logged_in") {set $skip_cache 1;}location ~ \.php$ {fastcgi_cache WP;fastcgi_cache_key "$scheme$request_method$host$request_uri";fastcgi_cache_valid 200 301 302 60m;fastcgi_cache_valid 404 1m;fastcgi_cache_use_stale error timeout updating;fastcgi_cache_bypass $skip_cache;fastcgi_no_cache $skip_cache;}几个容易被忽略的细节:fastcgi_cache_key的设计,上面用了\$scheme\$request_method\$host\$request_uri,HTTP和HTTPS会有两份缓存。如果全站HTTPS可以把\$scheme去掉。缓存旁路必须跳过已登录用户(wordpress_logged_in cookie),否则后台编辑完前台还是旧缓存。fastcgi_cache_use_stale在PHP-FPM挂掉时返回过期缓存而不是502,非常救命。
fastcgi_cache
WordPress、Typecho等PHP CMS,Nginx直连PHP-FPM架构。配置简单,效果立竿见影。
proxy_cache
Nginx做反向代理,后端是Node.js、Java、Python等。缓存后端API响应,减少应用服务器压力。
缓存清理的坑
Nginx原生不支持按URL清除缓存,需要编译ngx_cache_purge模块。简单粗暴的方案:rm -rf清空整个缓存目录。
还有一个选择是OpenResty + Lua + Redis的方案。比起fastcgi_cache存磁盘,Redis存内存的读写速度更快,天然支持按key过期和删除。但技术门槛高,需要会写Lua脚本、懂OpenResty配置。

四、Redis对象缓存:解决"同一个查询反复打MySQL"的问题
页面缓存解决了"同一个页面反复访问",但WordPress另一个性能黑洞是数据库查询。每个页面加载都要查菜单、查侧边栏、查配置、查文章列表——这些查询结果短时间内不变,但每次都去MySQL跑一遍。对象缓存就是把数据库查询结果缓存到内存里。
| 对比维度 | Redis | Memcached |
|---|---|---|
| 数据结构 | String、Hash、List、Set等 | 仅String(key-value) |
| 持久化 | RDB快照 + AOF日志 | 不支持,重启数据全丢 |
| 单key上限 | 512MB | 1MB |
| WordPress适配 | 优秀,插件完善 | 基本可用,功能受限 |
对WordPress来说Redis几乎是无脑选。原因是WordPress的查询结果经常是一大串对象数据——wp_posts表的一行转成的WP_Post对象。Memcached的1MB单key限制在复杂页面场景下会翻车,而且不支持持久化意味着服务器重启一次,缓存全部重建,数据库压力骤增。
Redis配WordPress推荐用Redis Object Cache免费插件,填host和port点启用就行。但注意:启用了Redis对象缓存后修改主题或插件代码,需要手动清缓存,否则前端可能还是旧数据。
血的教训:Redis不要和MySQL跑在同一台机器上然后不设maxmemory。Redis默认不限制内存使用,几十万条记录会逐渐吃掉所有可用内存,最后OOM被系统kill,缓存全丢,数据库瞬间被打爆。一定要在redis.conf里设置maxmemory 256mb和maxmemory-policy allkeys-lru。
五、CDN缓存:从源站到用户的最后一公里
前面三层缓存都是在"减少源站的计算量"。但还有一个问题没解决:你的服务器在广州,用户在伦敦打开你的网站,TCP三次握手就要几百毫秒,跟服务器性能无关,是物理距离决定的。CDN就是把内容缓存到全球边缘节点,用户访问离他最近的节点。
Cloudflare免费版是绝大多数个人站长和小团队的首选——全球300+节点、DDoS防护、免费SSL证书。但免费版的默认缓存策略比较保守:只缓存CSS/JS/图片等静态文件,不缓存HTML。意味着用户访问首页时CDN节点每次都要回源。
要突破这个限制,需要在Cloudflare里配置Cache Rules(免费版支持3条规则)。核心思路是告诉Cloudflare:HTML页面也给我缓存,按URL路径设置不同的缓存时间。
规则1:静态资源长缓存URL匹配:*.css, *.js, *.png, *.jpg, *.woff2操作:Cache → Eligible for cacheEdge TTL:30天Browser TTL:1年规则2:HTML页面中缓存URL匹配:*example.com/*(不含/wp-admin/)操作:Cache → Eligible for cacheEdge TTL:2小时Browser TTL:30分钟规则3:后台不缓存URL匹配:*example.com/wp-admin/*操作:Cache → Bypass cacheCloudflare免费版
300+
全球节点 / 3条Cache Rules / 无限流量
Cloudflare Pro
$20/月

20条Cache Rules / 图片优化Polish / 自动移动优化
又拍云/七牛云
国内节点
需备案域名 / 国内访问速度快 / 按流量计费
如果你的网站面向国内用户,Cloudflare的中国大陆节点是收费的(需要企业版),免费版的流量不会走中国大陆节点。这种情况可以考虑又拍云或七牛云的CDN,国内节点覆盖好,但需要域名备案。
六、四层缓存怎么组合:三种配置方案,看服务器和预算选
把上面四层缓存串起来,不是每层都要上,也不是越多越好。缓存层级多了,一致性维护成本就高,更新了一篇文章要清三层缓存才能让用户看到新内容。根据服务器环境和业务场景选组合。
方案A:入门级(零成本,适合个人博客、小企业站)
| 浏览器缓存 | Nginx expires + Cache-Control,静态资源365天,HTML 5分钟 |
| 页面缓存 | WP Super Cache或WP Fastest Cache免费版,开启静态HTML缓存 |
| CDN | Cloudflare免费版,配置Cache Rules缓存静态资源 |
| 对象缓存 | 不配置(访问量不大的话MySQL扛得住) |
方案B:进阶版(中小型站群,3-10个站点,日UV 5000以内)
| 浏览器缓存 | Nginx expires + Cache-Control,immutable + hash文件名 |
| 页面缓存 | Nginx fastcgi_cache(服务器级,效率高于插件)或WP Rocket |
| CDN | Cloudflare免费版 + Cache Rules缓存HTML,Edge TTL 2小时 |
| 对象缓存 | Redis + Redis Object Cache插件,maxmemory 256MB |
方案C:高配版(10+站点站群,日UV破万,对速度有极致要求)
| 浏览器缓存 | Nginx expires + immutable + Service Worker离线缓存 |
| 页面缓存 | Nginx fastcgi_cache + 定时预热脚本,缓存命中率95%+ |
| CDN | Cloudflare Pro或商业CDN,精细化缓存规则,支持API主动刷新 |
| 对象缓存 | Redis Cluster或Sentinel高可用,maxmemory-policy allkeys-lru |
| PHP层 | OPcache开启 + 合理配置opcache.memory_consumption |
七、站群场景的特殊缓存策略
如果你跑的是站群——多个网站在同一台服务器上,缓存配置要多考虑几个维度。不是每个站装个插件就完事了。
首先是缓存隔离。Nginx fastcgi_cache默认是按fastcgi_cache_key来区分缓存的,如果你的多个站点用同一个key规则(比如都是\$host\$request_uri),且key里没有区分站点的维度,A站的页面可能缓存到B站的路径下。正确做法是fastcgi_cache_path给每个站分一个独立的缓存目录,或者cache_key里包含server_name。
其次是Redis的命名空间。多个WordPress站共用同一个Redis实例时,如果不做前缀隔离,A站的文章缓存key可能会覆盖B站的。Redis Object Cache插件支持设置WP_REDIS_PREFIX常量,每个站的wp-config.php里设不同的前缀:
// 站点A的wp-config.phpdefine('WP_REDIS_PREFIX', 'site_a_');// 站点B的wp-config.phpdefine('WP_REDIS_PREFIX', 'site_b_');第三是CDN的缓存回源。站群如果共用一个CDN域名或者同一个Cloudflare账号,要注意缓存规则不要互相污染。最好每个站单独配置缓存规则,至少按域名区分。比如site-a.com的HTML缓存2小时,site-b.com因为更新频繁只缓存30分钟。
站群缓存的核心矛盾:缓存越激进性能越好,但更新越麻烦。10个站每发一篇文章都要清三层缓存,人工操作根本忙不过来。需要一个系统化方案——内容发布后自动触发缓存清除,而不是手动一个个去清。
用UC建站系统做站群的话,缓存管理可以统一收口。系统的内容中台发布文章后,自动调用各站点的缓存清除接口——WordPress站点通过REST API触发插件清除页面缓存,Nginx层面用ngx_cache_purge模块清理fastcgi_cache,CDN通过Cloudflare API刷新边缘缓存。一个发布动作串联起三层缓存的更新,不用登录每个站后台手动清。多站看板还能统一监控各站的缓存命中率、页面加载时间,哪一站缓存策略出了问题一眼就能看到。
说到底,缓存配置这件事没那么玄乎。浏览器缓存配几行Nginx、WordPress装个插件、服务器上跑个Redis、DNS接个Cloudflare,四步走完大多数站的速度问题就解决了。真正难的是一致性管理——更新了内容用户能看到新版本、缓存过期了能自动重建、哪层缓存出了问题能快速定位。单站还好说,站群一多就得靠系统化工具而不是人肉维护了。
