April 13, 2015 / Ben Huddleston

We recently had a client who needed archives of a custom content type by date within their WordPress website. While WordPress does a great job of setting up blog posts to be able to view an archive of them by date, there’s no documented manner in which to set up custom content types to have date based archives. Digging around the web eventually led me to a solution by fonglh that explained how to do it fairly simply: http://wpadventures.wordpress.com/2011/03/01/custom-post-type-archives-part-5/

Digging through the comments after having some issues with pagination eventually let to using this final code below. URL structure appears like this: /date/2015/03/page/2/?post_type_index=1&post_type=videos-on-demand

It isn’t perfect. I would prefer that the link structure be set up so it didn’t pass the parameters on the URL as a query string but rather as part of the URL but it’s functional.

function register_post_type_rewrite_rules($wp_rewrite) {
    $args = array('public' => true, '_builtin' => false); //get all public custom post types
    $output = 'names';
    $operator = 'and';
    $post_types = get_post_types($args, $output, $operator);
    $url_base = ($url_base == '') ? $url_base : $url_base . '/';
    $custom_rules = array();
    $post_types = implode('|', $post_types);
    $custom_rules = array(
      "$url_base($post_types)/([0-9]{4})/([0-9]{1,2})/?$" => 'index.php?post_type_index=1&post_type=' . $wp_rewrite->preg_index(1) . '&year=' . $wp_rewrite->preg_index(2) . '&monthnum=' . $wp_rewrite->preg_index(3), //year month
      "$url_base($post_types)/([0-9]{4})" => 'index.php?post_type_index=1&post_type=' . $wp_rewrite->preg_index(1) . '&year=' . $wp_rewrite->preg_index(2) //year

    // merge existing rules with custom ones
    $wp_rewrite->rules = array_merge($custom_rules, $wp_rewrite->rules);

    return $wp_rewrite;

  add_filter('generate_rewrite_rules', 'register_post_type_rewrite_rules', 100);


Daris, one of our developers came up with a better solution when working on a different project where we needed the same kind of capability. He succeeded in setting the date as part of the link structure. Adding support for year/month based archives for another content type is as simple as adding it to the $post_types array below. URL structure for this setup resembles: /newsletter/2015/01/

This certainly creates a more attractive URL.

// Add custom rewrite rules to handle things like years in custom post archives
add_filter('rewrite_rules_array', function ($rules) {

  $post_types = array('newsletter', 'foo');
  $line_separated = implode( '|', $post_types );
  $newrules = array();

  $newrules['(' . $line_separated . ')/(\d*)$'] = 'index.php?post_type=$matches[1]&m=$matches[2]';
  $newrules['(' . $line_separated . ')/(\d*)/(\d*)$'] = 'index.php?post_type=$matches[1]&m=$matches[2]$matches[3]';
  $newrules['(' . $line_separated . ')/(\d*)/(\d*)/(\d*)$'] = 'index.php?post_type=$matches[1]&m=$matches[2]$matches[3]$matches[4]';
  $newrules['(' . $line_separated . ')/(\d*)/page/(\d*)$'] = 'index.php?post_type=$matches[1]&m=$matches[2]&paged=$matches[3]';
  $newrules['(' . $line_separated . ')/(\d*)/(\d*)/page/(\d*)$'] = 'index.php?post_type=$matches[1]&m=$matches[2]$matches[3]&paged=$matches[4]';
  $newrules['(' . $line_separated . ')/(\d*)/(\d*)/(\d*)/page/(\d*)$'] = 'index.php?post_type=$matches[1]&m=$matches[2]$matches[3]$matches[4]&paged=$matches[5]';

  return $newrules + $rules;


Posted In: Blog, Programming, WordPress