I went over a hundred similar topics here which were close to what I need, but unfortunately I wasn't able to fine tune this so apologies if it's a duplicate.

I have a several post types (such as Events, Venues, Places) and I also use Posts, where the four post types are connected to the standard WordPress Categories taxonomy.

I've been trying to assemble a permalink that looks like that:


Where venues is the name of the category; 61312 is the post_id and california-venue is the postname.

All post types should render the same structure:


That permalink structure seems to work just fine for Posts in Settings -> Permalinks, but doesn't for the other post types.

The ones with with_front => false return 404, the others work fine, but redirect to post_type/postname.

I have a custom rewrite rule that looks like that:

  'regex'     => '^([^/]+)/([0-9]+)/([^/]+)?',  
  'query'       => 'category_name=$matches[1]&post_id=$matches[2]&pagename=$matches[3]', 

I tried filtering the post_type_link and see if the redirect would stop, and I also tried the following snippet by adding disable_redirect=1 to my query rules:

add_filter('query_vars', 'my_public_query_vars');
function my_public_query_vars($qv)
    $qv[] = 'disable_redirect';
    return $qv;
add_filter('wp_redirect', 'my_disable_redirect');
function my_disable_redirect($location)
    $disable_redirect = get_query_var('disable_redirect');
    if(!empty($disable_redirect)) return false;
    return $location;

That simply caused a WSOD.

How can I prevent the redirect to my single views of each post type (single post view seems to work fine) and keep the %category%/%post_id%/%postname% URL structure?

1 Answer 1


After some brainstorming and a lot of trial and error with T5 Rewrite and HM Rewrite, I went for the simplest possible solution.

The permalink was actually intercepted by WordPress so I didn't need the additional rewrite rule (even though it didn't hurt, other than performance). I left %category%/%post_id%/%postname% as the default custom permalink, and it seems to work for all post types so far.

The original slug is handled gracefully too (when leftover links are found), but the new one is the default.

I played with template_redirect, request, redirect_canonical, query_vars, wp_redirect and other hooks in order to find the problem and I ended up with this:

add_filter( 'request', 'sm_permalink_request_handler' );
function sm_permalink_request_handler( $request ) {
    if ( ! isset( $request['p'] ) ) {
        return $request;

    // Get the post type, it's required to prevent the redirect
    $post_id = $request['p'];
    $post = get_post( $post_id );
    $request['post_type'] = $post->post_type;

    return $request;

 * Override get_permalink and prevent redirects
function sm_fix_permalinks_for_post_types( $url, $post ) {
        $taxonomies = get_post_taxonomies();
        // See if categories are assigned to our CPT entry
        if ( ! empty( $taxonomies ) && in_array( 'category', $taxonomies ) ) {

            // Fetch all categories
            $categories = wp_get_post_categories( $post->ID, array() );

            if ( ! empty( $categories ) ) {
                // Get the main category in case there are more assigned
                // Somewhat arbitrary since it pops the last one.
                $main_category_id = array_pop( $categories );

                $main_category = get_category( $main_category_id );

                if( ! empty( $main_category ) ) {
                    $category_slug = $main_category->slug;
                    $post_id = $post->ID;
                    $post_name = $post->post_name;

                    $url = home_url( "$category_slug/$post_id/$post_name" );

        return $url;

add_filter( 'post_type_link', 'sm_fix_permalinks_for_post_types', 10, 2 );

The first problem was related to the lack of post_type passed with the request. My permalink was filtering by category name and post ID, but was posttype-agnostic which interfered with WordPress. By fetching the post by its ID and setting the post_type, things went back to normal.

The second problem was related to the permalinks for new posts, in the admin and anywhere around the frontend templates. Another filtering portion did the trick there and it works across the entire site.

  • 1
    Why is_singular()? What happens on archive pages?
    – fuxia
    Commented May 2, 2015 at 0:13
  • just different permalink requirements for the archives, they seem to work properly this way Commented May 2, 2015 at 7:47
  • Ah that actually makes sense, I misunderstood the question. We have several pages that should not apply these, but yes - in general is_singular should be removed, thanks for the heads up Commented May 2, 2015 at 11:38

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.