适用环境:WordPress 7.1.2 + Rank Math 1.0.279 + Nginx + PHP 8.3 + MariaDB 10.11(Rocky Linux 10.2,腾讯云)
背景
前阵子把博客从阿里云迁到腾讯云。没用镜像的方式,而是应用层重建 + 数据搬迁:新机器装好 Nginx / PHP-FPM / MariaDB,然后 mysqldump 导库、tar 打包 wp-content、scp 过去。
迁移本身很顺,坑在后面一个接一个:固定链接 404(缺 try_files)、502(nginx 配的 unix socket 但 php-fpm 监听 TCP 9000)、页面被 CDN 缓存了坏掉时期的 404……这些都比较常规,修完网站就正常了。
真正的硬骨头是 sitemap_index.xml 一直 404。这篇文章记录完整的排查过程——最后发现真凶居然是数据库里一行空选项,而且中间还踩了两个”写对了地方但写错了字段”的坑。
第一轮:把能排除的都排除掉
sitemap 404 常见原因无非:伪静态、rewrite 规则、插件模块没开、CDN 缓存。挨个验:
1. CDN 干扰?直连源站绕过:
curl -sk -o /dev/null -w "%{http_code}\n" \
--resolve huangdi888.top:443:127.0.0.1 \
https://huangdi888.top/sitemap_index.xml
源站也 404,CDN 无罪(响应头 eo-cache-status: MISS 也佐证了这点)。
2. rewrite 规则?
php -r "require 'wp-load.php'; foreach (get_option('rewrite_rules') as \$k => \$v)
if (strpos(\$k,'sitemap')!==false) echo \"\$k => \$v\\n\";"
sitemap_index\.xml$ => index.php?sitemap=1 在,规则没问题。
3. Rank Math 模块?
rank_math_modules 选项里 sitemap 在,模块开着。template_redirect 钩子上的 render_sitemaps 也挂上了。
4. query_vars?
在钩子里打日志,{"sitemap":"1"},解析正确,但 is_404=true。
到这里有点诡异:WordPress 明明把请求识别成了 sitemap,却返回了 404。
第二轮:直接驱动 Generator
既然 Web 链路全通,那就绕过 Web 直接测插件内部:
$g = new RankMath\Sitemap\Generator();
echo strlen($g->get_output('1', 1)); // 索引输出长度
echo strlen($g->get_output('post', 1)); // post-sitemap 长度
结果:两个都是 0。而 wp_count_posts() 显示文章 10 篇、页面 9 页,内容都在。
翻了 Rank Math 源码,class-sitemap-xml.php 里有一段:
if ( empty( $this->sitemap ) ) {
$wp_query->set_404(); // ← 404 是插件主动设的
status_header( 404 );
return;
}
404 不是”找不到”,是插件主动设的——因为 Generator 认为没有任何内容类型需要进 sitemap。
第三轮:数据库里找答案
查 wp_options 表里 Rank Math 的所有选项行:
php -r "require 'wp-load.php'; global \$wpdb;
print_r(\$wpdb->get_col(\"SELECT option_name FROM {\$wpdb->options}
WHERE option_name LIKE 'rank_math%'\"));"
发现关键的一行:rank-math-options-sitemap 存在,但值是空的。迁移导库的时候这行的内容丢了。general 和 titles 两个选项都完好,就 sitemap 这行是空的——丢失得毫无规律。
踩坑一:连字符 vs 下划线
第一反应是补选项,但想当然地写成了:
update_option('rank_math_options_sitemap', [...]); // ❌ 下划线,无效!
写完一测,还是 404。再查表才发现库里同时存在两套命名:
rank-math-options-sitemap(连字符,Rank Math 实际用的)rank_math_options_sitemap(下划线,我写的那份,没人读)
看插件源码 includes/helpers/class-options.php 第 84 行确认:
update_option( 'rank-math-options-sitemap', $sitemap ); // 连字符
踩坑二:字段名带 _sitemap 后缀
换成连字符的 key 重新写,内容是:
$opt['pt_post'] = 'on';
$opt['pt_page'] = 'on';
还是 404。Generator 输出还是 0。这时不再猜了,直接 grep 插件源码看判断条件:
grep -n "pt_" includes/modules/sitemap/providers/class-post-type.php
第 78 行:
! Helper::get_settings( 'sitemap.pt_' . $type . '_sitemap' ) ||
真凶找到了:1.0.279 判断的字段是 pt_post_sitemap、pt_page_sitemap,带 _sitemap 后缀。我写的 pt_post 根本不在它的检查范围内——写了等于没写。
修复
$opt = get_option('rank-math-options-sitemap');
$opt['items_per_page'] = 200;
$opt['include_images'] = 'on';
$opt['include_featured_images'] = 'on';
$opt['pt_post_sitemap'] = 'on'; // 带 _sitemap 后缀
$opt['pt_page_sitemap'] = 'on';
$opt['tax_category_sitemap'] = 'on';
update_option('rank-math-options-sitemap', $opt);
验证:
索引输出长度: 569
post-sitemap 长度: 4339
源站 sitemap -> 200
sitemap_index.xml 恢复,索引里 post-sitemap 和 page-sitemap 都回来了。顺带一提,后台 Rank Math → Sitemap Settings 页面之前一直空白,也是同一个原因(选项数组缺字段导致面板渲染失败),选项修好后自动恢复。
最后进后台点一次 Save Changes 让插件补全所有默认字段,去 Google Search Console 重新提交 sitemap,完事。
踩坑总结
这次排查最大的教训:改插件选项之前,先 grep 插件源码确认真实的 key 和字段名,不要凭经验猜命名风格。
三个具体坑:
- Rank Math 的 option key 是连字符(
rank-math-options-sitemap),和大多数 WordPress 插件的下划线风格不一样 - sitemap 模块的字段名带
_sitemap后缀(pt_post_sitemap),不是直觉上的pt_post - 插件选项行丢失不报错,只 404——迁移后 SEO 类插件的 sitemap 建议主动验一次,这种失败太隐蔽了
迁移收尾清单(自查)
- 固定链接正常(nginx
try_files $uri $uri/ /index.php?$args) - fastcgi_pass 和 php-fpm 监听方式一致(socket vs TCP)
- 修完源站清一次 CDN 缓存(坏掉期间的 404 可能被缓存住了)
- sitemap 200 且内容完整
- 删 readme.html(暴露 WP 版本)
- 关掉排查用的 WP_DEBUG
- 删掉所有临时探针文件
排查期间往 mu-plugins 里塞的调试探针记得删干净——挂在 template_redirect 上的探针写错了会全站 500,后台页面也跑不掉,还会制造”设置页空白”这种假象,把排查方向带偏。
https://huangdi888.top/wp-content/uploads/2026/09/wordpress-sitemap-404-diagnosis.zip
这里上面的zip可以导入您的Agent里面,说实话这里有一些复杂,如果您使用可以导入让ai协助路径也可以替换您自己的方便使用,下载zip后也可以直接点击里面的md文件自行查看分析,不过技能版更便于 AI 执行
