跳到主内容

WordPress 重写规则(Rewrite API)怎么用:自定义 URL、查询变量与刷新时机

100%
WordPress 重写规则(Rewrite API)怎么用:自定义 URL、查询变量与刷新时机

WordPress 重写规则(Rewrite API)负责把”好看的 URL”翻译成 index.php?name=xxx 这类 WP 能理解的查询变量。自定义一条 URL 只需要三步:用 add_rewrite_rule() 把正则映射到查询串,用 add_rewrite_tag() 让 WP 认识你的自定义变量,然后刷新一次重写规则让它真正落库。绝大多数”规则写了却 404″的问题,都出在漏了第三步。

重写规则在请求流程里的位置

一次前台请求的链路是这样的:

  1. Web 服务器(Nginx/Apache)把所有不存在的路径转给 index.php;
  2. WP 加载 rewrite_rules 这个 option 里存的规则数组;
  3. 按数组顺序逐条正则匹配当前请求路径,命中则转成查询变量;
  4. WP_Query 依据查询变量取数据,再交给模板层级渲染。

所以重写规则不是实时的——它是从数据库里读出来的一份缓存。改了规则不刷新,前台就还是旧行为。

四个核心函数

函数作用典型用途
add_rewrite_rule()加一条正则 → 查询变量的映射自定义页面 URL 、虚拟端点
add_rewrite_tag()注册自定义查询变量名让 WP 认识 ?city= 之类参数
add_rewrite_endpoint()追加一个端点/json/、/print/ 这类后缀
flush_rewrite_rules()重算并写库仅在激活/停用时调用

一条完整示例:把 /city/shanghai/ 变成可查询页

add_action( 'init', function () {
    add_rewrite_tag( '%city%', '([^&/]+)' );

    add_rewrite_rule(
        '^city/([^/]+)/?$',
        'index.php?pagename=city&city=$matches[1]',
        'top'
    );
} );

// 让模板里能拿到这个值
add_filter( 'query_vars', function ( $vars ) {
    $vars[] = 'city';
    return $vars;
} );

$matches[1] 对应正则里的第一个捕获组。第三个参数 top 表示插到规则数组最前面——只要传的不是 bottom,都会被放到顶部。放在顶部能避免被 WordPress 自带的宽泛规则抢先匹配。

自定义查询变量必须同时过 add_rewrite_tag() 和 query_vars 过滤器两道关。少了前者,重写能匹配但变量进不了查询;少了后者,get_query_var() 永远取不到值。

  1. 在 init 钩子里写规则:早于 init 注册无效,晚于 init 可能错过规则生成。

  2. 正则尽量收窄:用 ([^/]+) 而不是 (.+),避免吞掉分页、 feed 等后续路径。

  3. 用 add_rewrite_tag() 注册变量,并挂 query_vars 过滤器声明。

  4. 在插件的 register_activation_hook 里调 flush_rewrite_rules(),停用钩子里也调一次做清理。

  5. 如果不想写代码刷新,就去后台「设置 → 固定链接」点一次保存,效果等价。

  6. 验证:先后台看 /wp-admin/options.php 里的 rewrite_rules,再前台访问新 URL 确认不是 404 。

刷新时机:这是最容易出性能事故的地方

flush_rewrite_rules() 会先删规则再重算一遍全部规则并写库,是很慢的操作。官方参考文档里的示例虽然写了”规则不存在就刷”,但也同时注明实际项目中不该挂在 wp_loaded 上每次请求都跑。

正确做法只有两种:

  • 代码侧:写在插件激活/停用钩子里,规则变了才刷一次。
  • 人工侧:后台固定链接设置页保存一次。

如果你确实需要”启动时自动补齐”,至少加一个存在性判断再刷,别无条件调:

function my_maybe_flush() {
    $rules = get_option( 'rewrite_rules' );
    if ( ! is_array( $rules ) || ! isset( $rules['^city/([^/]+)/?$'] ) ) {
        flush_rewrite_rules();
    }
}
add_action( 'wp_loaded', 'my_maybe_flush' );
Nginx 环境下规则不生效的排查顺序

第一步确认 Nginx 侧有没有 try_files $uri $uri/ /index.php?$args;,没有的话请求根本到不了 WP;第二步确认 PHP-FPM 能正常执行;第三步才去看 WP 规则数组。很多”Rewrite API 不好使”其实是服务器层重定向没配对,跟 WP 规则无关。另外固定链接结构如果是”朴素”(?p=123),WP 不会启用重写规则,自定义规则自然不会命中。

常见坑

  • 规则写对了但一直 404:九成是没刷新。剩下一成是 $after 用了默认的 bottom,被其他规则抢先匹配。
  • 分页失效:正则写成 (.+) 把 /page/2 一起吃掉了,改成 ([^/]+) 并补一条带分页的规则。
  • 刷新写在 init 上:每个请求重写一次规则,站点明显变慢,是典型的”能跑但很脏”。
  • 和 CPT slug 冲突:自定义规则的 slug 与已有文章类型、分类法重名时,后者可能覆盖前者,换个前缀最省事。
  • 忘了清理:插件停用时要刷一次,否则数据库里会留下指向已消失处理逻辑的死规则。

add_rewrite_rule 和直接改 .htaccess 有什么区别?
add_rewrite_rule 加的是 WordPress 内部规则,存在数据库的 rewrite_rules option 里,由 PHP 解析,不依赖 Web 服务器,Nginx 环境同样有效。改 .htaccess 是 Apache 层面的重写,只在 Apache 生效,且不参与 WP 的查询变量体系。做 WP 层的自定义 URL 一律用 Rewrite API 。

为什么我的规则在 top 还是不生效?
先确认是否刷新过规则;再确认自定义查询变量有没有同时注册 add_rewrite_tag 和 query_vars;最后检查固定链接结构是否为”朴素”。三项都过了还不行,多半是服务器层没有把请求转给 index.php 。

能不能用 Rewrite API 做 301 跳转?
不建议。 Rewrite API 的设计目标是”把 URL 解析成查询”,不是做重定向。真正的 301 应该在服务器层(Nginx rewrite/return)或用 wp_redirect() 在 template_redirect 钩子上处理,语义和性能都更合适。

规则数组太大影响性能吗?
会。规则越多,每次请求要做的正则匹配越多。控制手段是收窄正则、避免注册大量 CPT 、停用插件时清理死规则。可以用 Query Monitor 之类插件观察规则总数与匹配耗时。

相关阅读

延伸阅读:Hooks 用法详解
这篇有帮助吗?
云上的幻象
云上的幻象查看主页

七彩云博客,分享 WordPress 建站实战与 AI 工具测评,覆盖服务器运维、站长工具、软件资源与电商运营干货,专注原创实用的主题插件、网站加速与安全优化教程。

811文章4评论

相关文章

评论 (0)

欢迎你,新朋友,感谢参与互动!文明发言,理性交流 · 首次评论将在审核后展示