I was wondering how i can remove the prefix from the slugs of custom post types using htaccess rules or any other appropriate method.

Currently my custom post types look like this:


and i would simply like this to be:


I was able to achieve this via a plugin but it significantly added to the page load time..

My common settings in Permalinks is set to "Post Name"

3 Answers 3


There is a simpler and lighter solution:

First: set the slug argument for your custom post type to '/' when registering the post type:

add_action( 'init', 'register_my_post_type' );
function register_my_post_type() {
    $args = array(
            //The rest of arguments you are currently using goes here
            'rewrite'    => array(
                             'slug' => '/'
     register_post_type( 'my-post-type', $args );   

Second: in preg_get_posts() action hook include your post type in the query when only the $query->query['name'] is present:

add_action( 'pre_get_posts', 'wpse_include_my_post_type_in_query' );
function wpse_include_my_post_type_in_query( $query ) {

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

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

      // Include my post type in the query
     if ( ! empty( $query->query['name'] ) )
          $query->set( 'post_type', array( 'post', 'page', 'my-post-type' ) );

Third: I've found that in some situations there are conflicts with pages and single posts. I've been able to avoid this conflict by checking if a page with the request name exists and overriding the query conditionals but I'm not sure if more conflict can happens:

add_action( 'parse_query', 'wpse_parse_query' );
function wpse_parse_query( $wp_query ) {

    if( get_page_by_path($wp_query->query_vars['name']) ) {
        $wp_query->is_single = false;
        $wp_query->is_page = true;


IMPORTANT: You need to flush the rewrite rules. You can do it by viviting the permalink options page in the admin area. This is needed because we are registering a new rewrite rule, so the current rewrite rules stored in the database must be regenerated.

  • hmm. I have tested the code but it seems that the whole custom post type disappears when I put its name in your code!
    – JoaMika
    May 19, 2014 at 16:59
  • The permalink is correctly changes to www.domain.com/postname but I do not see the link to edit the post nor I can see this at the backend..
    – JoaMika
    May 19, 2014 at 17:04
  • 1
    You have to complete the $args for register_post_type() function. To make things easier you can just add the argument 'rewrite' => array('slug' => '/' ) to your actual register post type implementation and then add the code of the second step to your functions.php theme file or to your plugin file. Note that in the second step you have to add the custom post type names in $query->set( 'post_type', array( 'post', 'page', 'my-post-type-1', my-post-type-2', 'etc..' ) );. If you post your actual code to register the custom post types I can post the full working code.
    – cybmeta
    May 19, 2014 at 18:29
  • ok this works now but it messes up with my pages template sidebar... is it possible to exclude pages from your code?
    – JoaMika
    Jun 1, 2014 at 13:18
  • If it works, please accept the answer. I don't understand you "new" problem about pages template sidebar. If you exclude pages from my code, URLs like http://mysite.com/i-am-a-page/ won't work. You will need to load the desired template/sidebar using conditional tags like is_page(), is_single() or `is_singular().
    – cybmeta
    Jun 2, 2014 at 10:12

// Well then, first use a function to mention your custom post types.

function my_custom_post_types(){
    return array(

// Then, filtering the request.

    add_action( 'parse_request','my_custom_post_types_parse_request', 999);

function my_custom_post_types_parse_request( $query ){

    if( isset($query->request) )
        $req = explode('/', ltrim($query->request,'/'));
        if( !is_admin() && isset($req['0']) )
            global $wpdb;
            $name = stripslashes($req['0']);
            $_post = $wpdb->get_row( "SELECT * FROM $wpdb->posts WHERE post_status = 'publish' AND post_name ='". $name ."' AND post_type IN ('" . join("', '", my_custom_post_types() ) . "')" );

            if( $_post )
                $query->query_vars['error'] = '';
                $query->query_vars['post_type'] = $_post->post_type;
                $query->query_vars['name'] = $name;

// And next filter all the desired post types link.

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

function my_custom_post_types_permalink( $post_link, $post ){
    if( in_array( $post->post_type, my_custom_post_types() ) && !empty($post->post_name) && $post->post_status == 'publish' && '' != get_option('permalink_structure') )
        $post_name = $post->post_name;
        $post_link = home_url("/$post_name/");
    return $post_link;

EDIT: fixed errors, adding this line due to edit 6 chars minimum requirement

  • I actually have a lot of custom post types namely os_estate, os_villas, os_chalets, os_cottages - could you please modify your code to include all of them?
    – JoaMika
    Dec 9, 2013 at 23:12
  • thank you i will test this tomorrow and will let you know how it goes.. I also did notice that i could modify my theme directly.. is your solution in terms of performance and speed adequate?
    – JoaMika
    Dec 10, 2013 at 0:40
  • Exactly, you can see i am just using one query to find a match within defined custom post types. But if you use empty slug, that will either return 404 error, or if that returns fine, then post (default post type) page will be inaccessible. But with this solution, we leaving the rest of your site as it is. Thanks.
    – Shazzad
    Dec 10, 2013 at 22:01
  • That extra query you are using is not needed at all if you include the custom post type in the main query. Also, it is not needed to filter the post_type_link if you set the slug you want to use during the registration of the custom post type. See my answer.
    – cybmeta
    May 14, 2014 at 9:47

I think that you just need to put rewrite rule in your code in place where you register post type.

Just place this code

$rewrite = array(
    'slug'                => '',
    'with_front'          => true,
    'pages'               => true,
    'feeds'               => true,

after the $labels array();

and before $args = array();

  • 1
    This won't work. If you set the slug = '' it will be filled with the default value which is the post_type.
    – cybmeta
    May 14, 2014 at 9:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.