最近我重新安装并启用了 Yoast SEO 免费版,原本只是想恢复 WordPress 文章的 SEO 标题、Meta Description、XML Sitemap 等基础功能。
一开始看起来一切都很顺利。Yoast SEO 成功安装后,系统提示可以进入首次配置向导。

但是在后续测试过程中,我发现了一个比较隐蔽的问题:
新文章可以正常打开;
旧文章中的某一篇却直接出现 WordPress 致命错误。
这篇文章记录一下完整排查过程。这个问题表面看起来像是 Yoast SEO 引起的,但最终发现真正原因并不是 Yoast,而是 WPCode 中一个自定义 PHP Snippet 的函数重复声明问题。
一、重新安装 Yoast SEO 免费版
我这次重新安装的是 Yoast SEO 免费版。
安装完成后,Yoast 会提示进入 First-time configuration,也就是首次配置向导。

进入配置向导后,第一步是 SEO 数据优化。这个过程会让 Yoast 扫描网站已有内容,并生成它自己的 SEO 数据索引。

因为我的博客文章数量比较多,所以这个步骤需要等待一会儿。
等优化完成后,页面显示:
We’ve successfully analyzed your site & optimized your SEO data!

到这里为止,Yoast SEO 本身的安装与基础初始化都没有问题。
二、异常现象:新文章正常,旧文章打不开
在启用 Yoast SEO 后,我测试了一篇新文章:
这篇文章可以正常打开。
但是另一篇 2021 年写的旧文章:
却出现了 WordPress 致命错误。
页面提示:
此站点遇到了致命错误。

这就比较容易误判。
因为现象是:
停用 Yoast SEO 后,旧文章恢复正常;
启用 Yoast SEO 后,旧文章打不开;
所以第一感觉很容易认为是 Yoast SEO 与旧文章内容结构不兼容。
但实际上,后面的日志证明,Yoast SEO 更像是“触发器”,并不是根因。
三、先确认后台编辑页是否正常
为了判断是不是文章内容本身彻底损坏,我先打开这篇旧文章的后台编辑页。
结果后台编辑页可以正常打开。

这篇旧文章使用的是 Classic Block 旧式内容结构,并且正文中有旧的 caption 图片短代码。
但后台能打开,说明文章数据本身并没有完全损坏。
问题更可能发生在前台渲染阶段。
四、查看 PHP-FPM 日志,定位真正错误
接下来通过服务器 SSH 查看 PHP-FPM 日志。
执行命令:
tail -n 80 /usr/local/php/var/log/php-fpm.log
日志中出现了关键报错:
PHP Fatal error: Cannot redeclare custom_desc_and_ads_inserter()
更完整的信息指向 WPCode 的代码执行器:
wp-content/plugins/insert-headers-and-footers/includes/class-wpcode-snippet-execute.php
也就是说,真正导致 WordPress 致命错误的,并不是 Yoast SEO 插件目录下的代码,而是 WPCode / Insert Headers and Footers 执行的某个 PHP Snippet。
这个函数名也很熟悉:
custom_desc_and_ads_inserter()
它来自我之前写的一个 PHP Snippet:
完美替代 16 条 Ad Inserter 规则的 PHP 代码
这个 Snippet 的作用,是根据文章语言、系列、分类等条件,在文章底部自动插入不同的推荐说明、联盟链接或技术服务咨询区块。
五、为什么新文章能打开,旧文章却打不开?
这个问题比较有迷惑性。
如果是 Yoast SEO 本身坏了,理论上所有文章都应该打不开。
但实际情况是:
新文章正常;
部分旧文章异常;
停用某个 WPCode Snippet 后恢复正常;
重新启用 Snippet 后再次异常。
这说明问题不是“Yoast SEO 导致所有文章崩溃”,而是某个 Snippet 在特定页面执行路径下被重复加载,最终重复声明了同一个 PHP 函数。
PHP 中函数不能重复声明。
如果同一个请求中执行了两次:
function custom_desc_and_ads_inserter() {
// ...
}
就会触发:
Cannot redeclare custom_desc_and_ads_inserter()
Yoast SEO 启用后,前台页面渲染过程发生了一些变化,可能让原本隐藏的 WPCode Snippet 加载问题暴露出来。
所以更准确的判断是:
Yoast SEO 是触发器;
WPCode Snippet 中不安全的函数声明才是根因。
六、临时解决:停用出问题的 Snippet
为了先恢复线上访问,我第一步是停用这个 Snippet。
路径是:
WordPress 后台 → Code Snippets → All Snippets → 完美替代 16 条 Ad Inserter 规则的 PHP 代码 → Deactivate
停用后,旧文章恢复正常。
这一步很重要,因为在线上环境排查问题时,优先级最高的是先恢复用户访问,而不是马上改代码。
七、第一次修复尝试:顶部加 return 保护,但不够理想
我一开始尝试在 Snippet 顶部加一段防重复加载保护:
if (
defined('SW_DESC_ADS_SNIPPET_LOADED') ||
function_exists('custom_desc_and_ads_inserter') ||
function_exists('custom_in_category_tree')
) {
return;
}
define('SW_DESC_ADS_SNIPPET_LOADED', true);
这个思路是:
如果函数已经存在,就直接 return,避免重复声明。
但实际测试后发现,旧文章虽然不再稳定报同样的错误,但文章底部的推荐/咨询区块没有正常显示。
原因也很明显:
这个 Snippet 最后是通过下面这行直接输出内容的:
echo custom_desc_and_ads_inserter();
如果顶部提前 return,后面的输出逻辑也可能被跳过。
所以这种写法不够适合当前这个 Snippet。
八、最终修复:给函数声明加 function_exists 保护
更稳妥的方式,是不要在 Snippet 顶部直接 return,而是给每个自定义函数声明加 function_exists() 保护。
原来的主函数是:
function custom_desc_and_ads_inserter() {
// 原来的函数内容
}
修改为:
if (!function_exists('custom_desc_and_ads_inserter')) {
function custom_desc_and_ads_inserter() {
// 原来的函数内容
}
}
辅助函数原来是:
function custom_in_category_tree($cat_slug) {
// 原来的函数内容
}
修改为:
if (!function_exists('custom_in_category_tree')) {
function custom_in_category_tree($cat_slug) {
// 原来的函数内容
}
}
这样处理后,即使 WPCode 在某些请求中重复加载了同一个 Snippet,也不会再次声明已经存在的函数。
同时,代码末尾的输出逻辑仍然可以保留:
echo custom_desc_and_ads_inserter();
这比顶部直接 return 更适合这个 Snippet 的结构。
九、重新启用 Snippet 并测试
修改完成后,我重新启用了这个 Snippet。
然后分别测试:
旧文章页面是否正常打开;
底部推荐/咨询区块是否正常显示;
新文章页面是否正常打开;
新文章底部区块是否正常显示。
测试结果:
旧文章正常打开;
旧文章底部区块正常显示;
新文章正常打开;
新文章底部区块正常显示;
PHP-FPM 日志中没有新的 Fatal error。

【图7:旧文章页面恢复正常】

十、完成 Yoast SEO 首次配置
确认致命错误修复后,我继续完成 Yoast SEO 的首次配置。
Yoast SEO 的首次配置主要包括以下几项:
SEO 数据优化;
现场代表;
社交个人资料;
偏好设置;
完成配置。
在“现场代表”这一步,我选择了“个人”。

社交个人资料这一步,我暂时没有补充,后续可以再完善。

偏好设置中,我没有订阅邮件,也选择不共享站点数据。

最后,Yoast SEO 显示配置完成。

十一、检查 Yoast SEO 仪表盘和 Alert center
配置完成后,我进入 Yoast SEO 仪表盘。
中间有一些 Yoast SEO Premium、Site Kit、SEO Task list 的推广提示,这些都不是必须处理的内容。
真正需要看的,是 Alert center。
Alert center 显示:
问题:0;
通知:0。

这说明 Yoast 当前没有检测到严重 SEO 配置问题。
十二、验证 XML Sitemap 和 robots.txt
安装 SEO 插件后,最重要的基础验证之一是 XML Sitemap。
打开:
https://www.shuijingwanwq.com/sitemap_index.xml
页面可以正常显示 Yoast SEO 生成的 sitemap index。

然后继续检查 robots.txt:
https://www.shuijingwanwq.com/robots.txt
内容中可以看到:
Sitemap: https://www.shuijingwanwq.com/sitemap_index.xml

这说明搜索引擎可以通过 robots.txt 找到 Yoast 生成的 XML Sitemap。
十三、最后清理缓存
完成配置和修复后,我通过 W3 Total Cache 清空了所有缓存。
路径是:
WordPress 后台顶部黑色工具栏 → 性能 → 清空所有缓存

清理缓存后,再次验证前台页面,确认旧文章、新文章、底部区块和 Yoast SEO 都正常。
十四、这次排查得到的结论
这次问题表面上看像是:
启用 Yoast SEO 后,旧文章打不开。
但最终排查下来,真正原因是:
WPCode 中的 PHP Snippet 重复声明了 custom_desc_and_ads_inserter() 函数,导致 PHP Fatal error。
Yoast SEO 只是让这个隐藏问题暴露出来,并不是根因。
比较稳妥的处理方式是:
不要急着卸载 Yoast;
先停用可疑 Snippet,确认页面是否恢复;
查看 PHP-FPM 日志,找到真正的 Fatal error;
对自定义 PHP 函数加 function_exists() 防重复保护;
重新启用 Snippet,并验证前台页面和 PHP 日志;
最后清理缓存。
十五、以后写 WPCode Snippet 需要注意什么?
这次问题也提醒我,以后在 WPCode 中写 PHP Snippet 时,最好养成几个习惯。
第一,自定义函数尽量加唯一前缀。
比如不要写太通用的函数名:
function custom_desc_and_ads_inserter() {
}
更好的方式是使用更明确的前缀:
function shuijingwan_custom_desc_and_ads_inserter() {
}
第二,函数声明前加 function_exists() 判断。
例如:
if (!function_exists('shuijingwan_custom_desc_and_ads_inserter')) {
function shuijingwan_custom_desc_and_ads_inserter() {
// ...
}
}
第三,线上排查插件冲突时,不要只看表面现象。
这次如果只看现象,很容易误以为 Yoast SEO 有问题。
但日志里的关键错误并不在 Yoast 插件目录,而是在 WPCode 的 Snippet 执行器中。
所以 WordPress 出现致命错误时,PHP 日志比后台页面提示更有价值。
结语
这次重新安装 Yoast SEO 的过程,比预期多花了一些时间。
但结果是好的:
Yoast SEO 免费版已经正常启用;
首次配置完成;
XML Sitemap 正常生成;
robots.txt 正常声明 sitemap;
旧文章打不开的问题已经修复;
WPCode Snippet 的底部推荐/咨询区块也恢复显示;
PHP-FPM 日志中没有新的 Fatal error。
对一个运行多年的 WordPress 博客来说,重新启用 SEO 插件、恢复旧数据、兼容历史文章和现有自定义代码,本来就容易遇到各种隐藏问题。
这次排查也算是一次很典型的 WordPress 插件冲突处理案例:不要只看哪个插件刚刚启用,而是要通过日志找到真正报错的代码位置。
WordPress 网站维护、性能优化与博客运营咨询
本站已持续运营超过 10 年,累计发布 1000+ 篇原创技术文章,长期实践 WordPress 网站建设、CDN / Cloudflare 配置、缓存优化、Google SEO、广告变现和多语言网站运营。
如果你的 WordPress 网站遇到访问慢、缓存异常、插件冲突、广告不显示、SEO 基础结构混乱、CDN 配置不确定等问题,可以联系我做一次远程技术排查。
适合以下用户:
✅ 个人博客站长
✅ WordPress 网站运营者
✅ 独立开发者与内容创作者
✅ SaaS 产品官网运营团队
✅ 希望优化网站速度与稳定性的站点
服务内容:
✅ WordPress 速度优化
✅ Cloudflare / CDN / 缓存配置排查
✅ 插件冲突与页面异常排查
✅ AdSense 广告显示问题排查
✅ SEO 基础结构检查
✅ 博客运营与商业化咨询
如需了解方案或交流相关问题,请直接联系我,并注明:WordPress 维护咨询。
联系方式:
Telegram:@shuijingwan
微信:13980074657
邮箱:shuijingwanwq@gmail.com


发表回复