WordPress 极致速度优化全记录
摘要
WordPress 博客(028000.xyz,Sakurairo 主题)曾经首页 HTML 234KB、Hero 视频 26MB,打开慢到自己都嫌弃。这篇记录一次"极致"速度优化:先测基线,再按层动手——nginx(HTTP/2、gzip、静态缓存)、PHP-FPM(ondemand→dynamic)、MySQL(buffer_pool 16M→128M)、Redis 对象缓存(命中率~81%)、前端(视频压缩、s.nmxc.ltd 本地化、HTML 瘦身)。最终首页 HTML 234KB→96KB(gzip 后 22KB),本机缓存命中 11ms。记住优化的铁律:先测基线,每做一步测一次,别一次改完再找是谁的功劳。
目录
- 一、优化前的基线:先测再动
- 二、nginx 层:HTTP/2、gzip、静态缓存
- 三、PHP-FPM:ondemand→dynamic
- 四、MySQL:buffer_pool 16M→128M
- 五、Redis 对象缓存:命中率 81%
- 六、前端:视频、字体、HTML 瘦身
- 七、插件分工:谁干什么别打架
- 八、最终成绩单
- 九、踩坑记录
- 十、总结
一、优化前的基线:先测再动
优化前先测,记下来,这是基线:
| 指标 | 优化前 |
|---|---|
| 首页 HTML | 234KB |
| Hero 视频 | 26MB |
| 封面图 | 8 张 JPG,共 6.1MB |
| 外部引用 | s.nmxc.ltd 30 处 |
| TTFB(外部) | 慢,体感明显 |
优化的铁律:先测基线,每做一步测一次。一次改十处,最后快了也不知道是谁的功劳;慢了更不知道回滚哪一处。基线是所有对比的锚点。
二、nginx 层:HTTP/2、gzip、静态缓存
# 关键配置
listen 443 ssl http2; # HTTP/2
gzip on;
gzip_comp_level 6; # gzip 6 级(压缩率与 CPU 的平衡点)
# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|webp)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
location ~* \.(woff|woff2|ttf)$ {
expires 365d; # 字体一年
}
location ~* \.(mp4|webm)$ {
expires 7d; # 视频 7 天
}
TLS 只开 1.2/1.3(和全家统一)。gzip 6 级是压缩率和 CPU 消耗的平衡点,再高收益递减。静态资源按类型设不同的过期:图片 30 天、字体 365 天(字体几乎不变)、视频 7 天。
三、PHP-FPM:ondemand→dynamic
PHP-FPM 的进程管理模式从 ondemand 改成 dynamic:
; php-fpm 配置
pm = dynamic
pm.max_children = 8
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4
ondemand 是"来请求才 fork 进程",每个请求都要付进程创建的成本;dynamic 常驻 2 个进程,请求来了直接用。博客这种持续有请求的场景,dynamic 的响应更稳。注意改完要 systemctl reload php-fpm-82,而且OPcache 会缓存旧页面,部署后如果页面没变化,先 reload php-fpm 再怀疑人生。
四、MySQL:buffer_pool 16M→128M
# my.cnf
innodb_buffer_pool_size = 128M # 原 16M
16M 的 buffer_pool,稍微大点的查询就全是磁盘 IO。128M 对这个量级的博客够用了,VPS 内存只有约 1 GiB,不能再大。MySQL 调优的原则:够用就好,内存是全家最缺的资源。
五、Redis 对象缓存:命中率 81%
装 Redis 8.0.2 做对象缓存,64M,专用 redis-blog.service,phpredis 6.3.0:
| 指标 | 数值 |
|---|---|
| Redis 版本 | 8.0.2 |
| 内存 | 64M |
| PHP 扩展 | phpredis 6.3.0 |
| 命中率 | ~81% |
81% 的命中率意味着 8 成的数据库查询被缓存挡掉了,剩下的 2 成才是真的查库。对象缓存和页面缓存分工不同:页面缓存(WP Fastest Cache)存整页 HTML,对象缓存存数据库查询结果,两层叠加。验证命中率:Redis 的 INFO stats 看 keyspace_hits/keyspace_misses,算一下就知道。
六、前端:视频、字体、HTML 瘦身
前端是"体感最明显"的一层:
| 优化项 | 优化前 | 优化后 |
|---|---|---|
| Hero 视频 | 26MB | 8.8MB(H.264 CRF26 720p)+ preload=metadata + 移动端不加载 |
| 封面图 | 8 张 JPG 6.1MB | WebP 2.6MB(原图备份 /www/backup/media-orig-20260930/) |
| 外部引用 | s.nmxc.ltd 30 处 | 0(彻底本地化) |
| 首页 HTML | 234KB | 96KB(gzip 后 22KB) |
几个细节:视频用 H.264 CRF26 转 720p,体积从 26MB 压到 8.8MB,preload=metadata 让浏览器先只加载元数据,移动端直接不加载视频(手机上 Hero 视频是纯负担)。s.nmxc.ltd 的 30 处引用全部本地化,外部依赖清零,页面不再被第三方拖慢。Heartbeat 前台禁用、后台 60s,减少 admin-ajax 的无谓请求。
封面图转 WebP 时,原图备份在 /www/backup/media-orig-20260930/,主题选项里的 URL 同步更新。图片优化永远保留原图,WebP 出问题能回滚。
七、插件分工:谁干什么别打架
| 插件 | 职责 |
|---|---|
| WP Fastest Cache | 页面缓存 |
| Autoptimize | HTML/CSS/JS 压缩合并 |
| WP-Optimize | 数据库优化(transient 清理、表优化) |
WP-Optimize 的 HTML/CSS/JS 压缩要关掉,因为和 Autoptimize 重复了。两个插件同时压缩,轻则浪费 CPU,重则互相打架出乱码。插件分工的原则:一个职责只给一个插件,功能重叠的只留一个。
八、最终成绩单
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 首页 HTML | 234KB | 96KB(gzip 22KB) |
| Hero 视频 | 26MB | 8.8MB |
| 封面图 | 6.1MB JPG | 2.6MB WebP |
| 外部引用 | 30 处 | 0 |
| 本机缓存命中 | — | 11ms |
| 本机动态 | — | 0.33s |
| 外部 TTFB | 慢 | ~0.9-1.1s(瓶颈在法兰克福网络路径,非服务器) |
外部 TTFB 的瓶颈在法兰克福的网络路径,不是服务器性能,这一点要认清楚:优化到网络瓶颈就收手,再往下是运营商的事。当时还出了份 Word 版报告归档。
九、踩坑记录
坑 1:OPcache 导致部署后页面不变
改完 PHP 或主题,页面还是旧的,第一反应是"缓存插件没清"。其实是 PHP-FPM 的 OPcache。解法:systemctl reload php-fpm-82。以后部署后页面不变,先 reload php-fpm,再查别的缓存。
坑 2:两个压缩插件打架
WP-Optimize 和 Autoptimize 都有 HTML/CSS/JS 压缩,同时开会冲突。解法:WP-Optimize 只留数据库优化,压缩全给 Autoptimize。教训:装插件前先看功能是否重叠。
坑 3:父主题改动升级会被覆盖
theme-plus.php 的改动记在子主题里,别直接改父主题。WordPress 主题升级会覆盖父主题文件,直接改等于白干。子主题(sakurairo-child)就是干这个的:custom.css、custom.js、功能覆盖全放子主题。
坑 4:CDN 的取舍
当时定了上 Cloudflare 免费版(SSL Full Strict + 静态边缘缓存 + wp-admin 不缓存 + Brotli/HTTP3),卡在等用户定注册邮箱。CDN 是"网络瓶颈"的解法,服务器优化到头了,剩下的交给边缘节点。注意 nps 管理面板直连不走 CDN。
瀑布图解读:时间花在哪了
优化时看瀑布图(浏览器开发者工具 → Network),时间花在哪一目了然:
| 阶段 | 看什么 | 优化前的问题 |
|---|---|---|
| DNS + 建连 + TLS | 域名解析和握手时间 | 外部 TTFB 高,瓶颈在法兰克福路径 |
| 首字节(TTFB) | 服务器生成 HTML 的时间 | PHP-FPM ondemand、MySQL buffer 小 |
| 内容下载 | HTML/JS/CSS/图片的体积 | 234KB HTML、6.1MB 封面、26MB 视频 |
| 第三方 | 外部域名的请求 | s.nmxc.ltd 30 处引用 |
瀑布图是优化的"地图":哪一段最长,就先动哪一段。别凭感觉优化,感觉会骗人,瀑布图不会。
持续监控:别让优化成果滑坡
优化完不是一劳永逸,装个新插件、换张大封面,成绩就滑回去了。防滑坡三件套:
- 定期测:每月跑一次首页加载测试,记下 TTFB 和 HTML 体积,和基线对比;
- 传图规范:封面图先转 WebP 再传,别直接丢 5MB 的原图上去;
- 插件审计:每季度看一次插件列表,停用的删掉,功能重叠的只留一个。
性能是"熵增"的,维护是"做功"。不维护的优化,半年后打回原形。
性能优化的终点:够用就好
优化做到最后,会遇到一个问题:什么时候停?首页 96KB 了,要不要压到 80KB?TTFB 0.4s 了,要不要压到 0.2s?答案是:够用就好。优化的终点不是"最快",是"快到用户感知不到慢"。
感知阈值:页面 1 秒内打开,用户觉得"快";1-3 秒,"能接受";3 秒以上,"慢"。从 5 秒优化到 1 秒,体感天差地别;从 1 秒优化到 0.5 秒,用户基本无感。优化的投入产出比,在 1 秒附近断崖式下跌。所以优化目标定在"1 秒内",到了就收手,剩下的时间干点别的。
优化的成本:每次优化都有成本。Redis 多占 64M 内存,页面缓存多占磁盘,Autoptimize 的合并多一次构建。成本不只是资源,还有复杂度:缓存层越多,"改了没生效"的排查越难(OPcache、页面缓存、CDN 缓存,三层套娃)。优化是加法,排查是乘法。够用就好,也是给未来的自己减负。
优化的敌人:最大的敌人不是"慢",是"滑坡"。新插件、超大封面、第三方统计代码,悄悄把成绩吃回去。所以"持续监控"那节比"优化清单"更重要。优化是一次性的,防滑坡是长期的。把"每月测一次"写进日历,比把"首页压到 80KB"有价值得多。
最后,性能优化的最高境界:用户不需要知道你优化过。页面打开就是快,没有加载动画,没有"正在优化中"的提示。好的优化是隐形的,就像好的基础设施——没人注意到它,恰恰说明它工作得很好。
缓存的层次:从浏览器到 CDN
这篇讲了页面缓存(WP Fastest Cache)、对象缓存(Redis)、OPcache,漏了一层:浏览器缓存,和再往外一层:CDN。缓存是分层的,每层挡掉一部分请求。
用户 ←→ CDN(边缘) ←→ nginx(静态) ←→ 页面缓存 ←→ PHP+OPcache ←→ Redis ←→ MySQL
快 ←——————————————————————————————————————————————————————————————→ 慢
浏览器缓存:静态资源(图片、CSS、JS)设长过期,浏览器第二次打开不请求了。这是最快的缓存,因为"不请求"比"请求了命中"还快。版本号策略:文件名带 hash(style.abc123.css),更新即换名,缓存永不过期。这是前端构建的常规操作。
CDN:Cloudflare 免费版,把静态资源推到边缘节点。用户在广东,请求到香港节点,不用跑到法兰克福。这是"网络瓶颈"的解法:服务器再快,跨国线路的物理延迟摆在那。CDN 把内容搬到用户旁边,延迟从 900ms 降到 50ms。动态内容(wp-admin、登录)不缓存,只缓存静态。
层次的原则:越靠近用户,越快,越便宜(不消耗源站资源)。优化时,从外往里配:浏览器缓存 → CDN → nginx 静态 → 页面缓存 → 对象缓存。外层命中了,内层不工作,省资源。缓存命中率,每层单独看:浏览器看 Network 的 (from disk cache),CDN 看 HIT/MISS 头,页面缓存看响应头,Redis 看 INFO。
缓存的代价:每一层都是"一致性"的妥协。缓存了,就有"过期"问题:文章更新了,CDN 还是旧的。解法是"主动 purge":发布文章时,调 Cloudflare API 清缓存;或者设短过期(HTML 不缓存,只缓存静态)。动态和静态分开:HTML 走页面缓存(短),静态走 CDN(长)。分开,才能各取所需。
缓存的层次配齐,博客的访问链条就是:用户 → 边缘命中(快)→ 未命中 → 源站层层挡 → MySQL(最后手段)。大部分请求在边缘就结束了,源站很闲。闲的服务器,才是快的服务器。
HTTP/3:下一步
nginx 配好 HTTP/2,下一步是 HTTP/3(QUIC)。Cloudflare 的方案里,HTTP/3 是标配(开一下就行);自建 nginx,HTTP/3 要较新的版本 + QUIC 支持。HTTP/3 的价值:弱网下更快(0-RTT、连接迁移),手机上刷博客,体感提升明显。
但 HTTP/3 不是"开了就快":UDP 443 要在防火墙放行,中间设备(运营商)对 UDP 的限速可能抵消收益。尝鲜可以,生产先观望。优化的节奏:HTTP/2 先吃透,CDN 先上好,HTTP/3 排队。技术追新,永远排在"稳定"后面。 WordPress 优化到这,服务器这头的事做完了。剩下的路在 CDN 和网络,该交棒了。优化有终点,够用就好。记住感知阈值:1 秒内快,1-3 秒能接受,3 秒以上慢。到了 1 秒内,就收手,剩下的时间写文章去。性能是手段,内容才是目的,别把手段当成目的。优化到 1 秒内,剩下的精力,给内容。读者为内容来,不是为 TTFB 来。TTFB 是门面,内容是里子。门面收拾到 1 秒内,里子慢慢写。
另外,优化报告(Word 版)归档在 NAS,数字都在里面。这篇是"怎么做",报告是"做到了多少",两个配合看。 优化的最后一块拼图是"测量"。没测量的优化,是玄学。每次改完,测三组数:首页 HTML 大小(curl 看 Content-Length)、TTFB(curl -w 看 time_starttransfer)、Lighthouse 跑分。记下来,和上次对比。数字涨了,优化有效;数字没动,改动白费。优化的习惯是"改一次,测一次,记一次"。这篇讲的每个手段,都值得这样验证一遍。验证完了,这篇的使命就完成了。使命完成,工具收好,下一篇见。优化是手段,写文章才是正事。正事要紧,手段够用就行。够用,就收手。
十、总结
WordPress 优化是分层工程:nginx(HTTP/2、gzip、静态缓存)→ PHP-FPM(dynamic)→ MySQL(buffer_pool)→ Redis(对象缓存 81%)→ 前端(视频/图片/外部引用/HTML 瘦身)→ 插件分工(不打架)。每层做完测一次,基线对比。最终首页 234KB→96KB(gzip 22KB),本机 11ms。记住:优化到网络瓶颈就收手;OPcache 是部署后的第一嫌疑人;父主题别直接改。