I have a Url structure of http://domain.com/hospital/hospitalname (here hospital is custom post type and hospitalname is a registered business user) I want to change the Url structure to http://domain.com/cityname/hospitalname I have a taxonmy "cities" how should I change the permalink structure. If I use below code will city taxonmy is going to appear as a tag on custom structure or is there any simple alternate way to acheive this. Please advise.

function wpse_modify_taxonomy() {
    $city_args = get_taxonomy( 'cities' );
    'rewrite' => true,
    'hierarchical' => true 

    register_taxonomy( 'cities', (array) $city_args );

add_action( 'init', 'wpse_modify_taxonomy', 11 );

add_filter('post_link', 'cities_permalink', 10, 3);
add_filter('post_type_link', 'cities_permalink', 10, 3);

function cities_permalink($permalink, $post_id, $leavename) {
    if (strpos($permalink, '%cities%') === FALSE) return $permalink;

        // Get post
        $post = get_post($post_id);
        if (!$post) return $permalink;

        // Get taxonomy terms
        $terms = wp_get_object_terms($post->ID, 'cities');   
        if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) $taxonomy_slug = $terms[0]->slug;
        else $taxonomy_slug = '';

    return str_replace('%cities%', $taxonomy_slug, $permalink);
  • Anyone know the solution to my problem. Please advise
    – Emma Expat
    Commented Mar 23, 2020 at 8:44
  • or is it possible to just have this [ Url: domain.com/hospitalname ] to make it more simple
    – Emma Expat
    Commented Mar 23, 2020 at 9:15
  • Your code seems a little bit weird in wpse_modify_taxonomy(), something seems missing...the arguments is incorrect for the syntax...hospitalname is a post of hospital? cities is taxonomy of hospital? Commented Apr 30, 2020 at 14:02
  • hospitalname (clinicname or doctorname) is the registered User and cities is the taxonomy. How to just have simple Url : domain.com/hospitalname(I.e. user name)
    – Emma Expat
    Commented Apr 30, 2020 at 14:57
  • @simongcc any suggestions to resolve this issue.
    – Emma Expat
    Commented May 1, 2020 at 7:20

1 Answer 1


WordPress is a query based system, you need to first understand that if using permalinks and when a link http://domain.com/hospital/hospitalname is submitted, WP analyses it internally as

  • post type = hospital
  • name = hospitalname

And then create a query to look up. It is likewise for other links such as http://domain.com/cityname/hospitalname


Before a custom rewrite is going to work. It is assumed that you know and understand post type and taxonomy setup. Because by the time of a post type and taxonomy setup. The arguments determines how rewrite path and query_var is going to be setup.

If, in case, the post type and taxonomy is built by plugins, by someone else instead by yourself. It is necessary to find out, it could not be guessed because when it is setup, custom arguments is probably changing the default name. It is true in your case. It will waste time if you are trying to test by guessing and assuming the name is same as what it seems to be.

The following example is based on asker's information in the discussion. It could benefit anyone struggling in understanding rewrite.

find out what taxonomy exists

add_action( 'init', 'ws361150_debug_test' );
function ws361150_debug_test() {
    // please comment/uncomment the debug output needed

    // get an array list of available taxonomies name
    // var_dump(get_taxonomies());

    // get an array list of available taxonomies name => object pair
    // var_dump(get_taxonomies( [], 'objects'));

    // get a specific object (include assoicated post type name)
    // var_dump(get_taxonomy( 'towns' ));

// ... reference result
// ["countries"]=> string(9) "countries" 
// ["cities"]=> string(6) "cities" 
// ...

find out what post type the taxonomy(default or custom) is associated to

  • what is the rewrite tag
  • what is the query_var
// taxonomy example
// object(WP_Taxonomy)[1329]
//   public 'name' => string 'towns' (length=5) // taxonomy slug
//   ...
//   public 'public' => boolean true
//   public 'publicly_queryable' => boolean true // http://example/?xxxxx= is allowed

//   // post type associated with this taxonomy
//   public 'object_type' => 
//     array (size=1)
//       0 => string 'post' (length=4)
//   public 'rewrite' => 
//     array (size=4)
//    ...
//       'slug' => string 'towns' (length=5) // rewrite tag is %towns%
//   public 'query_var' => string 'towns' (length=5) // http://example/?towns=

// some taxonomy's arguments, it is better view it in formatted form
// the taxonomy slug is missed in the discussion, so not sure which slug it means, but probably countries because the query_var is countries
// ["object_type"]=> array(1) { 
//  [0]=> string(13) "sp_categories" // linked to post type sp_categories
// } 
// ["rewrite"]=> array(4) { 
//  ["slug"]=> string(7) "country" // rewrite is /%country%/
// } 
// ["query_var"]=> string(9) "countries" // means http://example.com/?countries= is allowed

// taxonomy 'cities' arguments
// ["cities"]=> object(WP_Taxonomy)#2622 (26) { 
//  ["name"]=> string(6) "cities" // slug is cities
// ... missed in the discussion

find out what is the settings of a custom post type

  • what is the rewrite tag
  • what is the query_var
add_action( 'init', 'ws361150_debug_test_pt' );
function ws361150_debug_test_pt() {
    // please comment/uncomment the debug output needed
    // export an array list of all post types list
    var_dump( get_post_types() );

    // debug view one post type object by the following method
    $post_types = get_post_types( [], 'objects' );
    var_dump($post_types['post']); // put in the post type slug

// object(WP_Post_Type)[963]
//   public 'name' => string 'post' (length=4) // post type slug
//   ...
//   public 'publicly_queryable' => boolean true
//   public 'query_var' => boolean false
//   public 'rewrite' => boolean false // for post it is off, the settings is similar to taxonomy, if slug is set the rewrite is /%specified_slug%/, if true, it is by default /%post_slug%/, so that by default 'post' is http://example.com/post-name/ instead of http://example.com/post_slug/post-name/

There are many methods to accomplish, here shows 2 methods, one is rewrite, and the other one is manually modifying the query. I have tested both methods with sample post types and worked perfectly by putting them into theme's functions.php Please don't put method1 and method2 at the same time for only one method is recommended for the purpose of a successful test. In method 1, add_permastruct and add_rewrite_rule are used.

  • add_permastruct - add an extra permalink structure in addition to the settings > Permalinks in WordPress, if the rewrite tag is already registered such as taxonomy and post type, it could be used to create custom URL structure
  • add_rewrite_rule - add custom rewrite regular expression to match query URL, in some cases, it is required to do custom rules to do the task.

Method 1: Rewrite

// objective: http://domain.com/cityname/hospitalname
// post type: 'hospital', assumed the rewrite tag is '%hospital%'
// taxonomy: 'cities', assumed the rewrite tag is '%cities%' and it is associated with hospital, otherwise, it will not work
add_action( 'init', 'ws365575_taxonomy_rewrite1' );
function ws365575_taxonomy_rewrite1() {
    // optional settings as the 3rd argument for add_permastruct
    // default
    $args = array(
        // 'with_front'  => true,
        // 'ep_mask'     => EP_NONE,
        // 'paged'       => true,
        // 'feed'        => true,
        // 'forcomments' => false,
        // 'walk_dirs'   => true,
        // 'endpoints'   => true,

    // this will need to flush the permalink cache to be effective
    // add to $wp_rewrite->extra_permastructs
    add_permastruct( 'cities_with_hospitals', "%cities%/%hospital%", $args );
// objective: http://domain.com/hospitalname
// post_type: 'hospital', assumed the query vars is '%hospital%'
// the top option tells WP to match it in higher priority before it is being regarded as not found
add_action( 'init', 'ws365575_taxonomy_rewrite2' );
function ws365575_taxonomy_rewrite2() {
    // the rule is copied from originally registered post types 'hopsital' part and modify by following page post type's rewrite

    add_rewrite_rule('(.?.+?)/page/?([0-9]{1,})/?$', 'index.php?hospital=$matches[1]&paged=$matches[2]', 'top');
    add_rewrite_rule('(.?.+?)?(:/([0-9]+))?/?$', 'index.php?hospital=$matches[1]&page=$matches[2]', 'top');

    // because the above will override the priority, to avoid page cannot be loaded and become 404, manually add once more
    add_rewrite_rule('(.?.+?)/page/?([0-9]{1,})/?$', 'index.php?pagename=$matches[1]&paged=$matches[2]', 'top');
    add_rewrite_rule('(.?.+?)?(:/([0-9]+))?/?$', 'index.php?pagename=$matches[1]&page=$matches[2]', 'top');

**After setup the above code, the permalink cache is needed to flush. Please refer to the following notes. **

To confirm the registered tag is right or not, may var_dump the $wp_rewrite global variables.

// the following is for optional for debug
add_action( 'init', 'ws365575_confirm_wp_rewrite' );
function ws365575_confirm_wp_rewrite() {
    global $wp_rewrite;

    // if xdebug is not installed, it is recommended to view in source

    exit(); // break the code to read

Method 2: Manually Modify Query (Advanced)

Now, if visiting the link http://domain.com/hospitalname and make it equivalent to http://domain.com/hospital/hospitalname with missing post type information, it needs some logic in order to analyse and work.

The filter request provides early control of any query and is suitable to do the job. While it works, please note that it depends on how well educate WP to analyse. So, a lot of test and thorough thinking must be done to different situations such as similar name, guess wrong, exception handling.

*** It won't blow the WP, at most, it returns 404 or wrong page. The following is tested on a custom post type by placing in theme's function.php

add_filter( 'request', 'ws361150_custom_request_query' );
function ws361150_custom_request_query( $query ) {
    if ( isset( $_SERVER['HTTP_HOST'] ) ) {
        // build the URL in the address bar
        $requested_url  = is_ssl() ? 'https://' : 'http://';
        $requested_url .= $_SERVER['HTTP_HOST'];
        $requested_url .= $_SERVER['REQUEST_URI'];

    if( url_to_postid( $_SERVER['REQUEST_URI']) > 0 ) {
        // if something is found priorly, nothing to do, suppose something exists

        return $query;
    } else {
        // must think of a logic and test thoroughly before production

        // manual setup the post query by guess            
        // check if rewrite slugs match original settings (spelling...etc)
        if( $search_ID = url_to_postid( 'hospital' . $_SERVER['REQUEST_URI']) > 0 ) {
            // setup query based on source code, if match by test

            // check if post type name match settings
            $query['post_type'] = 'hospital';
            $query['hospital'] = $query['name'];

            // var_dump($query);
            // exit();
            return $query;

    return $query; // untouched

It looks like a simple code and less overhead, it depends heavily on the testing logic supplied.

Reset permalink (flush the cache)

Reminder, whenever changing a rewrite slug or rule, need to flush the cache. The simplest way is going to settings > permalinks and click save. If not updating the cache, it will probably stick to old settings and waste time to test.

Additional work required after setup the URL

And then it is necessary to use the post_link and post_type_link hook to change the links for get_permalinks() or get_post_permalink() to get links. Then the links will be reflected on the frontend or post edit page because WP does not do it automatically.

  • Firtst method is giving error Parse error: syntax error, unexpected 'add_permastruct' (T_STRING) in C:\xampp\htdocs\hoospital\wp-content\themes\listingo-child\functions.php on line 46 and second method no changed obeserved where I have to add post_link hook do you have any sample code for that. var_dump($wp_rewrite); there is no tag with '%hospital%' However, I found '%cities%' there.
    – Emma Expat
    Commented May 3, 2020 at 19:27
  • Let's go to chat room for discussion Commented May 3, 2020 at 21:57
  • Can you send me chat invitation. I don't see chat link here.
    – Emma Expat
    Commented May 3, 2020 at 22:11
  • I just go to the same one as before Commented May 3, 2020 at 22:14
  • @EmmaExpat I have added explanation notes and methods on understanding post type and taxonomy setup and the relationship of the arguments to rewrite and query var. The explanations is based on our discussion. I believe this could help you understand the importance of post type and taxonomy arguments, how they setup. This could also benefits future readers. Hope it could help you not only in just one or 2 occasion. Understanding correctly could make your life much easier. Commented May 9, 2020 at 12:50

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.