/**
* ============================================================
* NSP - Fix URLs con parámetros de WooCommerce
* ============================================================
*
* PROBLEMA: WooCommerce genera miles de URLs con parámetros como
* ?product_orderby=, ?product_count=, ?product_view= en páginas
* de /etiqueta-producto/ y /categoria-producto/. Google las descubre
* y quedan como "Bloqueada por robots.txt" en Search Console.
*
* SOLUCIÓN (3 capas):
* 1. noindex en páginas con parámetros problemáticos
* 2. canonical limpia (sin parámetros) en esas páginas
* 3. eliminar los links con parámetros del HTML para que Google
* no descubra nuevas URLs basura
*
* INSTALACIÓN: Pegar este código al final del functions.php del
* tema hijo (Avada Child), o mejor aún, crear un plugin MU:
* wp-content/mu-plugins/nsp-fix-woocommerce-url-params.php
* (en ese caso agregar el header de plugin al inicio)
*
* ============================================================
*/
// =============================================================
// LISTA DE PARÁMETROS PROBLEMÁTICOS
// =============================================================
function nsp_get_bad_params() {
return array(
'product_orderby',
'product_order',
'product_count',
'product_view',
'product_cat', // filtro de categoría en listados
'min_price',
'max_price',
'filter_', // prefijo para filtros de atributos
'query_type_', // prefijo para tipos de consulta
'orderby', // WooCommerce a veces usa este
);
}
/**
* Detecta si la URL actual tiene algún parámetro problemático
*/
function nsp_url_has_bad_params() {
if ( empty( $_GET ) ) {
return false;
}
$bad_params = nsp_get_bad_params();
foreach ( $_GET as $key => $value ) {
foreach ( $bad_params as $bad ) {
// Usamos strpos para capturar prefijos como filter_ y query_type_
if ( strpos( $key, $bad ) === 0 ) {
return true;
}
}
}
return false;
}
// =============================================================
// CAPA 1: Agregar noindex a páginas con parámetros basura
// =============================================================
add_action( 'wp_head', 'nsp_noindex_param_pages', 1 );
function nsp_noindex_param_pages() {
if ( ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
return;
}
if ( nsp_url_has_bad_params() ) {
echo '' . "\n";
}
}
// =============================================================
// CAPA 2: Forzar canonical limpia (sin parámetros)
// =============================================================
// Esto funciona con Rank Math. Si usás Rank Math, este filtro
// sobreescribe la canonical cuando hay parámetros basura.
add_filter( 'rank_math/frontend/canonical', 'nsp_clean_canonical_url' );
// Si usás Yoast en vez de Rank Math, descomentar esta línea:
// add_filter( 'wpseo_canonical', 'nsp_clean_canonical_url' );
function nsp_clean_canonical_url( $canonical ) {
if ( ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
return $canonical;
}
if ( nsp_url_has_bad_params() ) {
// Devolver la URL sin query string
$clean = strtok( $canonical, '?' );
// Manejar paginación: si estamos en página 2+, agregar /page/N/
$paged = get_query_var( 'paged' );
if ( $paged > 1 ) {
$clean = trailingslashit( $clean ) . 'page/' . $paged . '/';
}
return $clean;
}
return $canonical;
}
// Fallback: si NO usás Rank Math ni Yoast, inyectar canonical manual
add_action( 'wp_head', 'nsp_manual_canonical_fallback', 2 );
function nsp_manual_canonical_fallback() {
// Solo actuar si no hay plugin SEO generando canonical
if ( class_exists( 'RankMath' ) || defined( 'WPSEO_VERSION' ) ) {
return;
}
if ( ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
return;
}
if ( nsp_url_has_bad_params() ) {
global $wp;
$current_url = home_url( $wp->request );
$clean_url = trailingslashit( strtok( $current_url, '?' ) );
$paged = get_query_var( 'paged' );
if ( $paged > 1 ) {
$clean_url .= 'page/' . $paged . '/';
}
echo '' . "\n";
}
}
// =============================================================
// CAPA 3: Limpiar parámetros de los links de ordenamiento
// que WooCommerce inyecta en el HTML
// =============================================================
/**
* 3a. Eliminar el dropdown de "Ordenar por" de las páginas de archivo
* (es la fuente principal de las URLs con ?orderby=)
*/
// Descomentar la siguiente línea si querés ELIMINAR el selector "Ordenar por":
// remove_action( 'woocommerce_before_shop_loop', 'woocommerce_catalog_ordering', 30 );
/**
* 3b. Eliminar el selector "Mostrar X por página"
* (genera URLs con ?product_count=)
* Solo aplica si tu tema o un plugin agrega este selector.
*/
// Descomentar si aplica:
// remove_action( 'woocommerce_before_shop_loop', 'woocommerce_result_count', 20 );
/**
* 3c. Interceptar el output HTML final y agregar rel="nofollow"
* a los links que contengan parámetros problemáticos.
* Esto evita que Google siga esos links y descubra más URLs basura.
*/
add_action( 'template_redirect', 'nsp_start_output_buffer' );
function nsp_start_output_buffer() {
if ( ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
return;
}
ob_start( 'nsp_add_nofollow_to_param_links' );
}
function nsp_add_nofollow_to_param_links( $html ) {
if ( empty( $html ) ) {
return $html;
}
$bad_params = nsp_get_bad_params();
// Patrón: buscar
$html = preg_replace_callback(
'/]*href=["\'][^"\']*\?[^"\']*["\'][^>]*)>/i',
function( $matches ) use ( $bad_params ) {
$tag = $matches[1];
$has_bad = false;
foreach ( $bad_params as $param ) {
if ( strpos( $tag, $param . '=' ) !== false || strpos( $tag, $param ) !== false ) {
$has_bad = true;
break;
}
}
if ( $has_bad ) {
// Si ya tiene rel, agregar nofollow
if ( preg_match( '/rel=["\']([^"\']*)["\']/', $tag ) ) {
$tag = preg_replace(
'/rel=["\']([^"\']*)["\']/',
'rel="$1 nofollow"',
$tag
);
} else {
$tag .= ' rel="nofollow"';
}
}
return '';
},
$html
);
return $html;
}
// =============================================================
// CAPA EXTRA: Agregar reglas al robots.txt desde WordPress
// =============================================================
add_filter( 'robots_txt', 'nsp_custom_robots_rules', 10, 2 );
function nsp_custom_robots_rules( $output, $public ) {
// Solo agregar si el sitio es público
if ( '0' === $public ) {
return $output;
}
$custom_rules = "\n# NSP - Bloquear URLs con parámetros de WooCommerce\n";
$custom_rules .= "Disallow: /*?product_orderby=\n";
$custom_rules .= "Disallow: /*?product_order=\n";
$custom_rules .= "Disallow: /*?product_count=\n";
$custom_rules .= "Disallow: /*?product_view=\n";
$custom_rules .= "Disallow: /*?orderby=\n";
$custom_rules .= "Disallow: /*?min_price=\n";
$custom_rules .= "Disallow: /*?max_price=\n";
$custom_rules .= "Disallow: /*?filter_\n";
$custom_rules .= "Disallow: /*&product_orderby=\n";
$custom_rules .= "Disallow: /*&product_count=\n";
$custom_rules .= "Disallow: /*&product_view=\n";
$custom_rules .= "Disallow: /*&product_order=\n";
$output .= $custom_rules;
return $output;
}
// =============================================================
// FIN DEL SNIPPET
// =============================================================