Search code examples
phpwordpresswoocommercewordpress-hook

Issue with woocommerce parent child category url


In WooCommerce, child categories have the following URL structure:

site.com/product-category/parent-category/child-category.

However, if I modify the "parent-category" part of the URL in the browser, like this:

site.com/product-category/parenwqewqt-qwecategory/child-category, the page still returns a 200 status code instead of a 404, even though the URL is incorrect.

This seems to be a common WooCommerce issue that could lead to SEO problems, as invalid URLs are not properly flagged as 404 errors.

I tried to create a hook to fix this, but it didn't work. Here's the code I used:

I create this but nothing changes:

add_action('template_redirect', 'custom_woocommerce_category_404');
function custom_woocommerce_category_404() {
    if (is_product_category()) {
        $category = get_queried_object();
        if (!$category || !term_exists($category->slug, 'product_cat')) {
            global $wp_query;
            $wp_query->set_404();
            status_header(404);
            nocache_headers();
            include(get_query_template('404'));
            exit;
        }
    }
}

How can I modify this or create a proper hook to return a 404 error when the URL structure is incorrect?


Solution

  • WooCommerce category URLs return a 404 error when the parent category in the URL is incorrect. It hooks into the template_redirect action to check if a product category is being viewed.

    If the category exists and has a parent, it compares the parent slug in the URL with the actual parent category of the current child category.

    The function get_parent_category_from_url() extracts the parent slug from the URL, and if it doesn't match the actual parent category in the database, it triggers a 404 error using set_404().

    Try the following:

    // Hook into template redirect to modify WooCommerce category URL handling
    add_action('template_redirect', 'custom_woocommerce_category_404');
    
    /**
     * Custom function to trigger a 404 error if the WooCommerce category URL structure is invalid.
     * This checks if the parent category in the URL is correct for the given child category.
     */
    function custom_woocommerce_category_404() {
        if (is_product_category()) {
            // Get the current queried category object
            $category = get_queried_object();
    
            // If the category doesn't exist, trigger a 404 error
            if (!$category || !term_exists($category->slug, 'product_cat')) {
                handle_404();
            }
    
            // Check if the current category has a parent category and if the URL structure is correct
            if (is_tax('product_cat') && get_query_var('product_cat')) {
                $current_cat_slug = get_query_var('product_cat');
                $current_cat = get_term_by('slug', $current_cat_slug, 'product_cat');
    
                // If the current category has a parent, compare it with the URL structure
                if ($current_cat && $current_cat->parent != 0) {
                    $parent_cat = get_term($current_cat->parent, 'product_cat');
    
                    // Extract the parent category slug from the URL
                    $requested_parent_slug = get_parent_category_from_url();
    
                    // If the parent slug in the URL doesn't match the actual parent, trigger a 404 error
                    if ($requested_parent_slug && $requested_parent_slug !== $parent_cat->slug) {
                        handle_404();
                    }
                }
            }
        }
    }
    
    /**
     * Helper function to extract the parent category from the URL, ignoring pagination.
     * This works by splitting the URL path and retrieving the second-to-last part before 'page'.
     */
    function get_parent_category_from_url() {
        global $wp;
        $url_path = $wp->request; // Get the current request URL path
        $parts = explode('/', $url_path);
    
        // Remove pagination part (e.g., 'page/2') if it exists
        $pagination_index = array_search('page', $parts);
        if ($pagination_index !== false) {
            $parts = array_slice($parts, 0, $pagination_index);
        }
    
        // Return the second last part of the URL (which should be the parent category slug)
        return (count($parts) > 2) ? $parts[count($parts) - 2] : null;
    }
    
    /**
     * Helper function to handle triggering a 404 error.
     * This includes setting the 404 header, including the 404 template, and exiting.
     */
    function handle_404() {
        global $wp_query;
        $wp_query->set_404();
        status_header(404);
        nocache_headers();
        include(get_query_template('404'));
        exit;
    }
    

    This is a tested and working solution.