How to redirect CPT permalinks from ID to default pretty permalinks?

If I understood you correctly, you will first need to redirect your user to http://domain.com/cpt1/%postname%/ and afterward, you’ll need to rewrite the URL in order to get the correct post and avoid 404 errors.

So here you go:


add_action( 'template_redirect', 'redirect_postid_to_postname' );

function redirect_postid_to_postname() {

    $uri = $_SERVER['REQUEST_URI'];

    // Checks if the URI matches what we want. This means the URL should be sth like http://example.com/cpt1/POST_ID
    if ( !preg_match( "/^\/cpt1\/(\d+)/", $uri, $matches ) ) {
        return;
    }

    $post_id = $matches[1];

    // Checks if $post_id is actually an existent post ID.
    $post_obj = get_post( $post_id );

    // Checks if a post object was retrieved for the given post ID.
    if ( !$post_obj ) {
        return;
    }

    $post_name = $post_obj->post_name; // Keeps post slug.
    $site_url = get_site_url(); // Keeps site URL (it's supposed to be retrieved without trailing slash).
    $location = $site_url . "/cpt1/$post_name"; // Builds the new URL to redirect the user to.

    // Redirects!
    wp_redirect( $location, 302 );

    exit;

}

add_action( 'init', 'rewrite_custom_postname', 100 );

function rewrite_custom_postname() {

    $uri = $_SERVER['REQUEST_URI'];

    // Checks if the URI matches what we want. This means the URL should be sth like http://example.com/cpt1/POST_NAME
    if ( !preg_match( "/^\/cpt1\/(.+)/", $uri, $matches ) ) {
        return;
    }

    $post_name = $matches[1];

    $query_args = array(
        'name'        => $post_name,
        'post_type'   => 'cpt1',
        'post_status' => 'publish',
        'numberposts' => 1
    );

    $post_obj = get_posts( $query_args );

    // Checks if a post object was retrieved for the given query arguments.
    if ( !$post_obj ) {
        return;
    }

    $post_id = $post_obj[0]->ID;

    $regex = "^cpt1/{$matches[1]}";
    $query = "index.php?p=$post_id";
    $after = "top";

    add_rewrite_rule( $regex, $query, $after );

}

IMPORTANT!
If you are still making use of Permalink Manager and you’re also changing the uri from %postname% to %post_id% you might CREATE an infinite loop of redirections, so be careful.