伪静态 URL 重写

伪静态 URL 重写

Discuz、织梦等存量 CMS 的伪静态规则:语义对齐 Nginx rewrite,现有规则可一对一机械转换。

PHP 站点内置一套轻量 URL 重写模块,用来支撑 Discuz、织梦(DedeCMS)等存量 CMS 的伪静态。规则语义对齐 Nginx rewrite,现有的 Nginx 伪静态规则可以一对一机械转换。前端控制器型的 WordPress / ThinkPHP 由 try_files 自动覆盖,不需要写重写规则。

#配置格式

sites:
  - host: "bbs.example.com"
    fastcgi:
      address: "unix:/run/php/php-fpm.sock"
      root: "/var/www/discuz"
      index: "index.php"
      rewrite:
        - pattern: "^/thread-(\\d+)-(\\d+)-(\\d+)\\.html$"
          replacement: "/forum.php?mod=viewthread&tid=$1&extra=page%3D$3&page=$2"
          type: "internal"        # 可省略,默认 internal
字段 / 控件默认 / 示例说明
pattern(必填)RE2 正则(Go 标准库),匹配 URL 路径(不含 query)。线性时间、无回溯灾难,没有 ReDoS 风险。
replacement(必填)目标路径 + 可选 ?query$1 / $2 引用捕获组,后面紧跟数字或字母时写 ${1}
typeinternalinternal(≈ Nginx last,改写后重新走路由)| redirect(302)| permanent(301)。

#与 Nginx 的语义对照

行为Nginx墨守
内部重写、重新匹配rewrite ... last;type: internal(默认)
外部跳转rewrite ... redirect; / permanent;type: redirect / permanent
捕获组引用$1 $2$1 $2(歧义时 ${1}
query 保留replacement 无 ? → 原 query 保留
query 追加replacement 带 ?args → 原 query 追加其后
query 丢弃replacement 以 ? 结尾 → 丢弃原 query
REQUEST_URI原始 URL原始 URL(PHP 的 $_SERVER["REQUEST_URI"] 仍是美化后的地址)
QUERY_STRING重写后重写后(PHP 的 $_GET 来自此)
检测层看到的 URL——始终是原始 URL:重写在检测之后执行,不影响语义引擎的输入
循环保护:内部重写最多 10 轮,成环即停,绝不挂死;单站规则数上限 64 条。

#Apache flag 对照

Apache墨守
[L]type: internal(默认)
[R=301]type: permanent
[R] / [R=302]type: redirect
[QSA]replacement 带 ? 即自动追加原 query(默认行为)

#转换示例

原 Nginx 规则
location / {
    rewrite ^/thread-(\d+)-(\d+)-(\d+)\.html$ /forum.php?mod=viewthread&tid=$1&extra=page%3D$3&page=$2 last;
    rewrite ^/forum-(\w+)-(\d+)\.html$ /forum.php?mod=forumdisplay&fid=$1&page=$2 last;
}
转换后的墨守规则
rewrite:
  - {pattern: "^/thread-(\\d+)-(\\d+)-(\\d+)\\.html$", replacement: "/forum.php?mod=viewthread&tid=$1&extra=page%3D$3&page=$2"}
  - {pattern: "^/forum-(\\w+)-(\\d+)\\.html$",         replacement: "/forum.php?mod=forumdisplay&fid=$1&page=$2"}

#Discuz X 伪静态整套规则

rewrite:
  - {pattern: "^/archiver/(?:tid|fid)-(\\d+)\\.html$",   replacement: "/archiver/index.php?action=$1"}
  - {pattern: "^/forum-(\\w+)-(\\d+)\\.html$",          replacement: "/forum.php?mod=forumdisplay&fid=$1&page=$2"}
  - {pattern: "^/thread-(\\d+)-(\\d+)-(\\d+)\\.html$", replacement: "/forum.php?mod=viewthread&tid=$1&extra=page%3D$3&page=$2"}
  - {pattern: "^/group-(\\d+)-(\\d+)\\.html$",          replacement: "/forum.php?mod=group&fid=$1&page=$2"}
  - {pattern: "^/space-(?:uid|username)-(.+?)\\.html$",   replacement: "/home.php?mod=space&$1"}
  - {pattern: "^/blog-(\\d+)-(\\d+)\\.html$",           replacement: "/home.php?mod=space&uid=$1&do=blog&id=$2"}
与官方 Discuz Nginx 伪静态一致。站点若部署在子目录,请给 patternreplacement 统一加上子目录前缀。

#注意事项

  • 路径段里的注入照样被检测:语义引擎把原始 URL.Path 当作独立输入检测,/thread-1' or '1.html 这类构造在重写之前就已进入检测面。
  • 规则尽量锚定 ^...$:锚定即整段路径替换(伪静态惯用);非锚定只替换匹配片段、保留前后文(与 Nginx 一致)。
  • 目标可以是静态文件:若 replacement 指向真实存在的静态文件,则走本地静态发送,不进 php-fpm。