AeroCore 首页缺少 h1 并非误报:home.php 只拼标题给 head、渲染布局组件,也不触发 wp_body_open,改模板还会被升级覆盖。最佳解法是用 mu-plugin 在输出层注入视觉隐藏但读屏可读的 h1,不改主题、不影响视觉,且已有 h1 时跳过。
站长工具提示「首页缺少 h1 标记」,很多站主第一反应是去主题里改模板。但如果你用的是 AeroCore 这类「组件化拼装」的主题,改模板既费劲又会在主题升级后被覆盖。这篇记录我在 997笔记上的完整排查过程和最终解法:不改主题,用一个 mu-plugin 在输出层补一条视觉隐藏的 h1。
一、先确认问题是真的
不要急着改。先把「首页到底有没有 h1」这件事确认清楚,别最后改了半天发现是工具误报。
最快的方法是直接抓线上 HTML 数一下:
curl -s https://你的域名.com/ | grep -o '<h1' | wc -l
我这边实测返回 0,确实是真缺失,不是工具误报。下面这行是排查用的,可以顺便记下来备用:
| 检查项 | 命令 | 实测结果 |
|---|---|---|
| 首页 h1 数量 | curl -s 域名/ | grep -o '<h1' | wc -l |
0(缺失) |
| 文章页 h1 数量 | 同上,换成文章 URL | 1(正常) |
| 主题是否调 wp_body_open | grep -rn 'wp_body_open' wp-content/themes/主题名 | wc -l |
0(关键线索) |
| home.php 里有没有标题输出 | grep -n 'the_title|<h1' home.php |
0 处 |
注意:如果你页面开了 fastcgi_cache 或 CDN,curl 拿到的可能是缓存版本。给 URL 加个随机参数(如 ?_=$(date +%s))绕过缓存再测,否则容易把「缓存里没有」误判成「模板里没有」。
二、根因:AeroCore 的 home.php 结构
打开主题的首页模板 templates/default/home.php,全文核心逻辑就这么点:
<?php
namespace AeroCore;
$description = get_bloginfo('description');
$title = get_bloginfo('name');
if ($description !== '') {
$title .= ' - ' . $description;
}
Template::renderHeader([
'title' => $title
]);
$layout_config = get_option('aerocore_layout_config', '[]');
$layout_items = json_decode($layout_config, true);
// ... 循环 renderLayoutComponent($item) 输出轮播/文章列表/侧栏
Template::renderFooter();
看明白了:这个模板做三件事——拼一个标题字符串给 renderHeader(那是 <head> 里的 <title>)、读取后台配置的布局项、循环渲染布局组件。
它从头到尾没有输出任何 <h1>。标题字符串只进了 <head>,页面上一个标题标签都没有。这不是 bug,是这类主题的设计取向:首页在它眼里是「组件拼装页」,不是「文章页」,没有天然的标题元素。
还有个更隐蔽的坑,这类主题几乎都不调 wp_body_open()。我 grep 整个主题目录,结果是 0 次。这意味着坊间最常见的「挂 wp_body_open 钩子注入 h1」方案,对这个主题完全无效——钩子根本没人触发。这也是很多人试了网上教程还是没效果的原因。
三、为什么不该直接改 home.php
先说清楚我为什么不推荐直接编辑主题文件:
| 做法 | 当下 | 主题升级后 |
|---|---|---|
| 改主题 home.php | 生效 | 被覆盖,h1 丢失,得重新改 |
| 建子主题覆盖 | 生效 | 不丢,但要维护整套模板 |
| 写 mu-plugin | 生效 | 不丢,主题管不到它 |
mu-plugin 是 WordPress 官方机制:wp-content/mu-plugins/ 下的文件只会自动加载、无法在后台禁用、也不会被主题更新覆盖。对一个「补一个缺失标签」这种小改动,这是最合适的落点。
四、思路:不改 HTML 结构,在输出层补
核心思路是:不碰主题的任何文件,在页面即将输出时拦截 HTML,在 <body> 后插入一条 h1。
这里有个关键取舍——插入的 h1 是视觉隐藏的,页面上看不见。这不是偷懒,而是正确做法,原因有三:
- 首页视觉不能变。首页是组件拼装的,硬塞一个可见的大标题会破坏布局;截图对比会看出明显差异。
- 标题已通过 <title> 传达。访客和搜索引擎都已经从
<head>的标题拿到站点名了,视觉上重复反而是噪音。 - 读屏软件能读到。视觉隐藏 ≠ 对辅助技术隐藏。视障用户访问首页时会得到站点名,这正是 h1 的语义作用。
关键在于「视觉隐藏」的正确写法。这里有个很容易踩的坑:
千万别用 display:none 或 visibility:hidden。这两种方式元素会从无障碍树里彻底移除,读屏软件也读不到——那等于白加,h1 依然「不存在」。
必须用「视觉隐藏但保留在无障碍树」的方案,完整写法在下面的代码里。
五、完整代码
新建文件 wp-content/mu-plugins/n997-home-h1.php,内容如下:
<?php
/**
* Plugin Name: 首页 h1 补全
* Description: AeroCore 主题的 home.php 只输出布局组件,首页没有任何 h1,
* 站长工具会报「缺少 h1 标记」。本插件用输出层改写,
* 在首页 <body> 后注入一条屏幕阅读器专用的 h1。
* Version: 1.1
*/
if (!defined('ABSPATH')) {
exit;
}
final class N997_Home_H1
{
/** 视觉隐藏但读屏软件可读的 h1 样式 */
const CSS = '.n997-sr-h1{position:absolute;width:1px;height:1px;padding:0;margin:-1px;'
. 'overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);'
. 'white-space:nowrap;border:0;}';
public static function init()
{
add_action('template_redirect', array(__CLASS__, 'startBuffer'), 0);
}
public static function startBuffer()
{
if (is_admin() || (defined('DOING_AJAX') && DOING_AJAX)
|| (defined('REST_REQUEST') && REST_REQUEST) || is_feed()) {
return;
}
if (!is_front_page()) {
return;
}
if (self::frontPageHasH1()) {
return;
}
ob_start(array(__CLASS__, 'inject'));
}
/** 静态首页且正文非空时,主题会自己渲染 h1,不要重复加 */
private static function frontPageHasH1()
{
if (is_page() && is_front_page()) {
$p = get_queried_object();
if ($p instanceof WP_Post && '' !== trim((string) $p->post_content)) {
return true;
}
}
return false;
}
public static function inject($html)
{
if ($html === '' || $html === null) {
return $html;
}
// 双保险:输出层再查一次,已有 h1 就跳过
if (preg_match('#<h1b#i', $html)) {
return $html;
}
$name = trim((string) get_bloginfo('name'));
if ($name === '') {
return $html;
}
$desc = trim((string) get_bloginfo('description'));
$text = ($desc !== '') ? $name . '|' . $desc : $name;
$text = trim(wp_strip_all_tags(
html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8')
));
$h1 = '<h1 class="n997-sr-h1">' . esc_html($text) . "</h1>n";
$style = '<style>' . self::CSS . "</style>n";
// 样式必须在 h1 之前落,否则 h1 会占位显示 1px 外的元素
if (stripos($html, '</head>') !== false) {
$html = preg_replace('#</head>#i', $style . '</head>', $html, 1);
} else {
$html = $style . $html;
}
// 插到 <body...> 之后,只插第一个
$n = 0;
$html = preg_replace_callback('#(<bodyb[^>]*>)#i',
function ($m) use ($h1, &$n) {
if ($n > 0) {
return $m[1];
}
$n++;
return $m[1] . "n" . $h1;
}, $html, 1);
return $html;
}
}
N997_Home_H1::init();
上传后刷新首页,h1 数量就变成 1 了。
六、为什么用 template_redirect 而不是 wp_body_open
这点值得单独说,因为它是整个方案里最容易踩错的地方。
网上最常见的写法是:
add_action('wp_body_open', function () {
echo '<h1>xxx</h1>';
});
这段代码在标准主题(如 Twenty 系列)上是有效的。但我 grep 过 AeroCore 的整个主题目录,wp_body_open 出现 0 次——钩子从没被触发过,代码挂上去等于没写。
排查这类问题的通用技巧:当某个钩子「怎么都不生效」时,先 grep -rn '钩子名' 主题目录 确认主题到底调没调它,而不是反复怀疑自己的代码。钩子不存在,代码写得再对也是空跑。
所以方案改成 template_redirect 钩子 + ob_start 输出缓冲回调。这个位置在模板加载前触发,一定能拦住。
几个细节值得说明:
- 优先级写 0:确保尽可能早地启动缓冲,早于其他可能输出内容的逻辑。
- 排除后台/AJAX/REST/Feed:这四种场景用
ob_start会把 JSON、RSS 响应也缓冲掉,可能导致接口异常。 - 只对首页生效:
is_front_page()判断,文章页、留言页一概不管。 - 双重防重复:静态首页如果本身有正文(主题会渲染 h1),和方法里的兜底正则都能挡住,绝不会出现两个 h1。
七、验证:一定要做视觉回归
改完别急着收工,必须验证两件事:h1 有了,且页面没被改坏。
如果本机没装 PHP 环境,别在本地折腾语法检查,直接把文件传到服务器上跑:
# 语法检查
php -l wp-content/mu-plugins/n997-home-h1.php
| 页面 | 修复前 h1 | 修复后 h1 | 结论 |
|---|---|---|---|
首页 / |
0 | 1 | 已修复 |
留言板 /message/ |
1 | 1 | 未误伤 |
友情链接 /friend-links/ |
1 | 1 | 未误伤 |
站点地图 /sitemap/ |
1 | 1 | 未误伤 |
| 文章页 | 1 | 1 | 未误伤 |
「未误伤」这列才是重点。如果你的改动让文章页冒出 2 个 h1,那就是引入了新问题。
视觉验证要靠截图对比。命令行 curl 只能验证标签数量,验证不了「有没有多出一个奇怪的小黑块」。本地没装 Selenium 的话,用无头 Chrome 截图最省事:
chrome --headless=new --dump-dom "https://你的域名.com/" > page.html
grep -c '<h1' page.html
grep -o 'n997-sr-h1' page.html
我这边对比了修改前后的首页截图,像素级无任何可见变化,这才是合格的标准——SEO 标签的补充不应该让用户看到任何东西。
八、几个延伸问题
1. h1 内容该写什么?
我用的是「站点名|站点副标题」,比如「997笔记|大番茄 · 软路由 OpenWrt、Linux 命令、网络运维折腾笔记」。这样 h1 同时承载了品牌和页面主题,比单纯写站点名更利于理解页面内容。
注意:如果你给文章页配了自定义 SEO 标题,首页的 h1 建议别跟着改,保持「站点名 + 副标题」这个稳定的语义。
2. 会影响 SEO 吗?
正面影响。h1 是页面主题最核心的语义信号,缺失会导致搜索引擎对页面主题的判断依据变弱。补上之后,站长工具的「缺少 h1」告警消除,也补齐了无障碍访问的语义结构。
3. 主题以后更新了会怎样?
mu-plugin 不受影响。但有一种情况要注意:如果主题作者将来把 h1 补上了,你的插件会检测到并自动跳过(方法里的 preg_match('#<h1b#i', $html) 兜底),不会变成两个 h1。这就是那层双重防重复设计的意义。
4. 为什么不用子主题?
子主题也能解决,但代价是要复制一套模板文件。而这里的改动只是「加一个隐藏标签」,用 mu-plugin 几行代码搞定,维护成本低得多。改主题要改的量,用插件能小就小,就别用子主题。
九、一句话总结
组件化主题的首页没有 h1 是设计使然,不是 bug。修复的正确姿势是:先 grep 确认钩子是否存在 → 用 mu-plugin 承载改动 → template_redirect + ob_start 在输出层注入 → 视觉隐藏而非 display:none → 截图做视觉回归 → 验证其他页面没被误伤。
整套改动不到 90 行,不动主题任何文件,主题升级也不会失效。整站每个页面各留一个 h1,是最干净的 SEO 结构。
- 文章名称:WordPress 首页没有 H1 标记怎么修:AeroCore 主题实测修复全过程
- 文章永久网址:https://www.997wan.com/wordpress-homepage-missing-h1-aerocore-fix/
- 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长 997笔记 进行删除处理。
- 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
- 本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
- 本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
- 如无特别声明本文即为原创文章仅代表个人观点,版权归《997笔记》所有,欢迎转载,转载请保留原文链接。