最近我将 WordPress 图片资源迁移到了独立的 media 子域名:
https://media.shuijingwanwq.com/
原本的目标是减少主站 www.shuijingwanwq.com 的静态资源压力,并让图片资源通过单独的 CDN 链路处理。
迁移完成后,新写的文章已经能够正常使用 media.shuijingwanwq.com 的图片地址。但是在 EdgeOne 的访问分析中,我发现一个不太正常的现象:media 切换已经过去几个小时,最近 1 小时仍然有不少 301 请求。
一开始我以为是 EdgeOne 缓存、浏览器缓存,或者搜索引擎残留请求导致的。排查之后才确认:问题主要来自历史文章正文中写死的旧图片地址。

一、问题现象:EdgeOne 最近 1 小时仍然有 301
在 EdgeOne 指标分析中,最近 1 小时的数据大致如下:
200:183.2 MB,占 96.16%
301:5.59 MB,占 2.93%
404:1.55 MB,占 0.81%
单看比例,301 其实不算特别夸张。但是因为图片迁移已经完成了几个小时,理论上新页面应该尽量直接输出 media 子域名,不应该再频繁访问旧的 /wp-content/uploads/ 地址。
于是我进一步筛选状态码:
边缘响应状态码 = 301
筛选之后可以看到,最近 1 小时仅 301 请求就有:
L7 访问流量:7.25 MB
L7 访问请求数:966 次

继续查看 URL Path 排行,发现大量 301 都集中在旧图片路径上,例如:
/wp-content/uploads/2026/07/21-2-1024x175.png
/wp-content/uploads/2026/07/23-2-1024x180.png
/wp-content/uploads/2018/04/14-3-520x245.png
/wp-content/uploads/2023/12/3-6.png
/wp-content/uploads/2021/08/5-3.png

这说明,虽然图片已经迁移到 media 子域名,但是仍然有大量请求在访问旧图片 URL。
二、先确认 301 是否回源
看到 301 之后,首先要确认一个关键问题:
这些 301 是 EdgeOne 边缘直接处理,还是每次都回源到 WordPress / Nginx?
如果每次都回源,说明源站压力还在。
如果是边缘直接返回 301,问题就小很多。
我用 curl 测试其中一个旧图片地址:
curl -I https://www.shuijingwanwq.com/wp-content/uploads/2026/07/21-2-1024x175.png
返回结果中比较关键的是:
HTTP/2 301
location: https://media.shuijingwanwq.com/2026/07/21-2-1024x175.png
content-length: 0
server: TencentEdgeOne
x-cache-lookup: Return Directly
这个结果说明:
旧图片地址的 301 是由 EdgeOne 边缘节点直接返回的,并没有回源到 WordPress。
其中:
x-cache-lookup: Return Directly
是一个比较关键的信号。它说明 EdgeOne 直接返回了跳转响应,而不是把请求转发到源站后再返回。
因此,这一部分 301 虽然在 EdgeOne 统计中还存在,但它本身不是源站压力问题。
三、再确认 media 子域名图片是否正常缓存
接着测试跳转后的 media 图片地址:
curl -I https://media.shuijingwanwq.com/2026/07/21-2-1024x175.png
第一次返回:
HTTP/2 200
server: cloudflare
cache-control: max-age=2592000
cf-cache-status: MISS
第二次再次请求后返回:
HTTP/2 200
server: cloudflare
age: 10
cf-cache-status: HIT
这说明 media 子域名对应的图片资源已经可以正常被 Cloudflare 缓存。
所以当时的链路可以理解为:
旧图片地址:
https://www.shuijingwanwq.com/wp-content/uploads/...
EdgeOne 边缘直接 301:
location: https://media.shuijingwanwq.com/...
新图片地址:
https://media.shuijingwanwq.com/...
Cloudflare:
第一次 MISS,第二次 HIT
也就是说,图片文件本身没有反复回源,media 子域名缓存也是正常的。
四、真正的问题:旧文章 HTML 里仍然输出旧图片地址
既然 EdgeOne 301 是边缘直接处理,media 图片缓存也正常,那剩下的问题就是:
为什么还有这么多旧图片地址请求?
我先检查首页 HTML:
curl -s https://www.shuijingwanwq.com/ | grep -o "/wp-content/uploads/[^\"') ]*" | head
首页没有输出,说明首页基本是干净的。
然后检查一篇旧文章:
curl -s https://www.shuijingwanwq.com/2021/08/04/5122/ | grep -o "/wp-content/uploads/[^\"') ]*" | head
结果返回了大量旧路径:
/wp-content/uploads/2021/08/1-2.png
/wp-content/uploads/2021/08/1-2.png
/wp-content/uploads/2021/08/2-2.png
/wp-content/uploads/2021/08/2-2.png
/wp-content/uploads/2021/08/3-1.png
这就说明,问题不是单纯的历史缓存,而是旧文章页面本身还在输出旧图片地址。
我又在浏览器中查看这篇旧文章的页面源代码,发现确实存在大量:
/wp-content/uploads/

这就解释了为什么 EdgeOne 仍然有 301:
用户或爬虫访问旧文章
↓
旧文章 HTML 中仍然包含 /wp-content/uploads/ 图片地址
↓
浏览器继续请求旧图片地址
↓
EdgeOne 边缘返回 301
↓
跳转到 media.shuijingwanwq.com
五、为什么新文章正常,旧文章不正常?
一开始我还怀疑是不是经典编辑器和 Gutenberg 的差异。
后来检查后发现,跟编辑器类型没有关系。
根本原因是:
WordPress 文章正文里的图片地址,本质上是写死在 wp_posts.post_content 里的 HTML 字符串。
迁移前插入的图片,文章正文里保存的是:
<img src="https://www.shuijingwanwq.com/wp-content/uploads/2021/08/1-2.png">
迁移后新上传、新插入的图片,文章正文里保存的是:
<img src="https://media.shuijingwanwq.com/2026/07/1-22.png">
所以 media 配置只影响迁移之后新生成的图片 URL,不会自动回头修改历史文章里的 HTML。


六、先用 Better Search Replace 做 dry run
因为我的站点已经安装了 Better Search Replace 插件,所以没有额外安装 WP-CLI,而是直接用插件做检查。
一开始我搜索的是:
Search for:
/wp-content/uploads/
Replace with:
https://media.shuijingwanwq.com/
只选择:
wp_posts
并勾选:
Run as dry run
dry run 结果显示:
6455 cells were found that need to be updated

不过这个搜索条件太宽泛了,不适合直接正式替换。
因为旧文章里有两种情况:
第一种是图片完整 URL:
https://www.shuijingwanwq.com/wp-content/uploads/2021/08/1-2.png
这种可以安全替换成:
https://media.shuijingwanwq.com/2021/08/1-2.png
第二种是正文里的技术说明、路径记录、服务器目录说明,例如:
/wp-content/uploads/2021/07/
这种不一定是图片加载地址,可能只是文章内容的一部分。如果直接替换,反而会破坏文章原本的排查语境。
所以不能直接把所有 /wp-content/uploads/ 都替换掉。
七、改用完整旧图片域名做正式替换
更安全的搜索条件应该是完整旧图片 URL 前缀:
Search for:
https://www.shuijingwanwq.com/wp-content/uploads/
Replace with:
https://media.shuijingwanwq.com/
继续只选择:
wp_posts
并先勾选:
Run as dry run
这次 dry run 结果显示:
3304 cells were found that need to be updated

这个结果更符合预期。
因为它只针对历史文章中明确写死的完整旧图片地址:
https://www.shuijingwanwq.com/wp-content/uploads/
不会误伤普通正文里记录的相对路径:
/wp-content/uploads/
在正式替换前,我使用 UpdraftPlus 做了数据库备份。确认数据库备份完成后,取消 Run as dry run,执行正式替换。
正式替换结果:
During the search/replace, 1 tables were searched, with 3304 cells changed in 3299 updates.

八、清理 W3 Total Cache 后重新验证
正式替换完成后,我先查看旧文章的代码编辑器,发现图片地址已经变成:
https://media.shuijingwanwq.com/2021/08/1-2.png

但是一开始用 curl 访问前台页面时,仍然能看到旧地址。
这说明数据库已经替换成功,但前台页面缓存还没有刷新。
于是我在 WordPress 后台清理了 W3 Total Cache:
Performance → Purge All Caches
清理后重新执行:
curl -s "https://www.shuijingwanwq.com/2021/08/04/5122/?_nocache=$(date +%s)" | grep -o "https://www.shuijingwanwq.com/wp-content/uploads/[^\"') ]*" | head
结果没有任何输出。
再检查相对路径:
curl -s "https://www.shuijingwanwq.com/2021/08/04/5122/?_nocache=$(date +%s)" | grep -o "/wp-content/uploads/[^\"') ]*" | head
也没有任何输出。
这说明这篇旧文章前台 HTML 已经不再输出旧图片地址。
九、为什么没有继续替换剩余的 /wp-content/uploads/
第一批完整旧图片 URL 替换完成后,我又用 Better Search Replace 做了一次 dry run:
Search for:
/wp-content/uploads/
Replace with:
https://media.shuijingwanwq.com/
结果显示残留数量已经大幅减少。

但我没有继续正式替换这部分残留。
原因是,通过只读查询可以看到,剩余的 /wp-content/uploads/ 很多并不是图片加载 URL,而是文章正文中的技术说明、附件标题、历史修订版本,或者以前排查问题时记录的服务器路径。
例如某些文章本身就在讲:
通过 FlashFXP 上传文件至 /wp-content/uploads/2021/07/
这种内容如果强行替换成:
https://media.shuijingwanwq.com/2021/07/
反而会破坏文章原意。
所以我的处理原则是:
完整旧图片 URL 可以批量替换;
单独的 /wp-content/uploads/ 不再无脑替换。
十、这次排查后的结论
这次问题的核心并不是 EdgeOne 301 异常,也不是 media 子域名缓存失败,而是:
迁移 media 子域名后,WordPress 旧文章正文中保存的历史图片 URL 不会自动更新。
最终处理逻辑是:
1. EdgeOne 仍然有 301
2. 筛选发现 301 集中在 /wp-content/uploads/ 图片路径
3. curl 确认 301 是 EdgeOne 边缘直接返回,没有回源
4. curl 确认 media 子域名图片可以正常 Cloudflare HIT
5. 检查旧文章 HTML,发现仍然输出旧图片地址
6. 用 Better Search Replace dry run 检查完整旧图片 URL
7. 备份数据库
8. 正式替换完整旧图片 URL
9. 清理 W3 Total Cache
10. 重新验证旧文章前台 HTML 已经不再输出旧图片地址
这次真正需要替换的是:
https://www.shuijingwanwq.com/wp-content/uploads/
替换为:
https://media.shuijingwanwq.com/
而不是直接把所有:
/wp-content/uploads/
都替换掉。
十一、后续观察
替换完成后,EdgeOne 中的 301 不会立刻归零。
原因包括:
搜索引擎仍然可能请求旧图片 URL;
浏览器缓存中可能还保存旧地址;
外部引用可能仍然访问旧图片路径;
EdgeOne 的统计数据也有时间窗口延迟。
但后续真实用户访问旧文章时,页面 HTML 已经直接输出 media 子域名图片地址,不再先请求旧的 /wp-content/uploads/,因此由文章页面触发的 301 应该会逐步下降。
对我来说,这次排查也再次提醒了一点:
WordPress 的媒体 URL 配置影响的是未来新插入的图片,不会自动修改历史文章正文。
如果站点运营时间比较长,历史文章数量比较多,在迁移图片域名、CDN 域名、静态资源域名时,一定要把历史文章正文中的固定 URL 一起纳入排查范围。
需要长期技术维护或远程问题排查?
我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。
如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:
- ✅ PHP / Laravel / Yii2 老项目无人维护
- ✅ Go / Gin 后端接口需要排查或优化
- ✅ WordPress 网站访问慢、报错或插件冲突
- ✅ Nginx / MySQL / Redis / Linux 服务器异常
- ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
- ✅ 需要长期远程技术支持或兼职维护
更多介绍请查看:关于我 & 合作
微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan

发表回复