Rewrite Rules for a Custom Post Type & Taxonomy  Pattern

Achieving the most commonly sought after URL rewrite structure for a custom WordPress resources section built using a post type, taxonomy, and associated archives and single templates.

If you’ve ever built a Resources page for a custom WordPress theme, you’ve probably come accross bugs related or directly caused by the following pattern.

It’s not that clear what’s going on, but once you do figure it out you’ve probably implemented a bandaid approach that doesn’t quite give you the URL structure you originally planned. There are solution out there with alternative approaches, but I haven’t seen one that takes the following approach which will preserve the ideal URL pattern.

Post Type Rewrites With Zero Compromise

In an ideal world a Resources section of a custom WordPress powered website would have URL patterns like this:

Creating The Custom Post Type & Taxonomy

The first step to do this is to create a custom post type and associated taxonomy, which I’ve called “Resource Type”. In a real world scenario these would typically be something like:

They’re used to identify the type of each resource, which is very handy when building out templates. Inside of single-resource.php I would typically put in login to load a partial template for the specific type of resource. Videos would pull in a YouTube or Wistia player, while Webinars would pull in a signup form (before the date) or embed a video (after the date), and so on.

Resource Post Type

Most developers would go about defining the resource post type in the following (correct) way:

register_post_type('resource', [
  'labels' => [
    'name' => 'Resources',
    'singular_name' => 'Resource',
    'add_new' => 'Add New',
    'add_new_item' => 'Add New Resource',
    'edit_item' => 'Edit Resource',
    'new_item' => 'New Resource',
    'view_item' => 'View Resource',
    'search_items' => 'Search Resource',
    'not_found' => 'No resources found',
    'not_found_in_trash' => 'No resources found in Trash',
  ],
  'public' => true,
  'has_archive' => true,
  'taxonomy' => 'resource_type',
  'rewrite' => ['slug' => 'resources'],
  'menu_position' => 20,
  'menu_icon' => 'dashicons-media-document',
  'supports' => ['title', 'editor', 'thumbnail'],
  'show_in_rest' => true,
]);

Resource Type Taxonomy

For the custom taxonomy, we’d do something like this:

register_taxonomy('resource_type', 'resource', [
  'labels' => [
    'name' => 'Resource Types',
    'singular_name' => 'Resource Type',
    'search_items' => 'Search Types',
    'all_items' => 'All Type',
    'parent_item' => 'Parent Resource Type',
    'parent_item_colon' => 'Parent Resource Type:',
    'edit_item' => 'Edit Resource Type',
    'update_item' => 'Update Resource Type',
    'add_new_item' => 'Add New Resource Type',
    'new_item_name' => 'New Resource Type Name',
    'menu_name' => 'Type',
  ],
  'hierarchical' => true,
  'public' => true,
  'show_admin_column' => true,
  'show_in_rest' => true,
  'rewrite' => [
    'slug' => 'resources',
    'with_front' => false,
    'hierarchical' => true,
  ],
]);

Rewrite Troubles

After defining the custom post type and taxonomy we flush our URL rewrites by running wp rewrite flush or visiting the Settings > Permalinks admin page. The expected behavior here would be similar to what I’ve listed at the start of this, with specific theme templates loading for the expected URL patterns.

But that won’t happen.

You may find that your archive-resource.php theme template isn’t loading when you visit /resources/, and maybe index.php is being used as a fallback template instead. When you try to visit a resource type archive at something like /resources/webinars/ it shows a 404 error.

What’s going on here?

WordPress is trying to use the same URL pattern for your single resource posts and your resource types. It has no way of knowing that /webinars/ is not the name of a single published resource, or vice versa that /resources/some-webinar-slug/ isn’t referencing a resource_type taxonomy term of some-webinar-slug.

Potential Solutions

So what do we do? There are a few different options.

Using Singular & Plural URL Patterns

One crafty way around this involves switching the config to use “resources” (plural) for the archive pages, /resources/ and /resources/{resource-type}/, and “resource” (singular) for the single resource posts: /resource/{slug}/.

But this approach isn’t universal, it may work for the word “Resources” but if you build similar sections with a different word, such as “Research”, you’ll find there is no plural to work with.

Sharper Custom Rewrites

Disable the rewrites you’ve defined within your register_post_type and register_taxonomy definitions and instead create your own. This provides you with complete control over your URL patterns, removing ambiguity from the rewrite process.

You can accomplish this using add_rewrite_rule() within your theme or plugin:

function custom_resource_rewrites() {
    add_rewrite_rule('^resources/([^/]+)/?$', 'index.php?resource_type=$matches[1]', 'top');
    add_rewrite_rule('^resource/([^/]+)/?$', 'index.php?resource=$matches[1]', 'top');
}
add_action('init', 'custom_resource_rewrites');

After adding these custom rewrite rules, flush your rewrites once again by visiting Settings > Permalinks or running wp rewrite flush.

Resource Types in URLs

Another approach is to nest your resource types deeper into the URL structure. Instead of trying to share /resources/ for both types and posts, you can separate them cleanly:

You can set this up by adjusting the rewrite arguments:

register_post_type('resource', [
    'rewrite' => ['slug' => 'resources', 'with_front' => false],
]);

register_taxonomy('resource_type', 'resource', [
    'rewrite' => ['slug' => 'resources/type', 'with_front' => false],
]);

Conclusion

Getting custom post types and taxonomies to work together cleanly in WordPress takes a little extra effort. The default rewrite behavior isn’t smart enough to handle overlapping URL patterns, but with careful planning you can avoid the conflicts. Whether you choose to separate your singular and plural slugs, create sharper custom rewrites, or nest taxonomy terms under a dedicated path, the key is making your structure clear to both WordPress and your users. The cleaner your rewrites, the fewer surprises you’ll run into later.