`rewrite_rules` 写入成功但路由 404 的"时间差"陷阱:我如何被 `flush_rewrite_rules` 的"延迟生效"机制坑了整整一下午

小助手
小助手 版主圣羽星庭 勋望元宿志愿先锋
社区管理
插件开发 62 浏览 0 回复

昨天给客户部署一个自定义文章类型 + 自定义端点的插件,本地测试一切正常,传到生产环境后,安装流程走完,设置页能进,但所有自定义路由统一 404。最诡异的是,后台「设置-固定链接」里点一下保存,路由立刻复活。

第一反应是 .htaccess 没写权限,但文件明明更新了,内容也在。第二反应是 flush_rewrite_rules() 没执行到,我在 register_activation_hook 的回调里加了 error_log,日志确认走到了,甚至 get_option('rewrite_rules') 都能读出我注册的新规则。

问题就卡在这里:规则在数据库里,.htaccess 也刷了,为什么前端 404?

——直到我翻到 wp-admin/includes/misc.phpsave_mod_rewrite_rules() 的源码,才看见这行注释:

// If the file is writable, make sure it has the same permissions as before
// Some systems set umask to 022, which means the file is created as 644

这不是重点。重点是我之前为了"优化",把 flush_rewrite_rules() 包进了一个异步任务:

register_activation_hook( __FILE__, function() {
    // 错误示范:想"不阻塞激活流程"
    wp_schedule_single_event( time(), 'my_plugin_delayed_flush' );
} );

add_action( 'my_plugin_delayed_flush', function() {
    flush_rewrite_rules( true ); // hard flush
} );

坑在这里:wp_schedule_single_event 依赖 cron,而很多生产环境的 cron 是外部触发的(比如服务器 crontab 每分钟跑 wp-cron.php),不是用户请求触发的。插件激活那瞬间,cron 任务进了队列,但还没执行,用户已经去访问新路由了——404。

更隐蔽的是,如果你用某些"禁用 wp-cron"的插件,或者服务器没配外部 cron,这个任务可能永远挂起。我客户的站恰恰是 DISABLE_WP_CRON 设为 true,但外部 cron 没配 wp-cron.php 的路径,任务进了数据库的黑洞。

另一个我踩过的变种:在 init 钩子优先级 0 就调用 flush_rewrite_rules(),结果因为自定义文章类型的注册也在 init(默认优先级 10),规则刷的时候,文章类型还没注册进去。刷了个寂寞。

现在我的激活流程改成这样,分两步走:

register_activation_hook( __FILE__, function() {
    // 第一步:立刻 soft flush,确保当前请求周期内规则可用
    // 但前提是 CPT/endpoint 已经注册完毕
    add_action( 'init', function() {
        flush_rewrite_rules( false ); // soft,不碰 .htaccess,只更新 option
    }, 999 ); // 等所有注册跑完
    
    // 第二步:hard flush 还是放同步里,但加个标志位防止重复
    update_option( 'my_plugin_needs_hard_flush', 1 );
} );

// 在后续正常的 init 中检查
add_action( 'init', function() {
    if ( get_option( 'my_plugin_needs_hard_flush' ) ) {
        flush_rewrite_rules( true );
        delete_option( 'my_plugin_needs_hard_flush' );
    }
}, 1000 );

关键认知:flush_rewrite_rules 不是"魔法刷新",它依赖两个条件——当前请求周期内所有 rewrite tag 和 rule 已经注册完毕,以及 写入时机和写入方式(soft/hard)匹配你的服务器环境。异步任务、优先级错位、.htaccess 不可写但代码没报错,都是 404 的沉默帮凶。

最后补一个快速诊断命令,不用每次点后台保存:

wp rewrite flush --hard
wp rewrite list --match=/your-custom-slug/

WP-CLI 的 --match 能直接告诉你某个路径命中了哪条规则,比在前端刷新页面高效十倍。

评论0
回复 · 0
还没有回复
微信客服 微信客服