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 🙂 |
