I'm having a difficult time trying to get my custom post types URLs to work correctly. Every solution I find has crippled another part that needs to work. What I am trying to accomplish is domain.com/post_type/post_id/post_name.

What I have that works for everything except archives is:

    'rewrite' => array(
    'slug' => 'post-type/%post_id%',
    'with_front' => false,
    'pages' => true,
    'ep_mask' => 1 

Then I have:

add_filter('post_type_link', 'custom_post_type_link', 1, 3);
function custom_post_type_link($post_link, $post = 0, $leavename = false) {

    if ($post->post_type == 'post-type')) {
        return str_replace('%post_id%', $post->ID, $post_link);
    } else {
        return $post_link;

So I'm trying to find a way to get domain.com/post_type to work. It currently throws a 404.

I know that if I remove the filter and /%post_id% from my rewrite that the archive will work. From there I've tried adding a rewrite rule:

add_action( 'init', 'custom_rewrites_init' );
function custom_rewrites_init(){
        'top' );

Doing it this way the URL wont redirect if someone types in domain.com/post_type/post_id and domain.com/post_type/post_id/post_name/2 doesn't work.

Anyone know of the best way to do this?


3 Answers 3


Your code/solutions is pretty good. You use slug param in a very smart way, so WordPress automatically creates correct permastruct for this CPT.

The only thing you're missing, I guess, is has_archive param. It's default value is false and you don't set it to true. So WordPress doesn't create archive links/pages for your CPT.

has_archive (boolean or string)

(optional) Enables post type archives.

Will use $post_type as archive slug by default. Default: false Note: Will generate the proper rewrite rules if rewrite is enabled. Also use rewrite to change the slug used.

If you do this, WordPress will create rewrite rules for archive pages. There will be a tiny problem. They will contain %post_id% in them. But it's pretty easy to correct. You just have to add this code after your register_post_type call:

global $wp_rewrite;

$new_rules = array();
foreach ( $wp_rewrite->extra_rules_top as $key => $rule ) {
    echo $key;
    if (strpos($key, 'post-type/%post_id%/') === 0 ) {
        $new_rules[ str_replace('%post_id%/', '', $key) ] = $rule;
        unset( $wp_rewrite->extra_rules_top[$key] );
$wp_rewrite->extra_rules_top = $wp_rewrite->extra_rules_top + $new_rules;

It will repair rules for archive pages created by WordPress. It's clean solution - it doesn't leave any mess behind, and it takes care of all rules for archive page (feeds, etc.)

  • Just adding has_archive => true won't solve the problem, but in addition with birgire's code it will work. Jun 29, 2013 at 20:20
  • I played a little bit with this, and updated my answer. The problem was that WordPress created rewrite rules with %post_id% for archive pages. All you have to do is to repair them. Jun 29, 2013 at 21:01
  • Thanks @KrzysiekDróżdż, this worked exactly how I wanted it.
    – kel
    Jul 1, 2013 at 21:01

You could try to add this code snippet:

add_filter( 'rewrite_rules_array', 'custom_rewrite_rules_array' );
function custom_rewrite_rules_array( $rules ) {
        $newrules = array();
        $newrules['^post-type/page/?([0-9]{1,})/?$'] = 'index.php?post_type=post-type&paged=$matches[1]';
        $newrules['^post-type$'] = 'index.php?post_type=post-type';
       return $newrules + $rules;

to add support for domain.com/post-type/ and domain.com/post-type/page/2.

You just have to remember to save the permalinks.

  • Well, it's not perfect ;) 1. You leave incorrect rewrite rules created by WP. 2. You don't take care of other rewrite rules that WP creates for archive pages: feeds (not always), and so on. Jun 29, 2013 at 21:03
  • well let me post the whole working solution that I tested the above snippet on ;-)
    – birgire
    Jun 29, 2013 at 22:09
  • Are you sure you get only these rules? You have has_archive set to true, but there are no archive rules in your output. It's a little bit strange - WordPress generates them for me (don't forget about extra_rules_top). Jun 29, 2013 at 22:15
  • ok maybe I will test it again when I have time, will remove it untill then, thanks for the suggestions ;-)
    – birgire
    Jun 29, 2013 at 22:21

Have you reset your permalink settings after saving the template file? Before you can use a new permalink like that you must resave your permalink settings in Settings->Permalinks.

  • Yes, I do that every time I make a change to the permalinks.
    – kel
    Jun 27, 2013 at 15:11
  • Well, to be precise you should flush rewrite rules, you don't have to resave permalink settings (you can achieve it in other/more correct ways too). Jun 29, 2013 at 0:32

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.