custom permalink for single post category

In the pl_custom_rewrite_rules() function, change the REGEX pattern to ^new_slug/(.*)$ as in:

add_rewrite_rule( '^new_slug/(.*)$', 'index.php?name=$matches[1]', 'top' );

because WordPress strips the preceeding / from the request path (WP::$request), so using the ^/new_slug never evaluates to true.


Additionally, in the pl_custom_permalink() function, remove the global $post; line because the function already receiving the proper post data (i.e. $post) as in function pl_custom_permalink( $permalink, $post, $leavename ).


The full code I used, without any re-indentation: (tried & tested working on WordPress 4.9.8)

add_filter( 'post_link', 'pl_custom_permalink', 10, 3 );
function pl_custom_permalink( $permalink, $post, $leavename ) {
  if ( has_category( 'My Category', $post->ID) ) {
    $permalink = trailingslashit( home_url('/new_slug/'. $post->post_name . "https://wordpress.stackexchange.com/" ) );
  }
  return $permalink;
}
add_action('init', 'pl_custom_rewrite_rules');
function pl_custom_rewrite_rules( $wp_rewrite ) {
  add_rewrite_rule( '^new_slug/(.*)$', 'index.php?name=$matches[1]', 'top' );
}

Here’s the code for the canonical redirect, which redirects example.com/name-of-my-post to example.com/new_slug/name-of-my-post:

add_action( 'wp', function( $wp ){
    // If it's a single Post and it's in the 'My Category' category, redirect to
    // the canonical version. Also check if the request path doesn't start with
    // new_slug/ as in http://example.com/new_slug/hello-world/
    if ( ! preg_match( '#^new_slug/#', $wp->request ) &&
        is_singular() && in_category( 'My Category' ) ) {
        wp_redirect( get_permalink() );
        exit;
    }
} );