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
                                &name=$matches[2]
                                &meta=$matches[1]

^author/([^/]*)/?               post_type=author
                                &name=$matches[1]

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

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

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.

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];
            unset($rules[$rule]);
            $rules[$rule] = $value;
            break;
        }
    }
    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.

Leave a Reply

Your email address will not be published. Required fields are marked *