Is it possible for a custom post type to have a permalink as domain.com/custom-slug/ instead of domain.com/custom/custom-slug/?

I can't seem to achieve it. rewrite argument while registering it either defaults to the latter one or a custom one by using 'rewrite' => array( 'slug' => 'blah-blah' )

  • by "custom-slug" you mean the post's slug?
    – Bainternet
    Commented Mar 1, 2011 at 10:56
  • @Bainternet exactly, I want it to be like how a WP page is
    – Ashfame
    Commented Mar 1, 2011 at 10:58
  • @Ashfame, were you able to get this figured out? I'm trying to accomplish the same myself now. Commented Jan 22, 2012 at 20:30

3 Answers 3


Take a look at my answer to the following question. You can modify my parse_request() to get what you need:

And you can read the following to see why what you want can cause issues in WordPress and requires a complex solution:


Based on the comment I decided to go ahead and implement this as the next revision of the plugin. It's really only tested for the use-case for this question and the one from the prior referenced question. Over time I plan to implement it for all potential use-cases, as questions or clients occur to drive the demand.

Here is the code you'd place in your theme's functions.php file to set it up for the post_type 'custom':

function init_url_routes() {
  $post_type = 'custom'; // Change this to your actual post_type name

And here is the plugin which you can place in /wp-content/mu-plugins/:

Filename: wp-extended.php
Plugin Name: WP Extended for Taxonomy URL Routes
Author: Mike Schinkel
Version: 0.2
function register_url_route($args=array()) {
class WP_Extended extends WP {
  static $root = array();
  static function on_load() {
  static function register_url_route($args) {
    if (isset($args['taxonomy']))
      self::$root['taxonomy'][$args['taxonomy']] = get_taxonomy($args['taxonomy']);
    if (isset($args['post_type']))
      self::$root['posts'][$args['post_type']] = get_post_type_object($args['post_type']);
  static function setup_theme() { // Setup theme is 1st code run after WP is created.
    global $wp;
    $wp = new WP_Extended();  // Replace the global $wp
  function parse_request($extra_query_vars = '') {
    $path = $_SERVER['REQUEST_URI'];
    $domain = str_replace('.','\.',$_SERVER['SERVER_NAME']);
    $root_path = preg_replace("#^https?://{$domain}(/.*)$#",'$1',WP_SITEURL);
    if (substr($path,0,strlen($root_path))==$root_path)
      $path = substr($path,strlen($root_path));
    list($path) = explode('?',$path);
    $path_segments = explode('/',trim($path,'/'));
    // This does not handle ordering priority of type to match yet
    $matched = $this->parse_post_type_request($path_segments);
    if (!$matched)
      $matched = $this->parse_taxonomy_request($path_segments);
    if ($matched) {
      // This is hamfisted but necessary in some cases.
      // TODO: Look into ways to have more finesse with this.
    } else {
      parent::parse_request($extra_query_vars); // Delegate to WP class
  function parse_post_type_request($path_segments) {
    // This does not handle heirarchical pages yet
    $post_id = false;
    global $wpdb;
    $sql =<<<SQL
  AND post_status='publish'
  AND post_type='%s'
  AND post_name='%s'
    if (is_array(self::$root['posts'])) {
      foreach(self::$root['posts'] as $post_type => $post_type_object) {
        $sql = $wpdb->prepare($sql,$post_type,$path_segments[0]);
        $post_id = $wpdb->get_var($sql);
        if ($post_id) {
          $this->query_vars[($post_type=='page' ? 'page_id' : 'p')] = $post_id;
          unset($path_segments[0]);  // Remove from future consideration
    return ($post_id);
  function parse_taxonomy_request($path_segments) {
    $taxonomy_term = array();
    $parent_id = 0;
    if (is_array(self::$root['taxonomy'])) {
      foreach(self::$root['taxonomy'] as $taxonomy_slug => $taxonomy) {
        $terms = get_terms($taxonomy_slug);
        foreach($path_segments as $segment_index => $path_segment) {
          foreach($terms as $term_index => $term) {
            if ($term->slug==$path_segment) {
              if ($term->parent!=$parent_id) { // Make sure we test parents
                $taxonomy_term = array();
              } else {
                $parent_id = $term->term_id; // Capture parent ID for verification
                $taxonomy_term[] = $term->slug; // Collect slug as path segment
                unset($terms[$term_index]); // No need to scan it again
              unset($path_segments[$segment_index]);  // Remove from future consideration
        if (count($taxonomy_term))
      if (count($taxonomy_term)) {
        $path = implode('/',$taxonomy_term);
        switch ($taxonomy_slug) {
          case 'category':
            $this->query_vars['category_name'] = $path;
          case 'post_tag':
            $this->query_vars['tag'] = $path;
            $this->query_vars['taxonomy'] = $taxonomy_slug;
            $this->query_vars['term'] = $path;
    return count($taxonomy_term);
  • Hi @Mike I am aware of the permalinks performance issue. For this particular project, the site won't exceed 20 pages, so its totally acceptable for me. And regarding your first link, it totally went way over my head. I will try reading it again. Thanks for the great answers! :)
    – Ashfame
    Commented Mar 1, 2011 at 17:29
  • @Ashframe - Try the code in your test site and see how it enables categories in the root. That might inform your understanding? As for the performance issue, I usually answer as much for future readers who may not know about something as I do for the OP (you in this case.) Commented Mar 1, 2011 at 17:40
  • @Ashframe - I added code for you to try. Commented Mar 1, 2011 at 18:54
  • @MikeSchinkel - I'm attempting to use this code for the same use-case. I'm not quite getting it.. How can this be used to strip out the post type slug from the permalink? Commented Jan 22, 2012 at 20:33

To completely remove the slug so that the URL structure would look like:


in your register_post_type set

'rewrite' => array('slug' => false, 'with_front' => false)

hope this helps

  • 1
    Didn't work for me, does it work for you? I mean have you tested it, that it works?
    – Ashfame
    Commented Mar 1, 2011 at 12:02
  • Yes it works if your permalink structure is/%postname%/
    – Bainternet
    Commented Mar 1, 2011 at 13:44
  • Sorry didn't work for me in a fresh 3.1 install
    – Ashfame
    Commented Mar 1, 2011 at 17:29
  • I can confirm that this does not work.
    – Mike
    Commented Feb 1, 2012 at 18:41
  • @Bainternet do you have any ideas on this now at WP 4.3+ ? Commented Sep 28, 2015 at 16:12

Although it hasn't been updated for 2 years, the below plugin worked for me: http://wordpress.org/plugins/remove-slug-from-custom-post-type/

FYI I'm running WP 3.9.1 with WP Types 1.5.7

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.