Custom post types and taxonomies are one of the most powerful tools to transform WordPress from a blogging platform to a full CMS. One of the most common problems beginning developers have when starting to use them is understanding how they work. It’s easy to think that calling register_post_type
is all it takes only to discover that their new post type archives are not working. This likely leads to a long dive into first (hopefully) the code to see if something was written incorrectly and then to the WordPress support forums to figure out what the problem is. 99% percent of the time I wager it’s because the developer forgot to flush WordPress’s rewrite rules.
The misunderstanding is in thinking that the ‘register’ in register_post_type
means there is some kind of database entry or global variable that completely installs the new post type. Not so. What it actually does is hook your new post types into the list of post types WordPress looks for on every page load. Even if you pass the has_archive
and rewrite
arguments, post type registration does not touch the rewrite rules that tell your webserver where and how to find the new post type, just what those rules should be once they are created.
Unlike post types, rewrite rules are stored in the database and are occasionally cached so they aren’t pulled on every page load. The wp_rewrite
row of the wp_options
table might be quite large depending on what your permalink settings are, how many post types and taxonomies you have, and whether you have any rewrites going outside of WordPress. The only way to change the content of the global variable $wprewrite is to call `flushrewrite_rules`, typically done by saving permalinks settings in wp-admin, this function clears the rewrite row and re-saves it.
Sometimes it isn’t possible to click that button. Consider, for example, a production server without wp-admin accessible. How are you supposed to flush the rules? The flush_rewrite_rules
function is costly, as noted in the codex, but must be hooked into init
to work effectively. The problem is, flushing is expensive, espcially if you have a high-triffic website, and hooking to init
means it will happen on every page load. What to do? Check if rewrite rules are correct and flush only if needed.
We have such a configuration at CFPB where wp-admin is generally inaccessible and we’ve been struggling with exactly this problem every time we create a new post-type. I sloved the problem with a simple function called maybe_flush_rewrite
.
function maybe_flush_rewrite_rules($target) { $rules = get_option( 'rewrite_rules' ); if ( $rules[ $target . '/?$' ] != 'index.php?post_type=' . $target ) { flush_rewrite_rules( $hard = true ); } } // then pass the post type name to maybe flush rewrite rules maybe_flush_rewrite_rules( 'your_post_type' );
YMMV with this function because I’m guessing it may be dependent on your permalinks settings, but after poking around the content of the wp_rewrite
table I realized that if a custom post type had an archive it would have a rewrite structure similar to index.php?post_Type=<post_type_name>
. This function takes a post type name as the $target
parameter, checks if it is registered, and flushes only if it is not in the rewrite table. Now you can register a post type and call maybe_flush_rewrite_rules
anywhere to clear the rules only if needed.