I'm trying to filter posts in a category by the year of the post's date. I also want to do this without being redirected to the year template so my ideal URL would be http://example.com/category/reports/2011/ which would load the template file category.php where I could then use query_posts to include only the posts that were published in 2011 and are in the reports category.

Here is my rewrite rule code...

function filter_category_by_year_rewrite_rules( $wp_rewrite ) {
    /* Creates rewrite rules for filtering category archives by year (/category/reports/2011/)*/
    $new_rules = array(
        "category/(.+?)/(\d\d\d\d)/?$" => "index.php?category_name=" . $wp_rewrite->preg_index(1) . "&year=" . $wp_rewrite->preg_index(2)
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
add_action('generate_rewrite_rules', 'filter_category_by_year_rewrite_rules');

My problem is WordPress automatically redirects index.php?category_name=reports&year=2011 to /category/reports/ with no trace of the year parameter. How can I intercept this redirect?

I tried hooking in to the template_redirect action to no avail :(

function testing_redirect() {
    global $wp;
    if($wp->request['matched_rule'] == 'category/(.+?)/(\d\d\d\d)/?$') {
        load_template( 'category.php' ); //TEMPLATEPATH .'/category.php';
add_action('template_redirect', 'testing_redirect', 1);

2 Answers 2


This should work:

add_action( 'init', 'wpa12742_init' );

function wpa12742_init(){
  add_rewrite_rule( 'category/(.+?)/(\d{4})/?$', 'index.php?category_name=$matches[1]&year=$matches[2]', 'top' );
  add_rewrite_rule( 'category/(.+?)/(\d{4})/page/(\d+)/?$', 'index.php?category_name=$matches[1]&year=$matches[2]&paged=$matches[3]', 'top' );


On second thought, that's not enough, since you'll get caught by redirect_canonical().

Add this too:

add_filter( 'term_link', 'wpa12743_term_link', 10, 3 );

function wpa12743_term_link( $link, $term, $taxonomy ){
  if('category' != $taxonomy && !preg_match( '@^\d{4}$@', get_query_var('year') ) )
    return $link;
  return trailingslashit( $link ) . get_query_var( 'year' );
  • You're right, canonical_redirect was the problem. See my answer below this one for how I solved it. Did you mean to filter term_link and not redirect_canonical? I kind of like you're way better for the trailing slash.
    – kingkool68
    Mar 22, 2011 at 19:16
  • Cool! I did mean to filter term link, but your method works just fine. :) Mar 22, 2011 at 19:35

Ahhh redirect_canonical was the culprit. Here's how I weeded this bugger out...

function kill_canonical_redirect($redirect_url, $requested_url) {
    global $wp;
    if($wp->matched_rule == 'category/(.+?)/(\d\d\d\d)/?$') { 
        return false;
    } else {
        return $redirect_url;
add_filter('redirect_canonical', 'kill_canonical_redirect', 10, 2);

Basically I look at the rewrite rule that matched the page request and if it is the one I wrote then I know I can kill the canonical redirect by simply returning false.

Perhaps the more helpful info for anyone reading over this is how I figured this out. I installed the Better HTTP Redirects plugin ( http://hakre.wordpress.com/2011/03/20/how-to-debug-redirect-problems-in-wordpress/ ) which has a really handy redirect debugging tool when WP_DEBUG is set to true in wp-config.php.

From there I realized the redirect was being triggered from Line 367 of /wp-includes/canonical.php so after looking at the source code on Trac ( http://core.trac.wordpress.org/browser/trunk/wp-includes/canonical.php#L367 ), I figured out how to kill the canonical redirect.


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.