Discuz门户首页keywords显示「首页」3种修复方案

Discuz后台SEO配置填了但游客访问门户首页meta显示「首页」?本文从helper_seo.php取值逻辑讲清根因,给出改核心、改模板、Hook插件3种修复方案与完整验证流程。

张文保 更新 20 分钟阅读 3,186 阅读

我做Discuz站点的优化和维护已经有十几年了,从X2一路用到X3.5,期间踩过的SEO坑能写好几本书。今天要讲的这个问题特别典型:明明在后台「全局 → SEO设置」里把门户首页的标题、关键字、描述都填好了,更新缓存也做了,可是用查站工具一抓,关键字和描述居然显示成「首页」两个字。这一篇我把这个坑的来龙去脉、两种修复方案、底层原理、注意事项全部讲清楚。

问题现象的两面性

这个Bug最让人困惑的地方在于它有「两面性」。当你以管理员或普通用户身份登录后访问门户首页,查看页面源码会发现meta name="keywords"meta name="description"都是后台填写的内容,看起来一切正常。但只要你退出登录,或者用浏览器无痕窗口打开同一个网址,meta标签里的内容就变成了「首页」两个字,有些版本甚至直接为空。

搜索引擎的爬虫永远是以游客身份访问站点的。也就是说,百度蜘蛛、谷歌爬虫看到的版本就是那个有问题的版本。这意味着即使你后台SEO配置写得再漂亮,搜索引擎收录到的关键词和描述也是那个莫名其妙的「首页」。我接手过的好几个Discuz项目,刚开始做诊断时业主都说「我配置都填了啊为什么还排名不好」,结果一查源码就是这个问题。

用一段命令就能快速复现并验证。在你的服务器或本地终端执行:

# 模拟游客抓取,查看真实的 meta 输出
curl -s -A "Mozilla/5.0 (compatible; Baiduspider/2.0)" \
     https://你的域名/portal.php | grep -E 'keywords|description'

如果返回内容里keywords和description是空的或者只显示「首页」,那就坐实了这个Bug。继续往下看修复方案。

这个Bug的底层成因

要修得稳,得先搞清楚为什么会这样。我跟踪了source/class/helper/helper_seo.php这个文件几个版本的源码,发现问题出在Discuz对SEO数据的取值逻辑上。

核心逻辑大概是这样:Discuz在生成页面meta时,会调用helper_seo::get_title_page()和相关方法,先去取「当前页面专属的SEO设置」,如果没设置就取站点全局的SEO设置,再没设置就用「页面名称」兜底。门户首页对应的「页面名称」在游客上下文里就是「首页」二字。

这本来是合理的兜底逻辑,但问题在于游客状态下Discuz缓存机制对$_G['setting']['seokeywords']['portal']这一项的取值经常拿不到值,于是触发了兜底,返回了「首页」。我猜测是Discuz在做静态缓存时,把游客版本的SEO数据缓存得不完整,缓存命中时直接读了空值。这个Bug在X3.4 RC之后断断续续修复过几次,但仍有相当多老站受影响。

缓存机制的细节追踪

为了把根因定到字符级,我做过一次完整的代码追踪。Discuz的缓存系统有两层:内存缓存(Redis或Memcached)和文件缓存(data/cache/下的PHP数组文件)。SEO相关的设置缓存在data/cache/cache_setting.php这个文件里,初始化逻辑在source/function/function_cache.phpbuild_cache_setting()函数里。

这个函数会把common_setting表里所有skeyseokeywordsseodescriptionseotitle的记录读出来,序列化后写入缓存文件。问题是写入时它对portal这个键的判断依赖于一个!empty()检查——如果数据库里那条记录的value是空字符串(不是null),!empty()会返回false,导致缓存文件里干脆没有portal这一项。

等到游客访问门户首页时,helper_seo::get_keywords_page()读取$_G['setting']['seokeywords']['portal'],发现键不存在,按设计应该用全局fallback,但实际代码里这一段fallback被某个if条件跳过了,最终返回空字符串。然后页面渲染层的strlen() > 0判断也失败,最后让$navtitle(页面名称,即「首页」)顶上来填meta。

这一连串的缺陷叠加,造成了「明明后台填了SEO配置但游客访问看到首页字样」这个怪现象。

方案一:修改helper_seo.php让取值兜底正确

这是我比较推荐的改法,因为它从源头改正了取值逻辑,不依赖模板。备份完整个source/class/helper/helper_seo.php文件后,定位到get_keywords_page()方法,原始代码大致长这样:

public static function get_keywords_page($id) {
    global $_G;
    $keywords = $_G['setting']['seokeywords'][$id];
    return $keywords;
}

改成:

public static function get_keywords_page($id) {
    global $_G;
    $keywords = $_G['setting']['seokeywords'][$id];
    if (empty($keywords) && !empty($_G['setting']['sitekeywords'])) {
        $keywords = $_G['setting']['sitekeywords'];
    }
    return $keywords;
}

同样的逻辑加到get_description_page()方法上。改完后,当页面专属的SEO配置取不到值时,会自动fallback到站点全局的sitekeywordssitedescription。这样即使portal键缺失,meta也能拿到合理内容。

改完之后必须清缓存。后台「站长 → 数据 → 更新缓存」选「全部缓存」点更新;或者直接rm -rf data/cache/cache_setting.*把文件缓存删掉。

方案二:在portal模板里硬写meta

如果你不想改核心文件(怕版本升级被覆盖),可以从模板侧解决。打开template/default/portal/index.htm(如果你换了模板换成对应路径),找到顶部的!--{template common/header}-->这行,在它下面加:

!--{if !$_G['setting']['seokeywords']['portal']}-->
<meta name="keywords" content="你要的关键词1,关键词2,关键词3" />
<meta name="description" content="你要的页面描述,不要超过 160 字。" />
!--{/if}-->

注意Discuz模板里的条件判断语法是!--{if ...}-->而不是PHP的<?php if。模板编译后会被转成PHP执行。

这个方案的好处是不动核心文件,缺点是只适用于门户首页一个页面。如果你的论坛首页、其他门户页面也有同样问题,得逐个模板加。所以我一般在客户那只有门户首页有问题时用这个方案,全站性问题还是改helper_seo.php。

方案三:用Hook机制注入(推荐用于多站点统一管理)

如果你管理着多个Discuz站点,又不想每次都改核心文件,最优雅的做法是写一个插件,通过Discuz的Hook机制在合适的时机注入正确的SEO数据。

具体做法是创建一个轻量插件,在seo_extra这个钩子上挂载逻辑。插件目录结构:

source/plugin/seofix/
├── plugin.xml
├── seofix.class.php
└── discuz_plugin_seofix.xml

seofix.class.php核心代码:

class plugin_seofix {
    public function global_header() {
        global $_G;
        $is_portal_index = ($_G['mod'] == 'portal' && empty($_G['cat']) && empty($_G['aid']));
        if ($is_portal_index) {
            if (empty($_G['setting']['seokeywords']['portal'])) {
                $_G['setting']['seokeywords']['portal'] = $_G['setting']['sitekeywords'];
            }
            if (empty($_G['setting']['seodescription']['portal'])) {
                $_G['setting']['seodescription']['portal'] = $_G['setting']['sitedescription'];
            }
        }
    }
}

这个插件会在每次页面渲染前判断是否是门户首页,是的话就用全局SEO配置覆盖空值。安装插件后启用,效果立竿见影。版本升级Discuz核心也不会影响插件功能——这是Hook相对于直接改核心的最大优势。

修改后的验证步骤

不管用哪种方案,改完之后都要做完整验证:

第一步:清缓存。后台「站长 → 数据 → 更新缓存」选「全部缓存」更新;同时rm -rf data/cache/*.php清文件缓存;如果用了Memcached/Redis,flush_allFLUSHDB清掉。

第二步:游客无痕窗口验证。用Chrome无痕窗口(Ctrl+Shift+N)访问https://你的域名/portal.php,查看源码,确认meta name="keywords"meta name="description"都是你预期的内容。

第三步:模拟搜索引擎抓取。用curl带不同的User-Agent验证:

# 模拟百度蜘蛛
curl -s -A "Mozilla/5.0 (compatible; Baiduspider/2.0)" https://你的域名/portal.php | grep meta

# 模拟谷歌爬虫
curl -s -A "Mozilla/5.0 (compatible; Googlebot/2.1)" https://你的域名/portal.php | grep meta

# 模拟普通游客
curl -s -A "Mozilla/5.0 (Windows NT 10.0)" https://你的域名/portal.php | grep meta

三个UA下的输出应该都是正确的SEO内容。

第四步:百度站长工具诊断。登录百度搜索资源平台,用「URL检测 → 页面抓取诊断」工具直接抓一次门户首页,看百度看到的真实内容。这一步是最权威的——直接看搜索引擎眼里的页面。

多站点和多语言场景下的延伸问题

有些客户的Discuz站点会做多语言或多门户拆分,这时候SEO问题比单一站点更复杂。我归纳了几种实战中遇到的典型变体。

变体一:多门户共用一套Discuz但各有独立SEO

有的站点会用Discuz的「专题」(topic)功能搭建多个门户子站,每个子站希望有独立的SEO配置。原版Discuz的seokeywords设置只支持一组portal级别配置,多专题之间会冲突。解决方法是改写helper_seo的取值键,让它从专题ID维度去取:

public static function get_keywords_page($id) {
    global $_G;
    if ($_G['mod'] == 'topic' && !empty($_G['gp_specialid'])) {
        $key = 'topic_' . $_G['gp_specialid'];
        if (!empty($_G['setting']['seokeywords'][$key])) {
            return $_G['setting']['seokeywords'][$key];
        }
    }
    $keywords = $_G['setting']['seokeywords'][$id];
    if (empty($keywords)) {
        $keywords = $_G['setting']['sitekeywords'];
    }
    return $keywords;
}

对应的后台设置入口需要在admincp里手动加一段表单字段,让管理员能为每个专题填SEO配置。改造工作量稍大,但对多门户运营是必要的。

变体二:多语言版本各自独立SEO

Discuz的多语言切换(中文/英文/繁体)默认会让所有语言版本共用一套SEO配置,但各语言的关键词和描述应该是独立的。需要在helper_seo的取值逻辑里加上语言判断:

$lang = $_G['cookie']['lang'] ?: $_G['setting']['default_lang'];
$key = $id . '_' . $lang;
$keywords = $_G['setting']['seokeywords'][$key] ?: $_G['setting']['seokeywords'][$id];

这样英文用户访问门户首页时,meta会读到seokeywords['portal_en'],跟中文版本完全分开。

常见踩坑点

这个修复过程里我自己和客户踩过几次坑,列出来给你避免:

坑一:改完helper_seo.php但没重启php-fpm。如果你的服务器开了OPCache,PHP代码改动需要重启php-fpm或者opcache_reset()才能生效。改完不生效首先怀疑这个。

坑二:CDN缓存了页面。如果你用了Cloudflare、阿里云CDN、腾讯云CDN这类前端缓存,源服务器改完SEO但CDN还在返回旧版本。需要在CDN控制台手动清掉门户首页的缓存。

坑三:模板修改没生效。Discuz的模板是「先编译再缓存」机制,data/template/下会生成.tpl.php编译文件。改了原始.htm但没清编译缓存就不会生效。后台「站长 → 数据 → 更新缓存」会清掉模板编译缓存。

坑四:portal首页和论坛首页混淆。Discuz站点有两个「首页」概念:portal首页(portal.php)和论坛首页(forum.php或域名根目录)。这个Bug主要影响portal首页,论坛首页有自己的SEO逻辑分支。如果你的站点根目录显示的是论坛首页,要修对应的forum模块SEO逻辑而不是portal。

坑五:mobile模板和默认模板分别设置。Discuz的移动端模板(template/default_m)和PC默认模板是分开的,SEO配置也是分开的。如果你只改了PC模板的portal/index.htm,移动端访问还是会出现「首页」问题。两套模板都要改。

常见问题解答

这个Bug在Discuz X3.5、X3.4、X3.2等不同版本上都存在吗?

都存在但严重程度不同。X3.2是问题最严重的版本,几乎所有portal首页都会触发;X3.4在2017年的小补丁里部分修过,但还有少数环境会复现;X3.5在2022年发布时修了大部分情况,但当从老版本升级而来的站点(设置表里有遗留空值)仍会出现。我手上一个2024年从X3.4升到X3.5的站点也复现过,所以建议每个Discuz站都做一次完整验证。

我用的是Discuz Q(DZQ)会有这个问题吗?

不会。Discuz Q是腾讯主导的全新分支,重写了SEO模块,不再依赖helper_seo这套老逻辑。Discuz Q的portal功能也跟传统Discuz X的portal不一样。但Discuz Q有自己的SEO坑(比如默认禁用了部分蜘蛛抓取的JS渲染问题),那是另一个话题。

修改helper_seo.php会不会影响其他页面(论坛首页、版块页、帖子页)的SEO?

不会,反而是改善。helper_seo是所有页面共用的SEO取值入口,加了fallback逻辑后,所有取不到专属SEO配置的页面都会fallback到全局配置,对论坛首页、版块、帖子页都是正向修复。但要注意一点:如果你之前依赖「某些页面专门留空让Discuz生成自动SEO」(比如帖子标题作meta),加fallback后会被全局配置覆盖。这种依赖很少见,但改之前可以grep一下相关取值看看有没有特殊用法。

清完缓存还是显示「首页」是什么原因?

三种可能:第一,OPCache没失效,PHP代码改动还没生效——执行opcache_reset()或重启php-fpm;第二,CDN还在返回旧版本——去CDN控制台清掉对应URL;第三,浏览器自己缓存了旧版本——强制刷新(Ctrl+F5)或换无痕窗口。如果三个都试过还不行,开debug日志看helper_seo::get_keywords_page()实际拿到的值是什么。

插件方案(方案三)能在多站点共用吗?

能,且推荐。把插件打包成zip后,每个站点都装一遍即可,效果统一。我自己维护的多个站点用的就是这种方式,每次Discuz核心升级我都不需要逐个站重新打补丁,省了大量重复劳动。如果你管理10个以上Discuz站点,这种方案的运维收益会很显著。

修复后多久能让百度重新收录?

百度的更新节奏比较慢。一般做法是:第一,修完后立刻在百度站长工具提交「URL更新」请求门户首页;第二,更新一下站点sitemap里portal首页的lastmod;第三,发布几条高质量外链指向门户首页加速发现。我经验里大约2-4周百度就会重新爬取并更新meta,谷歌一般3-7天就能更新。

修复后旧的「首页」字样在百度索引里多久会消失?

百度的索引更新需要等待。即使你修复并提交了百度站长工具的URL更新请求,搜索结果里展示的旧描述(带「首页」字样)通常需要4-8周才会被替换。如果着急,可以在百度搜索资源平台用「快速收录」(针对移动端)和「URL更新提交」(针对PC端)双管齐下。谷歌侧用Search Console的「URL检查 → 请求编入索引」一般3-7天就更新。

能不能直接禁用Discuz的SEO模块自己写meta标签?

能,但不推荐。完全绕开Discuz的SEO模块意味着你要在每个模板里手写meta逻辑,论坛、版块、帖子、门户、专题各有变量来源,代码量大且容易漏。更好的方式还是修复Discuz的SEO模块本身(方案一或方案三),或者用一些专业的SEO插件(DZ应用中心搜索"SEO优化"会有第三方插件)。

分享到
标签
版权声明

本文标题:《Discuz门户首页keywords显示「首页」3种修复方案》

本文链接:https://zhangwenbao.com/discuz-portal-home-page-keyword-and-description-display-home-page-solution.html

版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交