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?
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.