Remove Post Type Slug from URLs

I’ve been asked about how to do it probably a million times. The question is how to make the URLs of your custom post types (or WooCommerce products) to look like Pages.

For example how to have example.com/t-shirt/ instead of example.com/product/t-shirt/.

In this tutorial I am going to provide you a complete guide! As an example we are going to remove /product/ from WooCommerce product URLs. But before we begin I would like to warn you that you have to avoid it if possible, because it may slows down the overall performance of your website and you have to be extra-careful when assigning a specific slug for your posts.

Let’s begin with a simple post_type_link filter hook.

add_filter( 'post_type_link', 'rudr_remove_cpt_slug', 20, 3 );

function rudr_remove_cpt_slug( $permalink, $post_id, $leavename ) {

	// please add a post type slug that is currently displaying in URL
	$cpt_slug = 'product';

	$permalink = str_replace( "/{$cpt_slug}/", '/', $permalink );

	return $permalink;

}

$cpt_slug here is what is provided in Settings – Permalinks, under Product Permalinks. For custom post types it is either a post type name or the rewrite slug parameter.

I used the default value, but theoretically you could reduce the number of possible errors if you use something custom here.

WooCommerce product permalinks

Now, if you go to WooCommerce shop page, you will see that the product links has been changes… but… when you try to open any product page you will get 404 error. It is because we changed the URLs but we didn’t change the way WordPress processes them.

Custom URL processing can be done in request hook. Please keep in mind that the code below is also dependent on permalinks common settings. It will work great for a post name value.

permalinks common settings

Finally, the code:

add_filter( 'request', function( $query ){

	if( empty( $query[ 'name' ] ) ) {
		return $query;
	}

	global $wpdb;

	$cpt_name = 'product'; // not slug!! 

	// yeah... we have to run one more SQL query...
	$post_id = $wpdb->get_var(
		$wpdb->prepare(
			"
			SELECT ID
			FROM $wpdb->posts
			WHERE post_name = '%s'
			AND post_type = '%s'
			",
			array(
				$query[ 'name' ],
				$cpt_name
			)
		)
	);

	if( $post_id ) {
		$query[ $cpt_name ] = $query[ 'name' ];
		$query[ 'post_type' ] = $cpt_name;
	}

	return $query;

}, 1 );

Still got 404 error? Just flush permalinks (you can update them in settings without changing).

That’s actually it! Maybe you would also like to configure redirects from old URLs to new ones with template_redirect hook but it is optional here.

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X