Page permalink conflict with Date

First of all rewrite handling can become very complicated very quickly – particularly when your desired structure conflicts with WordPress’ default behaviour. The best advice is probably to just avoid such conflicts rather than try to resolve them. With that as a premable…

WordPress generates rewrite rules from various sources: from registered post types, rules that plug-ins manually add and also ‘tags’ (default / plug-in added). Tags are the %year%, %author% etc that you can use in your custom permalink structure. It also has rewrite rules that it automatically generates: These include date structures (see source). e.g.

  www.example.com/2012 => www.example.com?year=2012
  www.example.com/2012/05 => www.example.com?year=2012&monthnum=05

These rules are more specific than page rules, since they only apply to (in regex) ([0-9]{4}) – that is 4 digit numbers like 2012. (The second rule only applies to ([0-9]{4})/([0-9]{2}) e.g. 2012/05.

There are two solutions to this:

  • Change the rule for the %year% tag
  • Change the date rewrite rules

Change rule for the %year% tag

As mentioned above, the %year% tag expects a 4-digit number – and the corresponding rule only appears when this is met. You can change this rule so it only matches date/([0-9]{4}) – that is, it matches date/[four-digit-number]. To do that:

add_action('init','wpse56448_change_year_tag');
function wpse56448_change_year_tag(){
     add_rewrite_tag('%date%','(date/[0-9]{4})');
}

Warning: This means whenever the %year% tag is used (say in your custom permalink structure) – the year in the permalink must be preceded by date/. Notable changes include

  www.example.com/2012/05

no longer points to May 2012 archives. Instead you must use:

  www.example.com/date/2012/05

(similarly for the date archive).

For this reason, you might prefer to use method 2.


Change the date rewrite rules

When the date rewrite rules are being generated they are filtered (see source) using the date_rewrite_rules filters.

More specifically an array of the form regex=>query is filtered. An array where the key is a regular expression, and the value is how it is interpreted as a query). E.g.:

Array
(
    [([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]
    [([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]
    [([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]
    [([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]
    [([0-9]{4})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]
    [([0-9]{4})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]
    [([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]
    [([0-9]{4})/([0-9]{1,2})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]
    [([0-9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&feed=$matches[2]
    [([0-9]{4})/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&feed=$matches[2]
    [([0-9]{4})/page/?([0-9]{1,})/?$] => index.php?year=$matches[1]&paged=$matches[2]
    [([0-9]{4})/?$] => index.php?year=$matches[1]
)

It’s this last rule that is causing the problem. So you can remove it, and then add a replacement (if desired):

add_filter('date_rewrite_rules','wpse56448_date_rewrite_rules');
function wpse56448_date_rewrite_rules( $rules ){
    //Remove year rewrite rule:
    unset($rules['([0-9]{4})/?$']);

    //Replace it with something else
    $rules['year/([0-9]{4})/?$'] = 'index.php?year=$matches[1]';
    return $rules;
}

Now

  www.example.com/2012

goes to your page. And

  www.example.com/year/2012

points to the year archives. While the month archives remain unchanged. e.g.:

  www.example.com/date/2012/05

still points to May 2012 archive.

Warning: Clearly you can see another conflict arising if your 2012 page has a sub-page called ’05’. Similar problems will occur if you use ‘page’, ‘feed’, ‘atom’ etc (see rewrite rules above). This method would require you to manually remove/replace each of the rules that need altering. This can get messy quickly – which is why its best just to avoid using these as page names.


Summary

First of all: any changes won’t take effect until you flush the rewrite rules. Do this (only) by going to Settings > Permalinks and clicking save.

Method 2 is probably preferable – given that method 1 may do ‘too much’. However, method 2 is a bit ‘dirtier’ – you’re ‘fine tuning’ the date permastructure and you may find that you need to tune it further.

For instance, using method 2, if your 2012 page had a sub-page 05 – it’s permalink would be be interpreted as the May 2012 archive. You would then need to alter the rewrite-rules for month archives. Going three-levels deep, with a page called ’01’ you will get the 1st May 2012 archive. At each level you also have problems if you have pages called ‘page’, or ‘feed’ and such. As mentioned, its probably best to try and avoid such names.

Leave a Comment