Different permalink for CPT and regular Posts/Pages but why?

The permalink configuration in WordPress backend is only for standard posts and pages. For CPT the default URL structure is http://example.com/cpt-identifier/post-slug. If you want a different URL structure for you CPT you will have to define and register your own rewrite rules.

For example:

add_action( 'init', 'register_posttype' );
function register_posttype() {
register_post_type( 'my_cpt', //this will be in the URL as CPT identifier
    array(
        'labels' => array(
            'name' => __( 'My Custom Post Types' ),
            'singular_name' => __( 'Custom Post Type' )
        ),
        'public' => true,
        'has_archive' => true,
        'rewrite' => array('slug' => 'products'), //This will override the CPT identifier
    )
);
}

If you want to include dinamic tag in the URL structure of your CPT, like year, you will have to define your own rewrite rules and permalink filter:

add_action( 'init', 'register_posttype' );
function register_posttype() {
register_post_type( 'my_cpt', //this will be in the URL as CPT identifier
    array(
        'labels' => array(
            'name' => __( 'My Custom Post Types' ),
            'singular_name' => __( 'Custom Post Type' )
        ),
        'public' => true,
        'has_archive' => true,
        'rewrite' => array('slug' => '%year%/my_cpt'), //This will override the CPT identifier
    )
);
}

add_filter('post_type_link', 'modify_permalink');
function modify_permalink($url, $post = null) {
    // limit to certain post type. remove if not needed
    if (get_post_type($post) != 'my_cpt') {
        return $url;
    } elseif(!is_object($post)) {
        global $post;
    }
    $url = str_replace("%year%",  get_the_date('Y'), $url);

    return $url;
}

add_action('init','my_add_rewrite_rules');
function my_add_rewrite_rules() {
    add_rewrite_rule('([0-9])/my_cpt/(.+)/?$', 'index.php?post_type=my_cpt&post=$matches[1]', 'top' );
}

tech