Got a call from a client last week. One of our junior devs had been tasked with a “simple” job: create a custom landing page at /welcome/ for a new campaign. But the page was throwing a 404. Total dead end. The client was getting anxious. The dev was stumped. They swore the page existed, the template was there, but WordPress just wouldn’t see it. This, my friends, is a classic case of getting tangled in the WordPress Rewrite API.
The Rewrite API is WordPress’s internal translator. It’s the magic that turns a pretty URL like /my-awesome-post/ into a query that WordPress understands, like ?p=123. Without it, we’d all be stuck with ugly URLs. But when you introduce custom rules, things can get messy. Fast.
The Obvious Fix That Makes Things Worse
My first thought was the usual suspect. “Did you flush the rewrite rules?” I asked. Of course, he had. He even showed me the code. He had hooked a function into init to add the new rule for the /welcome/ page, and to be “safe,” he added a call to flush_rewrite_rules() right after it. And that’s the kicker. I could feel the site groaning from here. He was regenerating the entire URL routing table on every single page load.
This is a total nightmare for performance. flush_rewrite_rules() is an expensive operation. It writes to the database and can even modify the .htaccess file. Running it on every request is like rebuilding your house every time you want to open a window. It “works,” but it’ll bring even a powerful server to its knees. This was my vulnerability—I’d made the same mistake years ago on a high-traffic site and almost took it down during a product launch. Trust me on this, you only learn that lesson once.
Flushing Rewrite Rules the Right Way
The rule itself was fine. The problem was the flushing strategy. You don’t need to flush rules all the time. You only need to do it once when the rules change. That’s it. On plugin activation, deactivation, or when a specific setting that affects URLs is changed.
The reliable way to handle this is with a database flag. You add your rewrite rules on init every time—that’s correct, because they need to be registered with WordPress on every load. But you only trigger the flush when you need it. This approach, which builds on a great concept I saw over at carlalexander.ca, is rock-solid.
// 1. Add your rule on every page load so WordPress knows about it
function my_custom_rewrite_rule() {
add_rewrite_rule('^welcome/?$', 'index.php?pagename=welcome-page', 'top');
}
add_action('init', 'my_custom_rewrite_rule');
// 2. Set a flag when your plugin is activated
function my_plugin_activate() {
add_option('my_plugin_flush_rewrite_rules', true);
}
register_activation_hook(__FILE__, 'my_plugin_activate');
// 3. Check for the flag, flush the rules ONCE, then delete the flag
function my_plugin_flush_rules_on_load() {
if (get_option('my_plugin_flush_rewrite_rules')) {
flush_rewrite_rules();
delete_option('my_plugin_flush_rewrite_rules');
}
}
add_action('init', 'my_plugin_flush_rules_on_load');
So, What’s the Real Takeaway?
The WordPress Rewrite API is powerful, but it demands respect. It’s not just about writing the right regex. It’s about understanding the performance implications of your actions. The key lesson is to separate rule registration from rule flushing.
- Register your rules on every page load using the
inithook. - Flush your rules only when they change. Use an activation hook or a settings-save callback to set a one-time flag.
Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.
Get this right, and you’ll build robust, performant features. Get it wrong, and you’ll be wondering why your server is on fire. Simple as that.
Leave a Reply