I recently ha a use case where I needed to prevent a certain category from being displayed in the front-end. In detail I needed to exclude posts assigned to that category from

  • the main query (“the Loop”)
  • search queries
  • category queries
  • archive queries
  • post navigation (next/prev_post_link())

and the category name itself from being displayed as item of

  • the_category() in theme templates
  • the Categories Widget (list and drop-down)

As a consequence I pretty soon found myself on a quest the results of which I’d like to share here and expose to further feedback.

1 Answer 1


Excluding categories with pre_get_posts()

I found that excluding a category via pre_get_posts() and set_query_var() would work fine except for widgets. The Recent Post Widget would only exclude the category when using $query->set() instead.

 * Does NOT apply to the Recent Posts widget. 
function glck1403271109_exclude_categories( $query ) {

    $excluded = array( '1', '2' );

    if( ! is_admin() )
        set_query_var( 'category__not_in', $excluded );
add_filter( 'pre_get_posts', 'glck1403271109_exclude_categories' );

 * Does apply to the Recent Posts widget. 
function glck1403271122_exclude_categories( $query ) {

    $excluded = array( '1', '2' );

    if( ! is_admin() )
        $query->set( 'category__not_in', $excluded );
add_filter( 'pre_get_posts', 'glck1403271122_exclude_categories' );

Excluding categories from queries and widgets

Excluding categories from queries and widgets took me a couple of functions I wrapped into a mini plugin. The code can be found in a Gist here.

Excluding categories from post navigation (next/prev_post_link())

next/prev_post_link() and their underlying get_ functions all rely on get_adjacent_post() which up to today (WordPress 3.9.1) does not use WP-Query, but fixes its own SQL query. Trac Ticket #26937 aims to get get_adjacent_post() to use WP_Query, but it might take a while until we’re there.

Excluding categories from next/prev_post_link() in my case could sufficiently be achieved passing category IDs to be excluded directly to the function. I’m sure this could also be down from a plugin interacting with get_adjacent_post() directly. Here’s my take for the template tag. Checking for glckprss_exclude_categories__category_names() from the previously mentioned mini plugin, of course, makes only sense when the latter is in use.

 * Exclude categories from prev/next post links.
 * $exclude  (array) - category slugs to retrieve IDs from
 * $excluded (array) - category IDs to be excluded
$exclude  = array();
$excluded = array();

// Mini plugin active?
if( function_exists( 'glckprss_exclude_categories__category_names' ) ) {

    $exclude = glckprss_exclude_categories__category_names();

else {

    $exclude  = array(
            get_category_by_slug( 'my-category' ),
            get_category_by_slug( 'my-other-category' )

// Retrieve IDs
foreach( $exclude as $category ) {

    if( $category )
        $excluded[] = absint( $category->term_id );

/* Next Post */
next_post_link( '%link', '%title', false, $excluded );

/* Previous post */
previous_post_link( '%link', '%title', false, $excluded );


For my use case it wasn’t necessary to exclude categories from menus as I had custom menus only, so I haven’t investigated in that direction. I’m pretty sure pre_get_posts() gets a grip on menu items, though, doesn’t it?

Thanks for reading all this, appreciate any comments!

  • 4
    Awesome for sharing. One minor improvement; you can simply use your second pre_get_posts filter and it'll work for both. set_query_var only works for the main loop, but $query->set will always work on the current query (including the main one!). May 17, 2014 at 13:57
  • 1
    @TheDeadMedic That’s the point I was trying to make, yes. ;) May 17, 2014 at 17:36

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.