织梦伪静态规则冲突排查方法

问题背景

织梦 DedeCMS 开启伪静态后,常出现各种怪现象:列表页能打开但翻页 404、文章 URL 跳到首页、tag 页与栏目页 URL 互相覆盖、移动站伪静态被 PC 站规则抢匹配。这些问题的根因大多是 rewrite 规则之间存在冲突,规则顺序不当,或正则贪婪匹配吃掉了不该吃的 URL。

原因分析

nginx 规则按顺序匹配

nginx locationrewrite 是按声明顺序匹配的,先写的规则优先生效。若把通用的 ^/(.*)$ 写在前面,后面所有具体规则都不会被匹配。

正则贪婪匹配

(.*)(.+) 默认贪婪,会尽可能多地吃字符。例如 ^/list-([0-9]+)-([0-9]+)\.html$^/list-(.*)\.html$ 同时存在时,后者会先匹配成功。

多 location 优先级

nginx 中 location=(精确)> location ^~(前缀)> location ~(正则)。前缀与正则混用时常出现非预期匹配。

解决步骤

一、复现并定位冲突 URL

用 curl 模拟请求,看实际命中哪条规则:

curl -I https://www.example.com/news/1.html
curl -I https://www.example.com/list-1-2.html

二、开启 nginx rewrite 日志

error_log /var/log/nginx/error.log notice;
rewrite_log on;

重启 nginx 后再次请求,error.log 中会打印每条 rewrite 的执行过程,可以清楚看到哪条规则被命中、跳到了哪个 URL。

三、规则按精确度排序

原则:精确的规则在前,模糊的规则在后:

# 1. 精确页面
location=/sitemap.xml { ... }
location=/robots.txt  { ... }

# 2. 列表页带翻页
location ~ ^/list-([0-9]+)-([0-9]+)\.html$ {
    rewrite ^ /plus/list.php?tid=$1&PageNo=$2 last;
}

# 3. 列表页不带翻页
location ~ ^/list-([0-9]+)\.html$ {
    rewrite ^ /plus/list.php?tid=$1 last;
}

# 4. 文章页
location ~ ^/news/([0-9]+)\.html$ {
    rewrite ^ /plus/view.php?aid=$1 last;
}

# 5. 兜底(最后)
location / { try_files $uri $uri/ /index.php; }

四、Apache 站点的排查

Apache 的 .htaccessRewriteRule 也按顺序匹配。开启日志:

RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 3

注意 Apache 2.4+ 已废弃上述指令,需在 vhost 配置中用 RewriteRule 调试。

五、解决移动站规则冲突

PC 与移动站共用一个 nginx 配置时,要按 host 分开:

server {
    server_name www.example.com;
    # PC 规则
}
server {
    server_name m.example.com;
    # 移动规则
}

避免在一个 server 块里用 if 判断 host,性能差且易错。

配置示例汇总

排查口诀:开 rewrite_log → curl 复现 → 看命中规则 → 调整顺序 → 加注释防止再乱。规则文件维护时建议加分组注释,便于多人协作。

注意事项

  • nginx 中 rewrite 的 flag 用 last 还是 break 容易混淆:last 会重新走 location 匹配,break 不再匹配。织梦伪静态多数用 last
  • 开启 rewrite_log 会显著增加 IO 负载,调试完务必关闭。
  • .htaccess 修改后立即生效,nginx 修改后需 nginx -t && nginx -s reload
  • CDN 缓存可能让你以为规则没生效,调试时记得先清 CDN 缓存或绕过 CDN。
  • 规则中正则元字符(如 .?)需正确转义,否则匹配范围不可控。

实施建议

建议把站点所有 rewrite 规则集中维护在一份 nginx include 文件中,按"精确 → 列表 → 文章 → tag → 兜底"顺序分组并加注释。每次新增规则先用 nginx -t 验证语法,再 curl 测试几个边界 URL(带参数、翻页、特殊字符),确认无冲突后再 reload。长期看,规则越少越好,能用 try_files 解决的就不要写 rewrite,能让站点伪静态逻辑保持简洁可维护。