WordPress 接入 CDN:回源、缓存规则与常见故障
CDN 接上去第一天通常很顺,问题出现在第二天:后台样式全丢、表单提交报 403、日志里所有访客都变成了同一个 IP、支付回调验签失败。这些都不是 CDN 的锅,是回源和缓存规则没配对。下面按上线顺序讲,先解决回源,再谈缓存,最后处理真实 IP 和故障。
回源三件事:源站地址、回源 Host、回源协议
回源地址填源站 IP,不要填已经接了 CDN 的域名,否则 CDN 回源又走一遍 CDN,形成循环。如果源站 IP 会变(比如云服务器重建),就填一个不经过 CDN 的二级域名,例如 origin.example.com,这条解析保持直连。
回源 Host 必须填站点真实域名。这一项配错的典型表现是首页正常、内页全 404——CDN 拿 IP 回源,Nginx 按 Host 匹配站点,匹配不到就落到默认站点,返回的是另一个站的内容或者 404。排查时直接在源站上执行:
curl -I -H "Host: www.example.com" http://127.0.0.1/
能返回 200 说明源站没问题,问题在 CDN 的 Host 配置。回源协议选 HTTPS 时,源站证书必须是可信证书;用自签证书的话 CDN 会报 526 或者握手失败,这种情况要么换成正式证书,要么回源协议退回 HTTP(前提是回源链路在内网或可信网络)。
还有一件必做的事:源站防火墙放行 CDN 的回源 IP 段。很多 CDN 厂商把节点 IP 段公开在文档里,把它们加进白名单,同时把 80/443 对公网关掉。这既是安全,也是避免别人绕过 CDN 直接压源站。不放心的话可以只允许回源 IP 访问,其余全部 drop,配置思路和防火墙那篇一致。
回源端口容易被忽略。源站如果把 HTTP 改成了 8080、HTTPS 改成了 8443(宝塔改端口后很常见),CDN 那边还按 80/443 回源,结果就是全站 502。打开 CDN 的回源配置确认端口号,和源站 Nginx 的 listen 指令对一遍,通常是两分钟能查完的事。
只加速静态资源,还是整站加速
这是接入 CDN 时第一个要做的取舍,直接决定后面踩多少坑。
| 方案 | 做法 | 适合的站 | 代价 |
|---|---|---|---|
| 只加速静态资源 | 把图片、CSS、JS 的域名指向 CDN,HTML 仍走源站 | 后台重、插件多、表单和登录逻辑复杂的企业站与商城 | HTML 首字节时间没改善,动态页仍然慢 |
| 整站加速 | 域名整体 CNAME 到 CDN,HTML 也缓存 | 内容站、资讯站、以文章为主的展示站 | 必须精确配置不缓存的路径和 cookie,配错就是登录串号 |
| 整站加速但不缓存 HTML | 域名接 CDN,HTML 设 TTL 为 0,只吃链路优化 | 既要动静态分离又不想改主题的站 | 省了跨地域延迟,但回源压力没降 |
我的建议是先做第一种,跑一周看监控,确认没有异常再考虑整站。整站加速回滚成本高,一旦缓存规则出错,用户看到的是错乱页面,不是加载慢。静态资源方案的回滚只需要把 CNAME 改回去,几分钟的事。
缓存规则:HTML 短,图片长,接口不缓存
CDN 缓存的核心是按路径和文件类型设 TTL。原则很简单:内容会变的给短 TTL,内容不变的给长 TTL,带用户态的一律不缓存。
| 对象 | 建议 TTL | 说明 |
|---|---|---|
| 首页、列表页、文章页 HTML | 300 到 600 秒 | 发布新文章后最迟十分钟可见,配合主动刷新更快 |
| 图片、字体、视频封面 | 30 天 | WordPress 上传的图片带日期目录,天然不会重名 |
| CSS、JS | 7 到 30 天 | 改版时必须改文件名或加版本号,否则用户拿到旧样式 |
/wp-admin/、/wp-login.php |
不缓存 | 后台必须实时,缓存会导致保存失败、样式错乱 |
/wp-json/、/cart/、/checkout/ |
不缓存 | 带用户态和鉴权,缓存会串数据 |
除了 TTL,还要加两条忽略规则:请求带 wordpress_logged_in_ cookie 时不缓存;响应带 Set-Cookie 时不缓存。这两条不做,登录用户和游客会互相看到对方的页面状态。
TTL 定完还要会用刷新。改了一篇文章的标题,主动提交这条 URL 的刷新,比等 TTL 过期快得多;改了整个站的模板,刷目录,不要一条条刷。刷新接口一般有配额,日常小改动用刷新,批量导入或主题改版前先把 TTL 临时调到 60 秒,改完再调回来。这样既不烧配额,也不会让用户看到半新半旧的页面。
CDN 后面怎么拿到真实 IP
接了 CDN 之后,REMOTE_ADDR 全是 CDN 节点的地址,WordPress 的评论拦截、登录限制、统计插件全部失效,防火墙也会误封整个 CDN 节点。真实 IP 在 X-Forwarded-For 里,但这一行可以被伪造,必须只在信任 CDN 的前提下读取。
Nginx 层用 realip 模块还原,配置写在 server 块外:
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 203.0.113.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
set_real_ip_from 要把 CDN 官方公布的回源段全部列进去,不能图省事写 0.0.0.0/0,那等于允许任何人伪造 IP。配好之后 $remote_addr 就是访客真实 IP,PHP 里读到的 REMOTE_ADDR 也跟着变,WordPress 不用改代码。
如果 CDN 用的是 X-Real-IP 或者厂商自定义的头(比如有的用 Ali-CDN-Real-IP),把 real_ip_header 换成对应的头名即可。改完用 curl -H "X-Forwarded-For: 1.2.3.4" 从外部测一下,能伪造成功说明白名单配大了。
后台样式错乱与混合内容
后台白屏、按钮没图标、编辑器加载不出来,八成是 CDN 把 /wp-admin/ 的 CSS 和 JS 也缓存了,或者 CDN 对这些文件做了压缩、合并、图片转换之类的优化。处理办法只有一条:后台路径直接不经过 CDN。在 CDN 里给 /wp-admin/*、/wp-includes/*、/wp-login.php 设 bypass,或者干脆让后台走一个不接 CDN 的子域名。
混合内容是另一类问题:站点已经是 HTTPS,但 CDN 回源走 HTTP,页面里被 CDN 改写出了 http:// 的资源地址,浏览器控制台报 Mixed Content: The page at 'https://...' was loaded over HTTPS, but requested an insecure stylesheet。定位方法是打开控制台看报错文件,或者用 curl 抓页面搜 http://。这类问题的根因通常在源站配置,和SSL 配置里讲的处理路径一样:先把源站的站点地址和资源地址统一成 https,再让 CDN 全链路走 HTTPS。
常见故障对照
| 现象 | 最可能的原因 | 处理 |
|---|---|---|
| 521 / 522 错误 | 源站拒绝 CDN 回源,或回源超时 | 放行回源 IP 段,检查源站负载与 443 端口 |
| 首页正常,内页 404 | 回源 Host 没填域名 | 把回源 Host 改成站点域名 |
| 更新文章后前台不变 | HTML 被缓存且未主动刷新 | 提交 URL 刷新,或把 HTML 的 TTL 调到 300 秒 |
| 后台登录后又跳回登录页 | /wp-admin/ 和登录 cookie 被缓存 |
后台路径设 bypass,关闭对 Set-Cookie 响应的缓存 |
| 所有访客 IP 相同 | 未还原真实 IP | Nginx 配 realip,或装对应的还原插件 |
上线节奏建议按这个顺序:先只接静态资源、TTL 给保守值、观察三天错误日志;再把 HTML 接进来,先给 300 秒;稳定一周后再逐步拉长。全站切换的当天把源站 IP 留在手上,出问题立刻改 DNS 回滚,同时提前一天把 DNS 的 TTL 调到 300 秒,否则回滚本身要等几个小时才生效。CDN 加速和速度优化是两件事,CDN 解决的是距离,本地该做的合并压缩、图片尺寸、数据库清理一样不能少,否则 CDN 只是把一个慢页面更快地送到用户面前。