Optional taxonomy in cpt permalink

You’re correct that the issue is caused by the fact that the rewrite rule is set to include the %tax-name% placeholder even when a taxonomy is not set for the post.

One way to dynamically set the rewrite rule based on whether a taxonomy is set or not is to use the post_type_link filter. This filter allows you to modify the permalink for a post based on the post object.

Here’s an example of how you could modify the rewrite rule to exclude the %tax-name% placeholder when no taxonomy is set:

add_filter( 'post_type_link', 'custom_post_type_permalink', 10, 2 );
function custom_post_type_permalink( $permalink, $post ) {
    if ( $post->post_type === 'your-custom-post-type' ) {
        $terms = get_the_terms( $post, 'your-taxonomy' );
        if ( $terms && ! is_wp_error( $terms ) ) {
            $term = array_pop( $terms );
            $permalink = str_replace( '%your-taxonomy%', $term->slug, $permalink );
        } else {
            $permalink = str_replace( '%your-taxonomy%/', '', $permalink );
    return $permalink;

In this example, the post_type_link filter is used to modify the permalink for the custom post type. If a taxonomy is set for the post, the %your-taxonomy% placeholder in the rewrite rule is replaced with the slug of the first term for that taxonomy. If no taxonomy is set, the %your-taxonomy%/ part is removed from the rewrite rule, effectively removing the taxonomy from the permalink.