4

I have found myself in a situation which I would like to understand and ultimately fix. We have custom post types and for them, custom taxonomies.

Everything is working fine. URLs a generated right. But: when, for whatever reason, someone is changing a part of the URL or it's misspelled, there is something strange happening (in my opinion). Calling this correct URL first:

/treatment/psychosomatic/psychosomatic-dysfunction/temporary-psychomatic-dysfunction/
/<custom post type>/<taxonomy level 0>/<taxonomy level 1>/<taxonomy level 2>/

and changing the URL by entering something completely different in an URL part, the content of the original URL is shown and also indexed and returning HTTP status code 200, which may be a SEO problem in the future. It also doesn't matter which URL part is changed, except the last one. So this still shows the content of the original page.

/treatment/exampletext/lorem-ipsum-dolor/temporary-psychomatic-dysfunction/

The question is, how to change the behaviour so that misspelled URLs will cause a 404 error?

Here are the post type and taxonomy registration codes:

function cptui_register_my_cpts_clinic() {

$labels = [
    "name" => __( "clinics", "spotter" ),
    "singular_name" => __( "clinic", "spotter" ),
];

$args = [
    "label" => __( "clinics", "spotter" ),
    "labels" => $labels,
    "description" => "",
    "public" => true,
    "publicly_queryable" => true,
    "show_ui" => true,
    "show_in_rest" => true,
    "rest_base" => "",
    "rest_controller_class" => "WP_REST_Posts_Controller",
    "has_archive" => false,
    "show_in_menu" => true,
    "show_in_nav_menus" => true,
    "delete_with_user" => false,
    "exclude_from_search" => false,
    "capability_type" => "post",
    "map_meta_cap" => true,
    "hierarchical" => false,
    "rewrite" => [ "slug" => "clinic", "with_front" => false ],
    "query_var" => true,
    "menu_icon" => "dashicons-admin-home",
    "supports" => [ "title", "editor", "thumbnail", "comments" ],
    "show_in_graphql" => false,
];

register_post_type( "clinic", $args );}

and

function cptui_register_my_taxes_treatment() {

$labels = [
    "name" => __( "treatments", "spotter" ),
    "singular_name" => __( "treatment", "spotter" ),
];


$args = [
    "label" => __( "Treatment", "spotter" ),
    "labels" => $labels,
    "public" => true,
    "publicly_queryable" => true,
    "hierarchical" => true,
    "show_ui" => true,
    "show_in_menu" => true,
    "show_in_nav_menus" => true,
    "query_var" => true,
    "rewrite" => [ 'slug' => 'treatment', 'with_front' => false,  'hierarchical' => true, ],
    "show_admin_column" => false,
    "show_in_rest" => true,
    "rest_base" => "treatment",
    "rest_controller_class" => "WP_REST_Terms_Controller",
    "show_in_quick_edit" => true,
    "show_in_graphql" => false,
];
register_taxonomy( "treatment", [ "clinic" ], $args );}

I hope the information is enough to see the problem. I'm happy to show more relevant code if needed.

1

It also doesn't matter which URL part is changed, except the last one. So this still shows the content of the original page.

Yes, because your taxonomy's rewrite is hierarchical with the permalink structure /treatment/<term slug> (e.g. /treatment/psychosomatic) or /treatment/<term slug>/<term slug>/... (if the term is a child term, e.g. /treatment/psychosomatic/psychosomatic-dysfunction), so as long as the current URL matches that structure and that the last <term slug> is a valid term slug, then the page would load normally.

how to change the behaviour so that misspelled URLs will cause a 404 error?

There is no taxonomy argument or admin setting that can do that, but you can use the pre_handle_404 filter like so: (you can add this to your theme functions file)

add_filter( 'pre_handle_404', 'my_pre_handle_404', 10, 2 );
function my_pre_handle_404( $preempt, $wp_query ) {
    // A list of taxonomy slugs.
    $taxonomies = array( 'treatment' );

    // Check if we're on the archive page for taxonomies in the above list.
    if ( $wp_query->is_tax( $taxonomies ) ) {
        // We need this to access the path of the current URL ($wp->request),
        // and note that the path does not include the trailing slashes.
        global $wp;

        // Get the original/correct term permalink.
        // e.g. https://example.com/treatment/parent/child/term-slug/
        $permalink = get_term_link( $wp_query->get_queried_object() );

        // Get the path in the above permalink.
        // e.g. treatment/parent/child/term-slug - traling slashes are trimmed
        $orig_path = trim( parse_url( $permalink, PHP_URL_PATH ), '/' );

        // Check if the above path matches the path of the current URL, and if
        // not, then we issue a 404 error.
        if ( $orig_path !== $wp->request ) {
            $wp_query->set_404();
        }
    }

    return $preempt;
}

If you want the code to work with the default category taxonomy, then change the $wp_query->is_tax( $taxonomies ) to $wp_query->is_tax( $taxonomies ) || $wp_query->is_category().

1
  • 1
    Excellent answer! Works like a charm, just what I was looking for. Thank you very much!
    – macstens
    Aug 21 at 1:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.