Occasionally you run across a situation where a website permalink structure needs to be changed from what it has been to a new structure. Most sites that have been public will have links to them from other websites that need to properly redirect to the new permalink structure. Removing an element from the permalink structure and redirecting old links is trivial, however adding an additional element to the url structure can be difficult. For example:
A fairly typical structure is: /%year%/%month%/%day%/%postname%/
Lets use a simple example of adding the category to the permalink structure: /%category%/%year%/%month%/%day%/%postname%/
To provide a more useful example:
Original: https://ben.lobaugh.net/2016/05/15/cruising-the-san-juans
With category: https://ben.lobaugh.net/sailing/2016/05/15/cruising-the-san-juans
The original url is going to display the 404 page. I am going to provide an example that you can use in your own site to capture the url causing the 404 and attempt to locate the new post permalink. If none can be found it will fallback to the 404 page. As long as there is something in the original url you can use to locate a post (like the %postname%) this method will work with minor tweaks.
add_action( 'template_redirect', 'maybe_redirect_404_old_permalink' );
/**
* Attempts to forward old permalinks to the new permalink structure
*
* @author Ben Lobaugh
*/
function maybe_redirect_404_old_permalink() {
// Only run this function if we are on a 404
if( ! is_404() ) {
return;
}
// "trick" to get the full URL
$url = add_query_arg( '', '' );
/*
* Pull the URL path apart to find a slug (post_name)
* The final segment should be the slug
*/
$parts = explode( '/', $url );
$parts = array_filter( $parts );
$size = count( $parts );
$maybe_slug = $parts[ $size ]; // We use size here because the filter turned 1 based
// Attempt to locate corresponding post in the database
$args = array(
'name' => $maybe_slug,
'post_type' => 'post',
'post_status' => 'publish',
'numberposts' => 1,
);
$posts = get_posts( $args );
// Identify a found post
if( $posts && ! empty( $posts[0]->ID ) ) {
$post_id = $posts[0]->ID;
$post_url = get_permalink( $post_id );
// Attempt to forward to the new post permalink
if( $post_url ) {
wp_safe_redirect( $post_url, 301 ); // Permanent redirect
}
}
/*
* If we made it down here then we could not find a matching post in
* the database. No biggie, simply do nothing and display the 404 page
* as normal :)
*/
}
Mischa
This is exactly the use case I was searchin for. However after thinking this through I came to the conclussion to not make this redirects. I simply keep my old permalink structure, and won’t add the category. Since this approach will put performance at risk.
Ahmad
EXACTLY WHAT I NEEDED! Thanks a lot for this amazing piece of code. I think this should be an essential to have in wordpress. I was worried that changing urls might hurt my sites SEO but this saved me!
Bob
Spot on thank you 🙂
Steve
This is awesome – works like a charm. Thank you!
kevin
Sorry but not following completely, where do I add this code?