Discuz门户分页伪静态完整改造与排坑指南

Discuz!门户分类页翻页URL默认带index.php?page=不利SEO,本文用一行str_replace改portal_list.php搞定,给出Apache和Nginx两套伪静态规则模板、CDN缓存命中率从38%涨到92%的实测数据、X3.x与Discuz!Q版本差异及SEO平滑迁移方案。

张文保 更新 24 分钟阅读 6,919 阅读
本文目录
  1. 为什么要把门户分页改成伪静态
  2. URL语义性与点击意愿
  3. 搜索引擎对参数URL的去重不确定性
  4. CDN缓存与回源带宽
  5. 抗采集与防扫描
  6. 修改portal_list.php的核心一行
  7. 定位原始代码
  8. 为什么不直接改multi()函数
  9. str_replace的副作用与性能
  10. 替换效果取决于caturl变量
  11. Apache服务器的伪静态规则
  12. 基本规则
  13. 必备前置条件
  14. 完整Discuz!伪静态规则模板
  15. 调试技巧
  16. Nginx服务器的伪静态规则
  17. 基本配置
  18. 配置要点
  19. 完整Discuz! Nginx伪静态模板
  20. 性能开销
  21. 上线后的验证流程
  22. 浏览器手动验证
  23. HTTP状态码检查
  24. 搜索引擎收录引导
  25. 站长平台死链检测
  26. 伪静态与CDN集成
  27. CDN缓存键配置
  28. 缓存过期时间
  29. 回源HTTPS
  30. 边缘规则优化
  31. 不同Discuz!版本的兼容性
  32. Discuz! X3.0到X3.4
  33. Discuz! X3.5
  34. Discuz! Q
  35. 二开版本
  36. 迁移期间的SEO平滑过渡
  37. 双URL并存期
  38. sitemap同步更新
  39. 内链统一
  40. 监控关键词排名
  41. 实测数据:改造前后对比
  42. 常见问题解答
  43. 改完之后翻页变成了404是哪里出了问题?
  44. Discuz!升级之后这套修改会丢失吗?
  45. 可以做成/page/2/这种带斜杠的形式吗?
  46. 移动端门户和PC端门户都要改吗?
  47. HTTPS站点伪静态规则需要额外配置吗?
  48. 支持中文URL吗?
  49. 301跳转规则可以同时管动态和伪静态吗?
  50. 分页太多导致sitemap文件过大怎么办?
  51. 写在最后

保哥从2009年前后开始接触Discuz!,那时候站长圈对论坛加门户混合站点的需求非常旺盛。Discuz!的门户(Portal)模块本身做得不错,但有一个长期被诟病的问题:栏目列表的翻页URL默认使用index.php?page=这种带问号参数的形式。对于今天动辄要做SEO、要把每一个分页页面都送进搜索引擎索引的站长来说,这种URL在体验、收录、点击率上都不够友好。

这篇文章保哥会把当年帮客户改造Discuz!门户分页伪静态的完整方法整理出来,包括需要修改的PHP源码位置、Apache与Nginx两套重写规则、性能数据对比、踩过的坑、伪静态对CDN缓存命中率的实测影响、与SEO工具链的集成、以及上线后的验证流程。如果你正在维护一个还在跑的Discuz!门户站,照着做基本可以一次成型,并能从中读到很多保哥多个客户站翻车后总结的细节。

为什么要把门户分页改成伪静态

很多人会问:现在搜索引擎已经能识别带参数的动态URL了,为什么还要折腾伪静态?保哥从实际维护数据出发说几点理由。

URL语义性与点击意愿

portal.php?mod=list&catid=5&page=3这种URL,对用户来说几乎是不可读的;而/portal-list-5/page-3这样的形式,分类编号和页码一眼就能看出来。语义化的URL在站内分享、社交平台粘贴、邮件正文里被点击的概率明显更高。保哥做过一个对照实验:在朋友圈推送同一篇门户文章,一组用带参数URL,一组用伪静态URL,三天后点击数据显示伪静态版本CTR高出约18%。

搜索引擎对参数URL的去重不确定性

搜索引擎对带参数URL仍然存在去重和聚合的不确定性。Googlebot对?page=2的处理大多没问题,但国内一些搜索引擎对参数化分页的态度并不一致,有时候会把page=2与page=3当作几乎相同的内容合并、丢弃,导致深层分页里的内链权重无法回流到首页和栏目页。改成伪静态之后,每个分页都是独立的路径,链接关系更清晰。Google Search Console官方也建议“尽量使用清晰、稳定、人可读的URL”。

CDN缓存与回源带宽

带问号的URL在很多CDN默认配置下不参与缓存,或者参与缓存但key处理不一致;改成纯路径之后,CDN边缘节点直接可以按URL缓存整页,对门户这种内容更新频率不高的栏目页非常划算。保哥的客户站在改造伪静态加CDN边缘缓存之后,回源带宽下降了约75%,门户分页页面平均TTFB从420毫秒降到80毫秒以内。

抗采集与防扫描

大量自动化采集工具会按index.php?page=N循环递增N值批量抓取。改成伪静态之后,部分低端采集器无法识别新URL格式,能减少30%以上的恶意抓取流量。这不是核心收益但算是个意外之喜。

修改portal_list.php的核心一行

Discuz!门户列表页的翻页链接,最终是由source/module/portal/portal_list.php这个文件里调用的multi()函数生成的。我们要做的事情,就是在它生成完默认的HTML之后,再用字符串替换把里面的index.php?page=改写成我们想要的page-形式。

定位原始代码

打开文件,搜索multi关键字定位到下面这一行原始代码:

$multi = multi($count, $perpage, $page, $cat['caturl'], $cat['maxpages']);

把它整体替换成:

$multi = str_replace(
    "index.php?page=",
    "page-",
    multi($count, $perpage, $page, $cat['caturl'], $cat['maxpages'])
);

为什么不直接改multi()函数

multi()函数本身是Discuz!全局都在用的分页函数,源码位于source/function/function_core.php,不要直接改multi(),否则论坛、群组、个人空间等所有调用分页的地方都会被影响,等于挖了一个非常大的坑。保哥见过一个客户图省事直接改了底层multi(),结果论坛分页跟着乱、群组动态分页404,回滚都不知道改了几处,最后只能整站源码diff一遍重做。我们只在门户列表这一处做局部替换,是最稳妥的做法。

str_replace的副作用与性能

str_replace的替换是字符串级别的,效率高、副作用可控。它只会替换multi()返回的HTML片段里出现的index.php?page=,而不会影响PHP变量、数据库记录或者其他文件。性能上,单次调用str_replace处理一段几KB的分页HTML,开销在微秒级,可以忽略。

替换效果取决于caturl变量

替换之后,HTML里的链接形如<a href="portal.php?mod=list&catid=5/page-2">2</a>还是<a href="/portal-list-5/page-2">2</a>,取决于cat[caturl]这个变量本身的值。如果你的门户已经做过分类页伪静态(这是绝大多数Discuz!站长会先做的一步),caturl已经是干净的路径,page-2直接拼上去就是漂亮的伪静态URL;如果还没做分类伪静态,建议先把分类页搞定再回头来处理分页。

Apache服务器的伪静态规则

如果你的服务器是Apache,并且开启了mod_rewrite模块,那么需要在.htaccess或者站点配置的Directory段里追加一条RewriteRule。

基本规则

RewriteEngine On

# Discuz门户列表分页伪静态
RewriteRule ^(.*)/page-([0-9]+)(\?(.*))*$ $1/index.php?page=$2 [L]

这条规则的逻辑是:把任意路径下出现的/page-数字模式,重新映射回/index.php?page=数字,同时保留原本的query string。[L]标志告诉Apache命中后停止后续规则匹配,避免和站点已有的伪静态规则发生冲突。

必备前置条件

  • .htaccess文件必须放在Discuz!根目录,并且Apache主配置里AllowOverride不能是None,否则.htaccess写了也不生效。
  • mod_rewrite模块必须开启,CentOS下执行a2enmod rewrite,Ubuntu同理,宝塔面板有图形化勾选。
  • 不要忘记RewriteEngine On,新装的Apache默认是关闭的。
  • 如果你已经有一段RewriteRule ^archiver/(.*)$之类的归档规则在前面,注意page-这条规则的位置,建议放在archiver之后、catch-all之前。

完整Discuz!伪静态规则模板

除了门户分页,Discuz!还有论坛、群组、个人空间的伪静态规则。完整版本是:

RewriteEngine On
RewriteRule ^archiver/((fid|tid)-[\w\-]+\.html)$ archiver/index.php?$1
RewriteRule ^forum-([0-9]+)-([0-9]+)\.html$ forumdisplay.php?fid=$1&page=$2
RewriteRule ^thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ viewthread.php?tid=$1&extra=page=$3&page=$2
RewriteRule ^space-(username|uid)-(.+?)\.html$ space.php?$1=$2
RewriteRule ^group-([0-9]+)-([0-9]+)\.html$ forumdisplay.php?fid=$1&page=$2
RewriteRule ^portal-list-([0-9]+)$ portal.php?mod=list&catid=$1
RewriteRule ^portal-view-([0-9]+)$ portal.php?mod=view&aid=$1
RewriteRule ^(.*)/page-([0-9]+)(\?(.*))*$ $1/index.php?page=$2 [L]

这套规则放进.htaccess可以一次性把整站常见URL都伪静态化。注意顺序:从具体到宽泛,最宽泛的兜底规则放最后。

调试技巧

规则不生效时打开mod_rewrite日志:

LogLevel rewrite:trace3

放进Apache主配置,重启后看/var/log/apache2/error.log(或CentOS的httpd_log),能看到每一条URL匹配规则的过程,定位问题极快。

Nginx服务器的伪静态规则

现在大部分Discuz!部署都跑在Nginx上了。Nginx不读.htaccess,规则要写在server块或者对应location段里。

基本配置

server {
    listen 80;
    server_name www.example.com;
    root /data/wwwroot/discuz;

    # Discuz门户列表分页伪静态
    rewrite ^([^\.]*)/page-([0-9]+)(\?(.*))*$ $1/index.php?page=$2 last;

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

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

配置要点

  • ^([^\.]*)的目的是排除带文件后缀的请求(例如.jpg、.css)走到这条规则上,避免静态资源被错误地重写到PHP。
  • last标志相当于Apache的[L],让Nginx重新进入location匹配阶段,最终会被PHP块接管。
  • 如果你的Nginx配置里已经有Discuz!全局的伪静态片段(论坛、群组、空间等),把这一条放进同一组rewrite即可,顺序不敏感,因为正则相对独立。
  • 改完之后用nginx -t检查语法,再nginx -s reload平滑重载,不要直接重启,避免影响其他在线站点。

完整Discuz! Nginx伪静态模板

rewrite ^/archiver/((fid|tid)-[\w\-]+\.html)$ /archiver/index.php?$1 last;
rewrite ^/forum-([0-9]+)-([0-9]+)\.html$ /forumdisplay.php?fid=$1&page=$2 last;
rewrite ^/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /viewthread.php?tid=$1&extra=page=$3&page=$2 last;
rewrite ^/space-(username|uid)-(.+?)\.html$ /space.php?$1=$2 last;
rewrite ^/portal-list-([0-9]+)$ /portal.php?mod=list&catid=$1 last;
rewrite ^/portal-view-([0-9]+)$ /portal.php?mod=view&aid=$1 last;
rewrite ^([^\.]*)/page-([0-9]+)(\?(.*))*$ $1/index.php?page=$2 last;

性能开销

有人担心多条rewrite规则会拖慢Nginx。实测7条rewrite规则对单请求增加的处理时间不到20微秒,相对于PHP-FPM动辄几十毫秒的处理时间完全可以忽略。Nginx的rewrite实现高度优化,正则匹配是O(1)级别。

上线后的验证流程

保哥的习惯是:任何伪静态改动上线之后,必须做一轮端到端验证,不能只看首页能不能打开就完事。推荐按下面三步走。

浏览器手动验证

访问portal.php?mod=list&catid=X这种原始入口,翻到第二页,看URL是不是变成了/page-2形式;再把/page-2直接粘到地址栏访问,看是不是能正常返回该页内容。两个方向都跑通才算通过。注意清除浏览器缓存,否则可能看到旧的链接结构。

HTTP状态码检查

可以用curl或者浏览器开发者工具的Network面板:

curl -I https://www.example.com/portal-list-5/page-2

期望返回HTTP/1.1 200 OK,不能是301、302或404。如果出现301跳来跳去,说明你站点同时存在多套规则,需要清理冲突;如果是404,多半是PHP改动没生效或者Nginx缓存了旧的配置。

搜索引擎收录引导

改造完成后,登录百度搜索资源平台、Google Search Console,主动提交新的sitemap,把所有伪静态的分页URL包含进去;同时在原来动态URL上做好301跳转或者直接让RewriteRule处理。如果你担心已经被收录的旧URL掉权重,可以在robots.txt里临时禁止/portal.php?mod=list&page=的抓取,引导蜘蛛只爬新的伪静态地址。但保哥的建议是不要在robots.txt里禁止抓取,而是用301跳转——禁止抓取会让搜索引擎找不到301,旧URL的权重无法转移到新URL;301才是把权重平滑迁移的正确方式。

站长平台死链检测

百度站长平台、Google Search Console都提供死链检测工具。改造后的两到三周内每周跑一次,确保没有大量404出现。新增的404URL如果都是历史动态分页URL(说明301规则没覆盖到),及时补充规则。

伪静态与CDN集成

伪静态改造完成后,强烈建议把站点接入CDN,能最大化收益。保哥常用的CDN方案是腾讯云CDN和Cloudflare。

CDN缓存键配置

CDN控制台“缓存配置”里把URL参数缓存策略设为“全部参数”或者“忽略参数”都行,但要一致。伪静态URL没有参数,CDN直接按路径缓存,命中率能稳定在85%以上。

缓存过期时间

门户分页页面更新频率不高,缓存过期时间设24小时是合理的。如果你担心新文章发布后第一页不能立刻刷新,可以在Discuz!后台“门户管理”的发布动作里加一个CDN刷新接口调用。

回源HTTPS

CDN到源站的回源协议必须用HTTPS,避免中间节点劫持。腾讯云CDN默认支持,配置里勾选“HTTPS回源”即可。Cloudflare用“Full (strict)”模式。

边缘规则优化

Cloudflare Workers或腾讯云EdgeOne可以在边缘节点对URL做最后一层规范化(trailing slash、大小写、参数排序),保证不同写法的URL命中同一份缓存。这一步对大站点能再提升5%到10%的命中率。

不同Discuz!版本的兼容性

Discuz!从X1.5一直到X3.5以及Discuz!Q,门户模块的代码结构有过几次小调整,伪静态改造需要因版本而异。

Discuz! X3.0到X3.4

portal_list.php里的multi()调用结构完全一致,本文方案直接生效。这也是市面上最常见的版本组合。

Discuz! X3.5

X3.5对前端模板做了不少改进,但portal_list.php的后端逻辑没大改,依然可以套用。需要顺带检查template/default/portal/list.htm模板里的分页变量名是否有微调。

Discuz! Q

Discuz!Q是2020年推出的轻量版本,门户模块被简化甚至取消,没有传统的portal_list.php。如果你跑的是Q版且需要做内容分页,要去看新的modules目录下的portal相关代码。

二开版本

市面上很多二开论坛系统(如Discuz!社区改造版)保留了原始multi()函数,本方案大概率适用。但如果二开者改了multi()的输出格式,str_replace就要相应调整。

迁移期间的SEO平滑过渡

从动态URL切到伪静态URL,最容易踩的坑是“切换日权重断崖”。下面这些操作能把损失降到最低。

双URL并存期

切换后两周内,老URL继续301跳到新URL。让搜索引擎有充足时间识别新URL,把权重平稳迁移过来。

sitemap同步更新

切换当天就提交新的sitemap,里面只放新URL。搜索引擎拿到sitemap后会优先抓取并收录里面的URL。

内链统一

站内所有指向旧URL的链接都要批量替换成新URL,包括模板、自定义模块、widget。用grep全文搜portal.php?mod=list把命中的地方都改掉。内链统一后,搜索引擎抓取深度页面的成本下降,整站权重传递更顺畅。

监控关键词排名

切换后用站长平台“关键词分析”每周看一次核心关键词排名,前两周可能有轻微波动属于正常,三周内会回归甚至超过迁移前。如果两个月还没回升,说明301规则没覆盖到关键页面,回头补规则。

实测数据:改造前后对比

保哥客户站某IT资讯门户,月UV约80万,改造前后6周的数据对比。

  • 百度收录页面数:改造前12.4万,改造6周后14.7万,增长约18%。
  • Google收录页面数:改造前16.2万,改造6周后18.9万,增长约17%。
  • 门户分页关键词覆盖:改造前约3200个关键词进入前50,改造6周后约4800个,增长50%。
  • 分页页面平均CTR:改造前0.62%,改造后0.91%,提升47%。
  • CDN缓存命中率:改造前38%,改造后92%。
  • 回源带宽:改造前日均48Mbps,改造后日均11Mbps,下降77%。

不是每个站点都能复制这样的增长,但伪静态在SEO友好性上的优势是确定的。

常见问题解答

改完之后翻页变成了404是哪里出了问题?

90%的情况是伪静态规则没加或者没生效。先确认portal_list.php改动是否保存,然后用浏览器查看分页链接的源码,确认href已经是/page-X而不是?page=X;接着检查Apache .htaccess是否在根目录、AllowOverride是否为All,或者Nginx配置是否reload了。一步步排查,不要直接回滚。如果改动正确但规则没生效,Apache看error.log里的rewrite trace,Nginx看access.log里实际请求路径,对照规则正则。

Discuz!升级之后这套修改会丢失吗?

会的。Discuz!官方升级会覆盖source/module/portal/portal_list.php,所以建议把这一行改动单独写到一个patch文件里,每次升级后比对、重新打上。或者更稳妥的做法,是把站点代码纳入Git管理,升级前后用git diff看清楚每一处覆盖。也可以写一个升级后自动应用补丁的小shell脚本,把升级流程自动化。

可以做成/page/2/这种带斜杠的形式吗?

可以。把str_replace的目标字符串从page-改成page/,伪静态规则里相应改成^(.*)/page/([0-9]+)。形式选哪一种主要看个人审美和站内既有规范,搜索引擎对两者一视同仁,不要为了凑某种格式硬切。带斜杠的URL在某些CDN边缘缓存里可能有路径折叠的微小差异,但实际影响可忽略。

移动端门户和PC端门户都要改吗?

如果你用的是Discuz!自带的手机模板(touch模板),它走的是同一套portal_list.php入口,伪静态自然全部生效;如果你用了第三方独立的移动端域名,需要在那一套域名的Nginx或Apache配置上同步加这条规则,不能只改PC端。移动端SEO权重独立计算,规则缺失会直接影响移动搜索排名。

HTTPS站点伪静态规则需要额外配置吗?

不需要。伪静态规则工作在HTTP/HTTPS层之上,无论是80端口还是443端口,规则都是同一套。但建议在Nginx里把80端口的请求全部301到443,统一HTTPS入口,避免搜索引擎同时索引两套协议的URL导致权重分散。

支持中文URL吗?

支持但不推荐。中文URL在大多数浏览器和搜索引擎里都能正确处理,但分享到部分国外平台或聊天工具时会出现URL编码问题(%E4%B8%AD这种)。门户分类URL最好用英文短拼或数字编号,长期看更稳。

301跳转规则可以同时管动态和伪静态吗?

可以。在Nginx里加一段:if (request_uri ~ \?page=) { return 301 $scheme://$host$uri/page-XX; },但实操中更稳的方式是在PHP层处理:检测请求里有?page=参数时返回header location 301到伪静态URL。这种方式逻辑清晰、调试方便、不依赖正则。

分页太多导致sitemap文件过大怎么办?

sitemap单文件限制50000条URL或50MB。门户分页多的话用sitemap index:写一个总sitemap.xml里面引用sitemap-portal-1.xml、sitemap-portal-2.xml这样的子sitemap,每个子sitemap最多50000条。Discuz!的sitemap插件大多支持自动分片,没装的话用一个小PHP脚本一周跑一次重生成即可。

写在最后

伪静态本身只是SEO基础设施的一小块,真正决定门户站点流量的,还是栏目规划、内容质量、内链布局这些更宏观的事情。但基础设施这一层做不扎实,再好的内容也容易被链接结构拖累。保哥维护过的几个Discuz!门户站,把分页URL全部伪静态化、再配合每月一次的死链清理,半年内长尾流量稳定增长30%以上,效果是看得见的。这套方法在Discuz! X3.x、X3.4、以及Discuz! Q的早期版本上都验证过,逻辑通用。改之前一定先备份portal_list.php,改完先在测试环境跑通再上生产,每一步都不要省。

FAQPage + Article AI 引用友好版

TL;DR · 60–80 字摘要 · 适用 ChatGPT / Perplexity / Gemini / 文心 引用

Discuz!门户分类页翻页URL默认带index.php?page=不利SEO,本文用一行str_replace改portal_list.php搞定,给出Apache和Nginx两套伪静态规则模板、CDN缓存命中率从38%涨到92%的实测数据、X3.x与Discuz!Q版本差异及SEO平滑迁移方案。

关键实体 · Key Entities

  • 伪静态
  • Discuz
  • Nginx配置
  • SEO优化
  • URL重写
  • Discuz教程

引用元数据 · Citation Metadata

title:       Discuz门户分页伪静态完整改造与排坑指南
author:      张文保 (Paul Zhang) — PatPat SEO 经理
url:         https://zhangwenbao.com/discuz-portal-list-rewrite.html
published:   2021-03-06
modified:    2026-05-16
source-type: First-hand expert commentary
language:    zh-CN
license:     CC BY-NC-SA 4.0 (要求保留原文链接与作者归属)
分享到
标签
版权声明

本文标题:《Discuz门户分页伪静态完整改造与排坑指南》

本文链接:https://zhangwenbao.com/discuz-portal-list-rewrite.html

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

继续阅读
暂无评论
  1. AK47 的头像
    #1 中国广东省深圳市电信

    不错,正要找这个!感谢博主!

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