You just registered a custom post type, changed your permalink structure, or activated a new plugin, and now pages that exist return 404 errors. WordPress stores its URL rewrite rules in the database, and those rules are out of sync with reality.
Flushing the rewrite rules rebuilds them. It’s one of the most common WordPress troubleshooting steps, and there are three ways to do it depending on your setup.
TLDR
Go to Settings > Permalinks and click “Save Changes” without changing anything. That flushes the rewrite rules. If you have SSH access, run wp rewrite flush instead. In plugin or theme code, call flush_rewrite_rules() on activation only, never on every page load.
When you need to flush
A few situations where stale rewrite rules cause problems:
- 404 errors on posts, pages, or custom post type archives that exist in the database.
- A new custom post type or taxonomy that doesn’t resolve to its URL.
- Permalink structure changes that don’t take effect after saving.
- A plugin that registers REST API routes or custom endpoints not showing up.
If you changed something that affects URLs and now URLs are broken, flushing rewrite rules is the first thing to try.
Method 1: the WordPress dashboard
The simplest approach. No code, no command line.
- Log in to WordPress admin.
- Go to Settings > Permalinks.
- Scroll down and click “Save Changes”.
You don’t need to change any settings. Clicking save triggers WordPress to rebuild the rewrite rules from scratch and write them fresh to the database.

This is the method most tutorials recommend, and for good reason. It works, it’s safe, and it’s available to anyone with admin access.
Method 2: WP-CLI
If you have SSH access to the server, WP-CLI is faster and works in situations where the dashboard isn’t accessible (broken admin, headless setups, CI/CD pipelines).
wp rewrite flush
That’s it. The rules get rebuilt. You can verify the current rewrite structure with:
wp rewrite list
This outputs every registered rewrite rule, which is useful for debugging custom post types or taxonomy URLs that aren’t matching correctly.
For a hard flush that also updates .htaccess (or the Nginx equivalent rules), add the --hard flag:
wp rewrite flush --hard
The --hard flag rewrites the .htaccess file with the current permalink structure. Use it after changing the permalink setting itself, not just after registering new post types.
WP-CLI is especially useful in deployment scripts. After deploying code that registers new post types or changes URL structures, adding wp rewrite flush to the deployment steps makes sure the rules stay current without anyone needing to visit the dashboard.
Method 3: in code
When you’re building a plugin or theme that registers custom post types or taxonomies, you need to flush rewrite rules when the plugin activates. Not on every page load.
register_activation_hook( __FILE__, 'my_plugin_activate' );
function my_plugin_activate() {
// Register the post type first so its rules exist.
my_plugin_register_post_types();
flush_rewrite_rules();
}
The flush_rewrite_rules() function is expensive. It queries every registered post type, taxonomy, and route, rebuilds the full rule set, and writes it to the wp_options table. Running it on every init or admin_init hook slows down every page load.
Only call it during activation, deactivation, or after a specific settings change that affects URLs. Never on init.
For theme activation:
add_action( 'after_switch_theme', function () {
flush_rewrite_rules();
} );
If flushing doesn’t fix the 404
Rewrite rules aren’t always the cause. If flushing didn’t help, check these:
.htaccessisn’t writable. WordPress can’t update its rules if the file permissions are wrong. Check that.htaccessexists in the root and is writable by the web server.- A server-level redirect is overriding WordPress. Nginx configurations and server-level
.htaccessrules take priority over WordPress rewrite rules. Check the server config. - A caching plugin is serving a stale 404. Clear the page cache after flushing rewrite rules. Some object cache setups cache rewrite rules too.
- The post type or taxonomy wasn’t registered with
'public' => trueand'publicly_queryable' => true. WordPress won’t generate rewrite rules for non-public types.
For deeper PHP-level debugging when things don’t work as expected, enabling WP_DEBUG is the next step.