I've gone through four or five Codex pages, a dozens Stackexchange pages, and four or five dev blogs trying to solve this.

I have a custom post type called authors. The CPT is set up correctly, I pulled the CPT framework from the Codex, and I've checked it against a CPT generator. The Admin screens create, edit, and list Author posts correctly, and the URLs are in the correct format of example.com/author/post_name/. Author archives also work.

But nothing I do will display that page on the front end. It's nothing but 404s.

I have flushed the rewrite a dozen times though the Permalinks page and WP-CLI. I have also installed Custom Post Type Permalinks and have the same issue.

CPT is here:

add_action( 'init', 'to4_cpt_author' );

function to4_cpt_author() {
  $labels = array(
    'name'               => _x( 'Authors', 'post type general name' ),
    'singular_name'      => _x( 'Author', 'post type singular name' ),
    'menu_name'          => _x( 'Authors', 'admin menu' ),
    'name_admin_bar'     => _x( 'Author', 'add new on admin bar' ),
    'add_new'            => _x( 'Add New', 'author' ),
    'add_new_item'       => __( 'Add New Author' ),
    'new_item'           => __( 'New Author' ),
    'edit_item'          => __( 'Edit Author' ),
    'view_item'          => __( 'View Author' ),
    'all_items'          => __( 'All Authors' ),
    'search_items'       => __( 'Search Authors' ),
    'parent_item_colon'  => __( 'Parent Authors:' ),
    'not_found'          => __( 'No authors found.' ),
    'not_found_in_trash' => __( 'No authors found in Trash.' )

  $args = array(
    'labels'             => $labels,
    'description'        => __( 'Author post type.' ),
    'public'             => true,
    'publicly_queryable' => true,
    'show_ui'            => true,
    'show_in_menu'       => true,
    'query_var'          => 'author',
    'rewrite'            => array('slug' => 'author', 'with_front' => true ),
    'capability_type'    => 'post',
    'has_archive'        => true,
    'hierarchical'       => false,
    'menu_position'      => 9,
    'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),
    'taxonomies'         => array( 'admin_tag', 'post_tag' ),
    'menu_icon'          => 'dashicons-id-alt'


  register_post_type( 'author', $args );

I've installed the Query Monitor plugin and it tell me the following rewrites are matched.

All Matching Rewrite Rules
Rule                            Query
([^/]*)/([^/]*)/?$              post_type=post

^author/([^/]*)/?               post_type=author

author/([^/]+)(?:/([0-9]+))?/?$ author=$matches[1]

(.?.+?)(?:/([0-9]+))?/?$        pagename=$matches[1]

The second query ^author/([^/]*)/? is one I wrote using:

function to4_rewrite_rule() {
    add_rewrite_rule( '^author/([^/]*)/?', 'index.php?post_type=author&name=$matches[1]','top' );
add_action('init', 'to4_rewrite_rule', 10, 0);

But the top query seems to be getting matched first, which is why I am getting 404s. Query Monitor shows the generated query as name=catherine-collins&post_type=post when it should be name=catherine-collins &post_type=author

Where is that query coming from and how do I demote it? There's no other add_rewrite_rule anywhere in my theme or any plugin so I am guessing this is core. The theme is Twenty-Seventeen with my own custom plugin defining several Custom Post Types.

  • Have you made permalink settings and updated your .htaccess file?
    – Annapurna
    Oct 10, 2017 at 7:08
  • Yes to the permalink settings, no to the htaccess update. As far as I can tell the standard htaccess should work.
    – Slam
    Oct 10, 2017 at 7:14
  • 1
    author slug is already used in WordPress core for frontend lists of posts by author. URLs like author/cahterine transform on name=catherin&post_type=post and get a list of posts (default post type) written by catherine and WordPress picks up a template for this list from the theme like archive-{author_id}.php, arhive-{author_nicename}.php, archive.php or index.php based on template hierarchy. it is very probably that you end up with a lot of conflicts.
    – cybmeta
    Oct 10, 2017 at 8:44
  • I was wondering if someone would notice that! I had discovered and resolved this after using WP All Import to assign post-to-post relationships between to4_cpt_author posts and to4_cpt_article posts, and finding it only match to users not to4_cpt_author. I'm rewriting that post type to be article-author. I also thought it was odd that exampe.com/authors was a working archive when I had no code specifying the plural of author. Rookie mistake.
    – Slam
    Oct 11, 2017 at 15:27
  • Is there any reason I can't hijack the authors slug, though, to display article-authors instead of post_authors? I have zero need to display the users list.
    – Slam
    Oct 11, 2017 at 15:31

2 Answers 2


While you've found the problem already, here is some basic code to move a specific rule to the end of the rewrite rules with the rewrite_rules_array filter for people stumbling across a similar problem.

Say I want to move the rule for ([^/]*)/([^/]*)/?$:

add_filter("rewrite_rules_array", function($rules) {
    $keys = array_keys($rules);
    foreach($keys as $rule) {
        if($rule == '([^/]*)/([^/]*)/?$') {
            $value = $rules[$rule];
            $rules[$rule] = $value;
    return $rules;

Note that you'll need to flush/update the rules for this to have any effect. Saving your permalinks configuration in the backend is enough to accomplish that.

  • Thanks! Is there also some reason why rewrite_rules_array put its value at the top, even when add_rewrite_rule was used with a priority of 1?
    – Slam
    Oct 11, 2017 at 15:21
  • This answers the original questions so I'm accepting it as the answer.
    – Slam
    Oct 11, 2017 at 15:32
  • rewrite_rules_array works directly on the array after the rules have been added, so priority is no longer in play and you are in full control.
    – janh
    Oct 11, 2017 at 15:35

Yeah so it was user error after all.

Months ago I had experiment with Yeost and had added a rewrite_rules_array is functions.php. It was taking priority over the add_rewrite_rule. Once I deleted that, custom posts types worked normally.

Here's the offending code:

add_filter( 'rewrite_rules_array', 'my_rewrite_rules_array');
function my_rewrite_rules_array($rules) {
    $rules = array('([^/]*)/([^/]*)/?$' => 'index.php?post_type=post&name=$matches[2]&meta=$matches[1]') + $rules;
    return $rules;

This was solved by using Atom to do a non-grep search for '([^/]*)/([^/]*)/?$' on the whole site folder.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.