Hiding WordPress categories

Photo by John Poyntz Tyler
Photo by John Poyntz Tyler

When I wrote my first WordPress related post, I admitted that I was only doing it to attract traffic and it would be my last post on the subject. However, I start again. This time around, however, I want to talk about something which isn’t common knowledge and neither did I get any responses on the official WordPress forum regarding this.

Suppose you do not want some of your posts to appear anywhere: not the homepage, not the RSS feeds, not the archives: nowhere. However you DO want it to appear only when its linked to, as a single post on the page. I regularly need to do this, because some part of the post is more like an ‘addendum’ or when including everything would make the post too long.

There is a standard solution available on the forums: creating a plugin and adding code to this effect:

function hs_cat_exclude($query)
if ($query->is_feed || $query->is_home || $query->is_archive ) {
$hsq = $query->gt;get('cat');
if (!isset($hsq)) {
$hsq = '-22';
else {
$hsq = $hsq . ",-22";
return $query;


Here, 22 is the category number of the category I wanted to exclude.

This code works fine, but the moment you add is_category to the ‘if‘ clause, it doesn’t work for the category page. This was perplexing to me, and I did not understand it. I spent a long time and then decided to dig deeper. I found out that the ‘wiring’ is faulty (this is what I believe). It can’t work like this for the category page. What is needed additionally is something like this:

function hs_cat_exclude_cat($where)
global $wp_query;
if ($wp_query->is_category) {
$where = $where . " AND NOT EXISTS(SELECT 1 FROM wp_term_relationships WHERE wp_term_relationships.object_id=wp_posts.id AND wp_term_relationships.term_taxonomy_id='22')";
return $where;


So far so good. What I wanted over and above this though, is for the category to not even appear on the category widget. I tried to find a way to get this done through the plugin but it did not work. Ultimately I had to ‘hack’ one of the core files to achieve this. If anyone knows of a better way to accomplish this, please add a comment. The change is to wp-includes/widgets.php. Find the line of code that looks like:

$cat_args = array('orderby' => 'name', 'show_count' => $c, 'hierarchical' => $h);

added an ‘exclude’ clause like this:

$cat_args = array('orderby' => 'name', 'show_count' => $c, 'hierarchical' => $h, 'exclude' => 22);

Thats all there is to it.

Update Jan 12th 2010: Since WP 2.8 I believe (I noticed the problem in 2.9.1), the last change above needs to be done to default-widgets.php rather than widgets.php


Licensing and information about the blog available here.