I have several posts on my wordpress site. Most of them belong to a specific category, but some don't.

For all the posts that belong to a category I want to have the following permalink structure: /%category%/%postname%/

All the other default posts belong "automatically" to the uncategorized category. Their URL structure will look like this /uncategorized/%postname%/

I want to change permalinks to the following: /%postname%/

Is there a way to make an exception for the permalinks that belong to the uncategorized category?

Thanks in advance!

1 Answer 1


Adding this kind of rewrite rule is not a problem, you have to add only this code.

 * Add custom rewrite rule to handle posts with and without category prefix.
 * Remember to flush rewrite rules to apply changes.
function wpse_288675_add_post_handling_without_category_prefix() {

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

These rewrite rules are working on when I set my permalink structure to /%category%/%postname%/.

Problem is that your content will be available under two links. With %category% prefix and without. To prevent duplicate content which is bad for SEO we must redirect using 301 code all not uncaterogized post to url with category and all uncaterogized post to url without category. We can achive this using code below.

 * Check if displayed post have category and which category to maybe redirect user
 * with 301 code to prevent duplicate content.
function wpse_288675_maybe_redirect( $query ) {

    // Define uncaterogized category id for convenience
    if( ! defined( 'UNCATEROGIZED_CATEGORY_ID' ) ) {
        define( 'UNCATEROGIZED_CATEGORY_ID', 1 );

    // Do not parse request on some conditions
    if( !is_admin() && $query->is_main_query() && $query->is_single() ) { 

        // Get post name and category from url
        $slug          = $query->get('name');
        $category_name = $query->get('category_name');

        $query = new WP_Query(array(
            'post_type' => 'post',
            'name' => $slug,

        if( $query->have_posts() ) {

            // Get current post
            $posts = $query->get_posts();
            $post = current($posts);

            // Get post current first category
            $categories = get_the_category( $post->ID );
            $category   = current($categories);

            // If there is no category in url redirect
            // all not uncaterogized post to url with category
            if( !$category_name && $category->term_id !== UNCATEROGIZED_CATEGORY_ID ) { 
                wp_safe_redirect( get_permalink( $post->ID ), 301 );

            // If there is category in url redirect
            // all uncaterogized post to url without category
            if($category_name && $category->term_id === UNCATEROGIZED_CATEGORY_ID ) { 

                $url_format = '%s/%s/';
                $url = sprintf($url_format, get_bloginfo('url'), $slug);

                wp_safe_redirect( $url, 301 );

    return $query;

add_filter( 'pre_get_posts', 'wpse_288675_maybe_redirect' );

I will describe how this code is working on example. I have two posts:

  • Test 1 with category Uncaterogized
  • Test 2 with category Category 1

When I get to /uncaterogized/test-1/ I'm redirecting to /test-1/. When I get to /category-1/test-2 I'm not redirected.

  • /al/test.html/ is okey i required this. but i want to remove "uncategorized" from this url /uncategorized/test.html/
    – mukesh
    Dec 15, 2017 at 10:38
  • This code should remove it, if your Uncaterogized category ID is equal 1. If not you must change UNCATEROGIZED_CATEGORY_ID constant to your Uncaterogized category ID. I have two posts Test 1 with catgory Uncaterogized and Test 2 with category Category 1. If I want to get to get to /uncaterogized/test-1 I'm redirecting to /test-1 if I want to get to /category-1/test-2 I'm not redirected.
    – kierzniak
    Dec 15, 2017 at 10:44
  • yes it removed the Uncaterogized from url but page not found error comes.
    – mukesh
    Dec 19, 2017 at 5:27
  • this line abort the execution (!is_admin() && $query->is_main_query() && $query->is_single()) when i removed it then it gives us this error Fatal error: Maximum function nesting level of '256' reached, aborting! in D:\wampserver\www\htmlweb\wp-includes\class-wp-query.php on line 521
    – mukesh
    Dec 19, 2017 at 5:32
  • You have to fit this line to your needs, for my example I want to execute code only when we are not in admin, current query is main query and current displayed page is single post page. This statement must exist to prevent loop.
    – kierzniak
    Dec 19, 2017 at 12:03

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.