WordPress 重写规则(Rewrite API)负责把”好看的 URL”翻译成 index.php?name=xxx 这类 WP 能理解的查询变量。自定义一条 URL 只需要三步:用 add_rewrite_rule() 把正则映射到查询串,用 add_rewrite_tag() 让 WP 认识你的自定义变量,然后刷新一次重写规则让它真正落库。绝大多数”规则写了却 404″的问题,都出在漏了第三步。
重写规则在请求流程里的位置
一次前台请求的链路是这样的:
- Web 服务器(Nginx/Apache)把所有不存在的路径转给
index.php; - WP 加载
rewrite_rules这个 option 里存的规则数组; - 按数组顺序逐条正则匹配当前请求路径,命中则转成查询变量;
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() 永远取不到值。- 在
init钩子里写规则:早于init注册无效,晚于init可能错过规则生成。 - 正则尽量收窄:用
([^/]+)而不是(.+),避免吞掉分页、 feed 等后续路径。 - 用
add_rewrite_tag()注册变量,并挂query_vars过滤器声明。 - 在插件的
register_activation_hook里调flush_rewrite_rules(),停用钩子里也调一次做清理。 - 如果不想写代码刷新,就去后台「设置 → 固定链接」点一次保存,效果等价。
- 验证:先后台看
/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 有什么区别?
为什么我的规则在 top 还是不生效?
能不能用 Rewrite API 做 301 跳转?
wp_redirect() 在 template_redirect 钩子上处理,语义和性能都更合适。规则数组太大影响性能吗?
相关阅读
- WordPress 钩子(Hooks)怎么用:Action 与 Filter 的区别与实战
- WordPress 全站编辑(FSE)实战:用站点编辑器改版整个网站
- WordPress 速度优化实测:从 3 秒到 0.5 秒的 8 个动作
- 让 WordPress 被 AI 引用:GEO 优化的 9 个实操步骤










评论 (0)