Contents
Introducción
En este tutorial detallado aprenderás a interceptar errores 404 en WordPress y a ofrecer sugerencias de contenido relacionadas mediante PHP. El objetivo es mejorar la experiencia del usuario, reducir tasas de rebote y recuperar tráfico potencial que de otro modo se perdería. Verás varias aproximaciones —desde la más simple y segura hasta implementaciones más avanzadas con cache, búsqueda por similitud y registro de URLs 404 para análisis— junto con fragmentos de código listos para integrar en tu tema o plugin.
Conceptos clave y buenas prácticas
- Preservar el código de estado 404: Aun mostrando sugerencias, el recurso no existe y el servidor debe devolver el código de estado 404 para no perjudicar al SEO.
- No redirigir automáticamente al home: Las redirecciones masivas a la página principal confunden a usuarios y motores de búsqueda.
- Saneamiento y seguridad: Siempre sanitiza y prepara consultas a la base de datos y datos de entrada.
- Cachear resultados: Generar sugerencias puede implicar consultas costosas usa transients o un sistema de cache para mejorar rendimiento.
- Medir y aprender: Registrar los 404 frecuentes te permitirá crear redirecciones o contenidos nuevos específicos.
Dónde interceptar 404 en WordPress
Hay varias maneras de detectar un 404:
- Plantilla 404 de tu tema: el archivo 404.php es el lugar natural para mostrar sugerencias.
- Hook template_redirect: detecta is_404() y actúa antes de renderizar plantilla.
- Hook pre_handle_404 o send_headers: opciones más avanzadas si necesitas controlar cabeceras y flujo.
Ejemplo simple: añadir sugerencias en 404.php
La solución más directa: en tu 404.php generar una búsqueda basada en la URL solicitada y mostrar resultados.
lt?php // 404.php (fragmento) // 1) Asegúrate de que WordPress marque el estado 404 status_header(404) // 2) Obtener la ruta solicitada (sin dominio ni query string) requested_path = trim( parse_url( _SERVER[REQUEST_URI], PHP_URL_PATH ), / ) // 3) Preparar cadena de búsqueda (reemplaza símbolos por espacios) search_terms = preg_replace(/[/-_] /, , requested_path) search_terms = urldecode( search_terms ) search_terms = trim( search_terms ) // 4) Ejecutar búsqueda simple con WP_Query search_query = new WP_Query( array( post_type => array(post,page), posts_per_page => 6, s => search_terms, post_status => publish, ) ) // 5) Mostrar sugerencias si hay resultados if ( search_query-gthave_posts() ) { echo lth3gtQuizá te interesen estos contenidos:lt/h3gt echo ltulgt while ( search_query-gthave_posts() ) { search_query-gtthe_post() echo ltligtlta href=.get_permalink().. gt.get_the_title().lt/agtlt/ligt } echo lt/ulgt wp_reset_postdata() } else { echo ltpgtNo se han encontrado sugerencias automáticas.lt/pgt } ?gt
Métodos para encontrar contenidos relacionados
Dependiendo del sitio y su estructura, puedes usar una o varias técnicas combinadas:
- Búsqueda simple (s) con WP_Query: rápida y sencilla funciona bien cuando el slug o la URL contienen palabras clave útiles.
- Coincidencia por taxonomías (categorías/etiquetas): extraer términos del slug y buscar posts que compartan esas taxonomías.
- Fulltext/FTS en MySQL: más precisa para grandes cantidades de contenido, pero requiere índices FULLTEXT y puede necesitar permisos.
- Ranking por distancia de Levenshtein o similitud: útil para errores tipográficos se obtiene una lista de candidatos y luego se ordena por similitud.
Ejemplo avanzado: combinar taxonomías búsqueda ranking por similitud
Este ejemplo generará candidatos por búsqueda general, buscará coincidencias por etiquetas/categorías y luego ordenará por similitud del título (levenshtein) para sugerir resultados más relevantes.
lt?php function get_suggestions_for_404( requested_path, limit = 6 ) { global wpdb requested_path = trim( requested_path, / ) search = preg_replace(/[/-_] /, , requested_path ) search = sanitize_text_field( urldecode( search ) ) // 1) Buscar candidatos por texto candidates = new WP_Query( array( post_type => array(post,page), post_status => publish, s => search, posts_per_page => 20, // obtener más para luego ordenar ) ) results = array() if ( candidates->have_posts() ) { while ( candidates->have_posts() ) { candidates->the_post() title = get_the_title() permalink = get_permalink() // 2) Calcular distancia de Levenshtein entre título y ruta (o palabras clave) dist = levenshtein( strtolower( search ), strtolower( title ) ) results[] = array( ID => get_the_ID(), title => title, permalink => permalink, distance => dist, ) } wp_reset_postdata() } // 3) Ordenar por menor distancia (más parecido) usort( results, function(a, b) { return a[distance] - b[distance] } ) // 4) Devolver los primeros limit elementos return array_slice( results, 0, limit ) } // Ejemplo de uso dentro de 404.php: requested_path = trim( parse_url( _SERVER[REQUEST_URI], PHP_URL_PATH ), / ) suggestions = get_suggestions_for_404( requested_path, 6 ) if ( ! empty( suggestions ) ) { echo lth3gtContenido relacionado sugerido:lt/h3gt echo ltulgt foreach ( suggestions as s ) { echo ltligtlta href= . esc_url( s[permalink] ) . gt . esc_html( s[title] ) . lt/agtlt/ligt } echo lt/ulgt } ?gt
Optimización y cache
Generar sugerencias puede implicar búsquedas pesadas. Usa transients para cachear resultados por URL solicitada. Además, invalidar cache cuando se publiquen contenidos relevantes.
lt?php function get_cached_404_suggestions( requested_path ) { key = 404_sugg_ . md5( requested_path ) cached = get_transient( key ) if ( cached !== false ) { return cached } // si no hay cache, generar suggestions = get_suggestions_for_404( requested_path, 6 ) // cache por 1 hora set_transient( key, suggestions, HOUR_IN_SECONDS ) return suggestions } ?gt
Registro de 404 (logging) para análisis
Registrar las URL 404 más frecuentes te permite identificar patrones y crear redirecciones 301 o contenido nuevo. A continuación un ejemplo de creación de tabla en la activación de un plugin y la función que registra o incrementa el contador.
lt?php // En el archivo principal del plugin: register_activation_hook( __FILE__, mi_plugin_create_404_table ) function mi_plugin_create_404_table() { global wpdb table = wpdb->prefix . 404_logs charset_collate = wpdb->get_charset_collate() sql = CREATE TABLE table ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, url text NOT NULL, referer text NULL, ip varchar(100) NULL, user_agent text NULL, hits int(11) NOT NULL DEFAULT 1, last_seen datetime NOT NULL, PRIMARY KEY (id) ) charset_collate require_once ABSPATH . wp-admin/includes/upgrade.php dbDelta( sql ) } // Función para registrar 404 (llamarla desde template_redirect cuando is_404) function mi_plugin_log_404() { if ( ! is_404() ) { return } global wpdb table = wpdb->prefix . 404_logs url = esc_url_raw( wp_unslash( _SERVER[REQUEST_URI] ) ) referer = isset( _SERVER[HTTP_REFERER] ) ? esc_url_raw( wp_unslash( _SERVER[HTTP_REFERER] ) ) : null ip = isset( _SERVER[REMOTE_ADDR] ) ? _SERVER[REMOTE_ADDR] : null ua = isset( _SERVER[HTTP_USER_AGENT] ) ? sanitize_text_field( wp_unslash( _SERVER[HTTP_USER_AGENT] ) ) : null // Intentar actualizar el registro existente existing = wpdb->get_row( wpdb->prepare( SELECT id, hits FROM table WHERE url = %s, url ) ) if ( existing ) { wpdb->update( table, array( hits => existing->hits 1, last_seen => current_time( mysql ), ), array( id => existing->id ), array( %d, %s ), array( %d ) ) } else { wpdb->insert( table, array( url => url, referer => referer, ip => ip, user_agent => ua, hits => 1, last_seen => current_time( mysql ), ), array( %s, %s, %s, %s, %d, %s ) ) } } // Enganchar al template_redirect add_action( template_redirect, mi_plugin_log_404, 5 ) ?gt
Uso de FULLTEXT y MySQL para sitios grandes
Si tu base de datos tiene índices FULLTEXT en post_title/post_content, puedes usar consultas MATCH…AGAINST para obtener resultados más relevantes y rápidos en colecciones grandes. Asegúrate de añadir índices y probar la compatibilidad con tu versión de MySQL/MariaDB.
-- Ejemplo de índice fulltext (ejecutar en la base de datos) ALTER TABLE wp_posts ADD FULLTEXT KEY ft_title_content (post_title, post_content)
lt?php global wpdb search = texto a buscar // sanear antes sql = wpdb->prepare( SELECT ID, post_title, MATCH(post_title, post_content) AGAINST (%s IN NATURAL LANGUAGE MODE) AS score FROM {wpdb->posts} WHERE post_status = publish AND post_type IN (post,page) AND MATCH(post_title, post_content) AGAINST (%s IN NATURAL LANGUAGE MODE) ORDER BY score DESC LIMIT 6, search, search ) rows = wpdb->get_results( sql ) ?gt
Consideraciones SEO y de UX
- Cabecera 404: Mantén status_header(404) para que los buscadores sepan que el recurso no existe.
- Contenido útil: Ofrece enlaces a páginas relevantes, un buscador interno y enlaces a categorías populares.
- Redirecciones selectivas: Si detectas una URL muy frecuente que debería mapear a un nuevo recurso, crea una redirección 301 permanente en lugar de mostrar siempre el 404.
- No indexar páginas 404: Añade meta robots noindex si tu 404 genera URLs accesibles que no deberían indexarse por duplicidad de contenido.
Errores comunes y cómo evitarlos
- Eliminar el status 404: Evita devolver 200 OK en páginas que no existen suele penalizar al SEO.
- Buscar en campos no sanitizados: Usa prepare/esc_sql y funciones de saneamiento al hacer consultas directas.
- Queries pesadas sin cache: Cachea los resultados con transients o con el sistema de object cache para no cargar la DB en cada 404.
Resumen práctico paso a paso
- Decide si la lógica irá en 404.php o en un plugin (plugin es preferible para mantener portabilidad).
- Obtén la URL solicitada y genera términos de búsqueda razonables (separar rutas, sustituir guiones, decodificar).
- Busca candidatos con WP_Query o consultas optimizadas (FULLTEXT) y ordénalos por relevancia.
- Cachea los resultados por URL para mejorar rendimiento.
- Registra 404 recurrentes para tomar decisiones de redirección o creación de contenido.
- Mantén el código de estado 404 y ofrece una experiencia de usuario clara y útil.
Tabla comparativa rápida
Método | Ventajas | Inconvenientes |
Búsqueda s con WP_Query | Fácil de implementar, sin DB estructural | Menos precisa en grandes sitios, puede ser lenta |
FULLTEXT (MySQL) | Rápida y precisa con índices | Requiere cambios en BD y configuración |
Taxonomía coincidencia | Muy relevante si la URL refleja categorías/etiquetas | Requiere lógica de extracción de términos |
Ejemplo final compacto: todo integrado (snippet para functions.php)
lt?php // Enganchar detección temprana add_action( template_redirect, mi_404_interceptor, 5 ) function mi_404_interceptor() { if ( ! is_404() ) { return } // Marcar cabecera 404 status_header(404) // Obtener ruta requested = trim( parse_url( _SERVER[REQUEST_URI], PHP_URL_PATH ), / ) // Intentar recuperar cache de sugerencias cache_key = 404_sugg_ . md5( requested ) suggestions = get_transient( cache_key ) if ( suggestions === false ) { suggestions = get_suggestions_for_404( requested, 6 ) // función definida antes set_transient( cache_key, suggestions, HOUR_IN_SECONDS ) } // Registrar 404 para análisis if ( function_exists( mi_plugin_log_404 ) ) { mi_plugin_log_404() } // Si estamos en el flujo de 404.php no hacemos más en plantillas headless podrías inyectar sugerencias. // Aquí no redirigimos solo dejamos sugerencias disponibles para la plantilla 404. // Las sugerencias pueden almacenarse globalmente para ser leídas en 404.php: GLOBALS[mi_404_suggestions] = suggestions } ?gt
Últimas notas
Implementar sugerencias en páginas 404 mejora la experiencia y puede recuperar tráfico. Empieza por una solución simple y mide resultados: ¿las sugerencias capturan clics? ¿qué URLs aparecen con más frecuencia en tus logs 404? A partir de esos datos, ajusta las reglas de búsqueda, añade redirecciones y crea contenido cuando sea necesario.
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |