How to Change the Slug of a Post Type in WordPress

The slug of a post type is a string that identifies the post type in web addresses.

Problem

If URL rewriting is enabled for a post type, WordPress by default uses as slug the identifier of the post type. On the other hand, best practices recommend that post-type identifiers should be prefixed to avoid conflicts.

The result is that URLs generated for the post type look ugly and are not easy to remember. Consider a post type registered as 'ns_book_cpt':

Solution

Specify a new slug using the 'slug' member of the 'rewrite' attribute when the post type is registered in WordPress. For completeness and if it makes sense in your scenario, customize the 'query_var' attribute too.

Example

The following code registers the custom post type Book. The identifier of the custom post type is 'ns_book_cpt', but its slug is set to 'books' and the query variable is set to 'book'.

/*
 * Registers the custom post type Book.
 */
function ns_register_book_cpt(){
    
    // See all possible labels in the PHPDoc of the function get_post_type_labels
    $labels = array(
       'name'                  => __('Books', 'ns'),
       'singular_name'         => __('Book', 'ns'),
       'menu_name'             => __('Books', 'ns'),
       'all_items'             => __('All Books', 'ns'),
       'add_new'               => __('Add New', 'ns'),
       'add_new_item'          => __('Add New Book', 'ns'),
       'edit_item'             => __('Edit Book', 'ns'),
       'new_item'              => __('New Book', 'ns'),
       'view_item'             => __('View Book', 'ns'),
       'view_items'            => __('View Books', 'ns'),
       'search_items'          => __('Search Books', 'ns'),
       'not_found'             => __('No books found.', 'ns'),
       'not_found_in_trash'    => __('No books found in Trash.', 'ns'),
       'archives'              => __('Book Archives', 'ns'),
       'filter_items_list'     => __('Filter books list', 'ns'),
       'items_list_navigation' => __('Books list navigation', 'ns'),
       'items_list'            => __('Books list', 'ns')
    );
    
    // See all possible attributes in the PHPDoc of the function register_post_type
    $args = array(
        'label'                 => __('Books', 'ns'),
        'labels'                => $labels, 
        'public'                => true,
        'exclude_from_search'   => false,
        'publicly_queryable'    => true,
        'show_ui'               => true,
        'show_in_nav_menus'     => true,
        'show_in_menu'          => true,
        'show_in_admin_bar'     => true,
        'hierarchical'          => false,
        'supports'              => array('title', 'editor', 'author'), 
        'taxonomies'            => array('post_tag'),
        'has_archive'           => true, 
        'rewrite'               => array('slug' => 'books'),
        'query_var'             => 'book'
    );
    
    register_post_type('ns_book_cpt', $args);
}
add_action('init', 'ns_register_book_cpt', 10, 0);

Since the post type Book is registered specifying a custom slug, URLs generated for the post type will have the following form:

Issues

If you get a “Page not found” error when you try to visit a book or an entry of any other custom post type, apply this procedure:

  1. Open the administration area
  2. Go to the Settings menu
  3. Go to the Permalinks submenu
  4. Click the Save Changes button

Further reading

I recommend the other tutorials in this series to learn more about post types in WordPress.

Source code

The source code developed in this tutorial is available here.