WordPress 的 robots.txt 和 wp-sitemap.xml 为什么会同时 404

robots.txt 和 wp-sitemap.xml 请求经过 Web 服务器,错误路由返回 404,正确路由进入 index.php

结论先行

启用了非空固定链接、WordPress 前台 Home URL 位于域名根路径、站点在 WordPress 中被标记为公开可索引的前提下,如果 /robots.txt/wp-sitemap.xml 同时返回的是 Nginx 风格 404,而根 URI 和 PHP 又有可用处理路径,先检查 Nginx 有没有把不存在的虚拟路径交给 WordPress 的 index.php。错误页外观只是一条线索,不能单独证明故障层级。

这次故障不是少了两个文件,而是少了一次请求交接。两个 URL 都由 WordPress 在运行时生成:核心重写规则把 robots.txt 映射到 index.php?robots=1,把 wp-sitemap.xml 映射到 index.php?sitemap=index。请求若被 Nginx 提前以 404 结束,WordPress 连出场的机会都没有。

最终修复只有一条核心逻辑:实体文件和目录优先,其余前台请求进入 index.php

location / {
    try_files $uri $uri/ /index.php?$args;
}

但不要把这一段不看上下文地粘进生产配置。下面先用四个请求的状态码、Content-Type、响应链和正文,判断故障究竟在 Nginx、WordPress,还是站点可见性和插件层。

现场:两个 SEO 入口一起失效

这是本站完成证书恢复与严格 HTTPS 验收后发现的第二个问题。环境是 Nginx、PHP-FPM 和 WordPress 7.0.2,首页能打开,带 /index.php/ 的历史文章也能访问,但两个基础入口异常:

  • /robots.txt 返回 404;
  • /wp-sitemap.xml 返回 404;
  • 当天修复前的访问日志分别记录到 4 次和 9 次 404;
  • PHP-FPM 没有对应报错,站点也没有 5xx。

这组现象很关键。首页能开,只能说明根 URI 有可用处理路径,内容可能来自 PHP、缓存或代理;带 index.php 的地址能开,只能说明这条 PHP 路径可用。它们都不能证明“不存在的前台路径”已经被转交给 WordPress。

为什么磁盘上找不到这两个文件

WordPress 默认不会在网站根目录自动创建一份实体 robots.txt。核心的 @@IFORZINLINE1@@ 会在适用条件下注册 robots\.txt$,再由 @@IFORZINLINE3@@text/plain 动态输出内容。

核心 XML sitemap 也是同样思路。@@IFORZINLINE0@@ 注册索引、子 sitemap 和 XSL 路由;默认索引入口是 /wp-sitemap.xml,不是很多 SEO 插件使用的 /sitemap_index.xml

因此,下面这样的配置会把它们一起截断:

location / {
    try_files $uri $uri/ =404;
}

Nginx 会先检查磁盘上的同名文件和目录。两者都不存在时,最后参数 =404 会直接结束请求。根据 Nginx @@IFORZINLINE1@@ 官方说明,最后参数写成普通 URI 或命名 location 时才会内部跳转;要把请求交给 WordPress,应指向真实的 index.php 或站点实际使用的 WordPress 命名 location。

先做四个请求,不要先改配置

把域名换成自己的,分别测试漂亮路径和 WordPress 查询形式:

site='https://example.com'

curl -sS --connect-timeout 5 --max-time 15 -D - "$site/robots.txt"
curl -sS --connect-timeout 5 --max-time 15 -D - "$site/wp-sitemap.xml"

curl -sS --connect-timeout 5 --max-time 15 -D - "$site/?robots=1"
curl -sS --connect-timeout 5 --max-time 15 -D - "$site/?sitemap=index"

结果可以这样读:

  • /?robots=1 为 200、Content-Type 以 text/plain 开头,正文是 robots 规则,而漂亮路径 404:WordPress 已能动态输出 robots,重点查 Nginx 对 /robots.txt 的路径交接。
  • /?sitemap=index 直接返回 200 XML:再确认 Content-Type 以 application/xml 开头且正文可解析,才能证明核心生成了 sitemap。
  • sitemap 查询返回 301 到 /wp-sitemap.xml,且响应含 X-Redirect-By: WordPress:这表明 WordPress 已 bootstrap 并识别查询,但不能单独证明 sitemap 正文正常;单凭 301 和 Location 不能作此判断,也不要跟随跳转后再把同一个 404 当成独立证据。
  • sitemap 查询仍为 404 或正文不是 XML:检查“建议搜索引擎不索引本站”、wp_sitemaps_enabled 过滤器或 SEO 插件。WordPress 的 @@IFORZINLINE1@@ 默认读取站点公开状态,禁用后核心会有意返回 404。
  • 四个请求都失败:不要直接套用通用 location /;继续检查 PHP location、FastCGI 查询参数、WordPress bootstrap、插件和错误日志。
  • 未启用漂亮固定链接:WordPress 核心的 @@IFORZINLINE0@@ 返回 /?sitemap=index,不能看到 /wp-sitemap.xml 404 就直接判定路由坏了。

如果 PHP 缺少 SimpleXML,WordPress 核心 sitemap 会返回 501;那也不是本文讨论的 rewrite 404。

这一步的价值是把问题分层。反复保存固定链接、清缓存或创建实体文件,都不能替代分层诊断。

本次修复:让虚拟 URL 进入前端控制器

先找到实际被目标虚拟主机 include 的配置文件。面板环境经常把主配置、站点配置、rewrite 和代理规则拆开;改到一份没有被加载的示例文件,nginx -t 也可能照样成功。

确认目标后,先排查是否已有 location = /robots.txt,是否有匹配 .xml.xsl.txt 的正则静态块,以及是否存在 ^~ 前缀或 sitemap 专用正则。精确 location 会立即结束匹配,正则 location 也可能覆盖普通 location /;这些块若提前截住请求,只改通用 fallback 仍不会生效。

排除优先级冲突后先备份,再修改现有的 location /,或者在确实没有该块时补上。不要制造两个重复的 location /

location / {
    try_files $uri $uri/ /index.php?$args;
}

这三段的含义依次是:

  • $uri:真实文件优先,例如图片、CSS 和 JavaScript;
  • $uri/:真实目录优先;
  • /index.php?$args:其余请求内部转给 WordPress,并保留原查询参数。

本站原有几组精确前缀代理规则,修复时全部保留。Nginx 的 @@IFORZINLINE0@@ 匹配规则 决定了 ^~ 前缀和精确匹配仍可处理自己的路径,普通 WordPress 请求才走通用 fallback。

修改后先检查,成功才平滑重载。下面只适用于 PATH 中的 nginx 就是运行中实例的常规安装;面板或多实例环境必须使用线上 master 对应的二进制、配置路径和 reload 方式,并保证测试与重载针对同一实例。

sudo nginx -t
sudo nginx -s reload

@@IFORZINLINE0@@ 能发现语法错误和无法打开的引用文件;reload 机制 会在新配置无法应用时继续使用旧配置。但语法正确不代表路由语义正确,所以 HTTP 回归一步也不能少。

三个常见误修

创建一份实体 robots.txt

在本文这种“实体文件优先”的配置中,这只能遮住一个症状。wp-sitemap.xml、子 sitemap 和其他 WordPress 虚拟路由仍然失效,而且实体文件会绕过核心动态维护的 Sitemap: 行。

只给 wp-sitemap.xml 写一个特例

索引也许恢复了,但 /wp-sitemap-posts-post-1.xml 之类的子路由仍会 404。正确修复点是通用前端控制器交接,而不是只照顾一个 URL。

删除 PHP location 里的文件存在性检查

很多站点会在 PHP handler 中保留 try_files $uri =404,防止把不存在的任意 .php 脚本交给 PHP-FPM。不要为了修前台路由把这层安全检查拆掉;应由 location / 内部跳到真实存在的 /index.php

同样,反复执行 @@IFORZINLINE0@@ 也不解决这个故障。它只刷新 WordPress 的重写规则缓存,而且成本较高;请求没有进入 index.php 时,缓存里的规则根本没有机会生效。

发布前必须完成的整站验收

本次修复后,不只检查“两个 URL 变成 200”,而是按下面的信号一起验收:

  • /robots.txt 返回 200,Content-Type 以 text/plain 开头,内容包含正确 sitemap 地址;
  • /wp-sitemap.xml 返回 200,Content-Type 以 application/xml 开头,XML 可以解析;
  • sitemap 中至少一个子 sitemap 返回 200;
  • 首页、代表文章、RSS 和静态资源继续正常;
  • 随机不存在路径仍返回真正的 404,而不是被错误改成 200;
  • HTTP 仍只跳转一次到唯一 HTTPS 主域;
  • 访问日志没有新增 5xx,Nginx 和 PHP 错误日志没有新增相关错误。

本站当前公网复测结果是:robots、sitemap、子 sitemap、首页、文章和 RSS 均为严格 HTTPS 200;随机不存在路径为 404;修复后的统计窗口没有 5xx。凌晨备份的数据库、站点文件和服务器配置也都通过校验。

回滚怎么做

如果 reload 后出现文章、静态资源或代理路径回归:

  • 立即恢复刚才备份的目标站点配置;
  • 再次运行 nginx -t
  • 检查通过后平滑 reload;
  • 重复 robots、sitemap、子 sitemap、首页、文章、RSS、随机 404 和错误日志验证。

不要用“重启整个服务器”代替回滚,也不要在故障现场同时改永久链接、PHP 配置和插件。一次只改一个变量,才能知道是哪一项真正生效。

最后判断

robots.txtwp-sitemap.xml 同时 404,不是一个可以无条件套用的“Nginx 必错”结论;固定链接状态、前台 Home URL 路径、站点可索引状态和插件都可能改变结果。但当 robots 查询正文正常、漂亮路径失败,sitemap 查询由 WordPress 明确重定向回同一个失效漂亮路径,并且两者受同一个 Nginx location 处理时,证据已经足够集中:先修 Web 服务器到前端控制器的交接,而不是凭空补文件。

这次真正恢复的也不只是两个 SEO URL,而是整组 WordPress 虚拟路由。更多经过实际回滚和验收的记录会持续整理在文章归档里。