I recently asked this question Custom post types - Use post_id in permalink structure and solved it but as I have enabled 'has_archive' => true the solution given no longer works. Let me explain:

I'm after this structure:

  • archive-events.php => /news/events/
  • single-events.php => /news/events/%post_id%/%postname%

To get post_id in a single event permalink I've had to add %post_id% to the CPT slug but when enabling has_archive => true the archive page becomes the slug; in this case becoming /news/events/%post_id%/ which is invalid.

So my question:

How do I have post_id in permalink structure when using has_archive => true


1 Answer 1


To get what you need you have to add a custom rewrite rule and to filter the permalink construction. The following code does both:

<?php # -*- coding: utf-8 -*-
 * Plugin Name: Event Permalinks
// Not a WordPress context? Stop.
! defined( 'ABSPATH' ) and exit;

// Wait until all needed functions are loaded.
add_action( 'init', array ( 'Bradys_Events', 'init' ) );

class Bradys_Events
     * Creates a new instance.
     * @wp-hook init
     * @see    __construct()
     * @return void
    public static function init()
        new self;

     * Constructor
    public function __construct()
        $args = array(
            'label' => 'events',
            'public' => true,
            'hierarchical' => false,
            'has_archive' => true,
            'rewrite' => array(
                'with_front' => false,
                'slug' => "news/events"
            'supports' => array( 'title', 'editor', 'thumbnail' )
        register_post_type("events", $args);

        // Prevent WordPress from sending a 404 for our new perma structure.

        // Inject our custom structure.
        add_filter( 'post_type_link', array ( $this, 'fix_permalink' ), 1, 2 );

     * Filter permalink construction.
     * @wp-hook post_type_link
     * @param  string $post_link default link.
     * @param  int    $id Post ID
     * @return string
    public function fix_permalink( $post_link, $id = 0 )
        $post = &get_post($id);
        if ( is_wp_error($post) || $post->post_type != 'events' )
            return $post_link;
        // preview
        empty ( $post->slug )
            and $post->slug = sanitize_title_with_dashes( $post->post_title );

        return home_url(
            user_trailingslashit( "news/events/$post->ID/$post->slug" )

Visit wp-admin/options-permalink.php one time to let WordPress update its rewrite rules.

  • I knew you could figure it out ;) Thank you very much
    – Scott
    Commented Mar 6, 2012 at 14:06
  • I love it. Now i can have: /%post_id%/%postname%/ Is it possible to be able to work also with the /%post_id%/ alone too? Just in case the author changes the title name, so the people can still find the post by the id. ----- As clarification, I would like to be able to have the duality where no matter the way the URL is written, it works ( news/events/%post_id%/%postname%/ ) and/or ( news/events/%post_id% )
    – Antonio D.
    Commented Feb 14, 2021 at 7:03
  • @books4languages.com A random order be possible, but it would require a lot of work. Post ID alone does work.
    – fuxia
    Commented Feb 14, 2021 at 8:26
  • with both, Wordpress Post configuration and also with Custom Post Type Permalinks plugin configuration, is possible to have %post_id%/%postname%/ option where if i just write the post_id the page is found. I´m wondering if is possible just with a snippet, like yours. As I do not would like to use a plugin for that.
    – Antonio D.
    Commented Feb 14, 2021 at 8:56

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.