I'm adding a custom post type like so (names have been altered to protect the innocent):

add_action('init', 'my_post_types');
function my_post_types() {

      'labels' => array(...),
      'rewrite' => array(
        'slug' => 'publications/%year%',
        'with_front' => true
      'public' => true,
      'menu_position' => 21,
      'supports' => array('title', 'editor', 'author', 'excerpts', 'revisions', 'thumbnail'),
      'show_ui' => true,

I would expect with these settings that the URL of a given publication would be:


However, the URLs produced by get_permalink() are coming out as:


The desired URL pattern is recognised by WordPress, but simply redirects to the lower URL. This URL does display the desired page.

I've tried the commonly suggested solutions:

  • Making sure that register_post_type is called on the init action, so it isn't created too late.
  • Flushing the rewrite rules by visiting the Permalinks settings page. I also tried doing this on every page using $wp_rewrite->flush_rules(), but that would be pointlessly expensive in reality.
  • Changing the value of with_front has no effect. I wouldn't expect it to since the site is at /, not in a subfolder.
  • Taking out the %year% part of the slug has no effect either, so it isn't causing the rewrite rules to fail.
  • I don't have the Redirection plugin installed. It's a known troublemaker with URLs.

If necessary, I can intercept WordPress' routing and force it to deliver the right page, and use filters to return the right URL from get_permalink, but that seems like the wrong solution. Setting the rewrite rules when I register the post type is the right solution. Is there something I'm doing wrong?

1 Answer 1


The undesirable answer I alluded to in my question is to intercept WordPress' routing and URL generation. Manipulate the URLs with:

add_filter('post_type_link', 'my_post_link', 10, 3);
function my_post_link($permalink, $post, $leavename) {
  if ($post->post_type == 'publication') {
    $year = date('Y', strtotime($post->post_date));
    return site_url("/publications/$year/$post->post_name");
  return $permalink;

Then intercept the routing:

add_action('template_redirect', 'my_template_redirect');
function my_template_redirect() {
  if (preg_match('!^/publications/([0-9]+)/([a-z0-9-]+)/?!i', $uri, $matches)) {
    $year = $matches[1];
    $publication = $matches[2];

    $ps = get_posts(array(
      'post_type' => 'publication',
      'year' => $year,
      'name' => $publication,
      'numberposts' => 1,
    if (!empty($ps)) {
      global $post;
      $post = array_shift($ps);

      global $wp_query;
      $wp_query->is_404 = false;


This works, but it's ugly and nasty!. There are other ways of doing it that involve working with WordPress' rewrite table, but those are also less than ideal. I'd like to find a nicer solution if possible.

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.