Take the 2-minute tour ×

I created a custom post type, of which the (simplified) arguments are:

    'public' => true,
    'rewrite' => array( 'slug' => 'eventy'),
    'has_archive' => false,
    'hierarchical' => false

It works fine in general. However if I try to rewrite the URLs in functions.php, for example:

function my_post_type_link_filter_function( $post_link, $id = 0, $leavename = FALSE ) {
   return str_replace('eventy', 'eventx', $post_link);
add_filter('post_type_link', 'my_post_type_link_filter_function', 1, 3);

The result is that the new 'eventx' URLs appear correctly in links, lists, etc, generated by WordPress. But when I go to those 'eventx' URLs I get a 404. Going to an 'eventy' URL still works.

I've tried changing the permalinks settings to default and back to post name, many times. This is supposed to reset permalinks and fixes this issue for many people I've seen online. Not for me.

Anyone know what the cause might be? How do I go about troubleshooting this? I'm weak on the theory of URL rewriting.

If you're interested, my purpose is not the trivial eventy → eventx substitution mentioned above. I actually want to slip in the year/month an event starts (not when it was was published) into its URL. Debugging got me to realise I had a more basic issue as even this trivial x → y rewrite still throws 404s.

Thanks for reading!

[clarification added]

At the moment I have say three events (posts in my custom post type) called say "Fishing trip". These get assigned permalinks by default like

  • mysite.com/events/fishing-trip
  • mysite.com/events/fishing-trip-2
  • mysite.com/events/fishing-trip-3

Which is boring and inelegant.

I want instead

  • mysite.com/events/2013/january/fishing-trip
  • mysite.com/events/2013/may/fishing-trip
  • mysite.com/events/2013/may/fishing-trip-2
  • mysite.com/events/2014/march/fishing-trip

Prettier and more informative.

I'm not interested in having category-style pages like

  • mysite.com/events/2013

The year and month is just cosmetic in the URL.

share|improve this question
Filtering 'post_type_link' is simply changing the URLs WordPress generates when it outputs a permalink for your post type. When you visit one of those URLs, WordPress doesn't know what to do with them or what they're supposed to point to, as you haven't done anything to generate rewrite rules for incoming requests. Without knowing what format you want for your links, it's not possible to give an answer beyond pointing you to add_rewrite_rule and add_rewrite_tag. –  Milo Jan 29 '13 at 1:45
thanks @milo. What i'm after is: –  Jonathan Jan 29 '13 at 18:14
thanks@milo post edited to add more info about what URLs I want –  Jonathan Jan 29 '13 at 18:18

2 Answers 2

up vote 2 down vote accepted

Step 1, add the rewrite tags for custom event year and month query vars, then register the event post type with those tags in the slug argument of the rewrite argument:

function wpa83531_register_event_post_type(){


    register_post_type( 'event',
            'public' => true,
            'rewrite' => array( 'slug' => 'events/%event_year%/%event_month%' ),
            'has_archive' => false,
            'hierarchical' => false,
            'supports' => array('custom-fields', 'title', 'editor')

add_action( 'init', 'wpa83531_register_event_post_type' );

Step 2, filter the post type link to replace those tags with values from custom fields event_year and event_month. If the custom fields don't exist, some default values are inserted so you can at least preview a post without error:

function wpa83531_event_post_link( $permalink, $post, $leavename ) {
    if ( stripos( $permalink, '%event_year%' ) == false )
        return $permalink;

    if ( is_object( $post ) && 'event' == $post->post_type ) {

        $default_year = '1970';
        $default_month = 'january';

        if( $event_year = get_post_meta( $post->ID, 'event_year', true ) ){
            $permalink = str_replace( '%event_year%', $event_year, $permalink );
        } else {
            $permalink = str_replace( '%event_year%', $default_year, $permalink );

        if( $event_month = get_post_meta( $post->ID, 'event_month', true ) ){
            $permalink = str_replace( '%event_month%', $event_month, $permalink );
        } else {
            $permalink = str_replace( '%event_month%', $default_month, $permalink );


    return $permalink;

add_filter( 'post_type_link', 'wpa83531_event_post_link', 10, 3 );
share|improve this answer
Thanks Milo that did it. –  Jonathan Jan 31 '13 at 18:25

You have to set 'rewrite' => false in your register_post_type(); and add after closing register_post_type() :

// 404 CPT WP bug...

global $wp_rewrite;
$projet_structure = '/projets/%projet%';
$wp_rewrite->add_rewrite_tag("%projet%", '([^/]+)', "projet=");
$wp_rewrite->add_permastruct('projet', $projet_structure, false);

Where projet is my CPT.

You can check here for more.

share|improve this answer

Your Answer


