WordPress Rewrites Without Duplicate Content

There are times when you need to surface content in a different context. For instance, you might have an events section and you want to have your related live-blog posts reside within the context of that specific event URI structure. The problem lies in surfacing the same content in the original location and in a rewritten location. Search engines frown on this. You can easily sidestep this and stay search engine friendly by setting up simple redirects.

The following presupposes the following:

  • A custom post type called ‘event’ with a consumer-facing URI segment of ‘events’
  • A custom taxonomy type called ‘events’ with a consumer-facing URI segment of ‘events’. This is used by both ‘post’ and ‘event’ post types.
  • The slug of the ‘events’ term matches the slug of the ‘event’ post.

There are four steps that you need to take to accomplish this.

Add a public query variable

add_filter('query_vars', 'add_event_post_query_var', 10);
function add_event_post_query_var($vars){
	$vars[] = 'event-post';
	return $vars;
}

We will use this query variable to indicate the post is being used in a different context.

Add a rewrite

add_rewrite_rule(
	'event/(.+?)/live-blog/(.+)/?$',
	'index.php?
		event-post=1& //Custom Query Var
		events=$matches[1]& //Tax Term
		post=$matches[2]', //
	'top'
);

Posts will live at: http://yourdomain.com/event/your-event/live-blog/your-post-title. The beauty of using a tax here is that you can easily locate the parent event page with the same slug as the term. A use case for this would be to surface event post meta onto the live blog page.

Update the post permalink

add_filter('post_type_link', 'event_post_permalink', 10, 4);
function event_post_permalink($post_link, $post, $leavename, $sample) {
	if($post->post_type === 'post'){
		$terms = wp_get_post_terms($post->ID, 'events');
		if($terms){
			//Only one term can be used, so grab the first
			$term = $terms[0]->slug;
			//Set the structure to match the rewrite
			$post_link = get_bloginfo('url')."/event/$term/live-blog/$post->post_name";
		}
	}
	return $post_link;
}

By setting the post link, whenever an events category is selected, the URL will automatically convert to the format of our rewrite rule. This happens on both the admin and consumer-facing sides. If your post appears in a loop elsewhere, it will always live in the event context as long as it contains an events term.

Add redirects to prohibit duplicate content

add_action('template_redirect', 'redirect_event_posts', 1);
function redirect_event_posts(){
	global $post;
	if($post->post_type === 'post'){

		//Indicator if this is from our rewrite
		$event_post = get_query_var('event-post');

		//At event location, check if needs to be redirect to origin location
		if($event_post){
			$event = get_query_var('events');
			if($event){
				//We don't want to surface the post here if it doesn't have the term
				if(!has_term($event, 'events', $post->ID)){
					//Let our permalink function handle building the URL
					$redirect = get_permalink($post->ID);
					wp_redirect($redirect, 301);
					exit();
				}
			}
		}

		//At origin location - Check if needs to be redirected to event location
		elseif(!$event_post){
			//If the the permalink indicates our custom rewrite structure, redirect it
			//The permalink function builds the proper URL
			$url = get_permalink($post->ID);
			if(preg_match('/live-blog/'), $url)){
				wp_redirect($url, 301);
				exit();
			}
		}
	}
}

Caveat

You might need to add a rewrite tag to account for editing the post slug on the post.php edit screen. Other than that, this approach is pretty solid to keep you from offending the search engine bosses.

Let me know your thoughts.