Documentation Remove the slug from your custom post type permalinks

Remove the slug from your custom post type permalinks

WordPress easily lets you use just the post_name value for Posts and Pages, without any performance impacts, thanks to some clever code in core. To achieve the same effect with a custom post type, you’ll need to use some trickery.

First, you’d register your custom post type as though you want to keep the slug in the URI:

/**
 * Register a custom post type but don't do anything fancy
 */
register_post_type( 'event', array( 'label' => 'Event', 'public' => true ) );

Next, we want to filter the permalink for our custom post type such that all published posts don’t have the slug in the URI:

/**
 * Remove the slug from published post permalinks. Only affect our CPT though.
 */
function vipx_remove_cpt_slug( $post_link, $post, $leavename ) {

	if ( 'event' != $post->post_type || 'publish' != $post->post_status ) {
		return $post_link;
	}

	$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );

	return $post_link;
}
add_filter( 'post_type_link', 'vipx_remove_cpt_slug', 10, 3 );

At this point, you’ll hit a 404 if you try to view the link. That’s because WordPress only knows the URI can be Posts or Pages. We need to tell it to also pay attention to our custom post type. Here’s how:

/**
 * Some hackery to have WordPress match postname to any of our public post types
 * All of our public post types can have /post-name/ as the slug, so they better be unique across all posts
 * Typically core only accounts for posts and pages where the slug is /post-name/
 */
function vipx_parse_request_tricksy( $query ) {

	// Only noop the main query
	if ( ! $query->is_main_query() )
		return;

	// Only noop our very specific rewrite rule match
	if ( 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
		return;
	}

	// 'name' will be set if post permalinks are just post_name, otherwise the page rule will match
	if ( ! empty( $query->query['name'] ) ) {
		$query->set( 'post_type', array( 'post', 'event', 'page' ) );
	}
}
add_action( 'pre_get_posts', 'vipx_parse_request_tricksy' );

Voila! Custom post type permalinks without the slug.