Introducción a Apache mod_rewrite
El archivo .htaccess es una de las herramientas más poderosas en el arsenal de un administrador de WordPress. Este archivo de configuración te permite anular la configuración del servidor a nivel de directorio, dándote control granular sobre las redirecciones de URL, restricciones de acceso y optimizaciones de rendimiento.
Qué es mod_rewrite en Apache
mod_rewrite es un módulo de Apache que proporciona un motor de reescritura basado en reglas para reescribir URLs solicitadas sobre la marcha. Es el motor detrás de los permalinks bonitos de WordPress y es esencial para las estructuras de URL amigables con el SEO.
Beneficios clave:
- Preservación del SEO: Redirige URLs antiguas a nuevas sin perder posiciones en buscadores
- Experiencia del usuario: Asegura que los visitantes siempre lleguen al contenido correcto
- Seguridad: Fuerza HTTPS y bloquea solicitudes maliciosas
- Rendimiento: Implementa cabeceras de caché y compresión
Cuándo usar redirecciones 301 vs 302
Entender los códigos de estado HTTP es crucial para una implementación SEO adecuada:
| Tipo de redirección | Caso de uso | Impacto SEO |
|---|---|---|
| 301 Permanente | Contenido movido permanentemente, cambios de dominio, migración HTTPS | Google la usa como señal canónica y muestra la URL de destino en los resultados |
| 302 Encontrado | Mantenimiento temporal, pruebas A/B, movimientos de contenido a corto plazo | Googlebot la sigue, pero no es señal canónica: la URL original suele quedarse en los resultados |
| 307 Temporal | Redirección temporal estricta HTTP/1.1, preservación de datos POST | Igual que 302 pero preserva el método HTTP |
| 308 Permanente | Redirección permanente estricta HTTP/1.1, preservación de datos POST | Igual que 301 pero preserva el método HTTP |
Regla de oro: Si el cambio es permanente, siempre usa 301. Con una 302 en un cambio permanente, la URL antigua puede quedarse en los resultados en lugar de la nueva.
Ejemplos de redirección 301 en .htaccess
1. Redirigir una sola página
La forma más simple de redirección mueve una URL específica a otra:
# Redirigir una sola página (sintaxis simple)
Redirect 301 /página-antigua.html https://www.dominio.com/página-nueva.html
# Alternativa usando RewriteRule
RewriteEngine on
RewriteRule ^página-antigua\.html$ /página-nueva.html [R=301,L]Cuándo usar cada una:
- Usa
Redirect 301para coincidencias de URL exactas y simples - Usa
RewriteRulecuando necesites coincidencia de patrones o condiciones
2. Redirigir todo el sitio a un nuevo dominio
Mover todo tu sitio web a un nuevo dominio requiere un manejo cuidadoso:
# Redirigir sitio completo a nuevo dominio
Redirect 301 / https://www.nuevodominio.com/
# Preservar la estructura completa de URL
RewriteEngine on
RewriteBase /
RewriteRule (.*) https://www.nuevodominio.com/$1 [R=301,L]Importante: Siempre prueba con algunas URLs primero antes de implementar redirecciones a nivel de sitio.
3. Redirigir una subcarpeta a la raíz (o al revés)
Común al reestructurar contenido:
# Redirigir sitio completo a una subcarpeta (sin bucle en el mismo host)
RedirectMatch 301 ^/(?!subcarpeta/)(.*)$ https://www.dominio.com/subcarpeta/$1
# Redirigir subcarpeta a la raíz (con barra final, sin doble barra)
Redirect 301 /subcarpeta/ https://www.dominio.com/
# Usando RewriteRule con preservación de ruta completa
RewriteEngine on
RewriteRule ^subcarpeta/(.*)$ /$1 [R=301,L]4. Redirigir .html a .php y quitar extensiones
Al migrar de .html a .php o implementar permalinks bonitos:
# Redirigir .html a .php
RedirectMatch 301 (.*)\.html$ https://www.dominio.com$1.php
# Eliminar extensión .php (hacer URLs más limpias)
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^([^\.]+)$ $1.php [NC,L]5. Forzar HTTPS (SSL)
Esencial para seguridad y SEO:
# Forzar HTTPS - Método 1 (simple)
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Forzar HTTPS - Método 2 (con manejo de www)
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^(.*)$ https://www.dominio.com/$1 [L,R=301]6. Forzar www o sin www
La consistencia en la estructura de URL importa para el SEO:
# Forzar www
RewriteEngine on
RewriteCond %{HTTP_HOST} ^dominio.com [NC]
RewriteRule ^(.*)$ https://www.dominio.com/$1 [L,R=301]
# Forzar sin www (dominio desnudo)
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.dominio\.com [NC]
RewriteRule ^(.*)$ https://dominio.com/$1 [L,R=301]RewriteRule con parámetros y expresiones regulares
Cómo redirigir URLs con parámetros de consulta
El manejo de URLs con parámetros requiere atención especial:
# Redirigir parámetro de consulta específico
RewriteEngine on
RewriteCond %{QUERY_STRING} (^|&)id=1(&|$)
RewriteRule ^index\.php$ /ruta-a-nueva-ubicación/? [R=301,L]
# Pasar el valor de un parámetro a la ruta
RewriteEngine On
RewriteCond %{QUERY_STRING} ^category=([^&]+)
RewriteRule ^index\.php$ /categoria/%1/? [R=301,L]
# Eliminar cadenas de consulta por completo
RewriteEngine On
RewriteCond %{QUERY_STRING} .
RewriteRule ^$ /? [R=301,L]Nota: El ? final en la URL de destino elimina la cadena de consulta original. Desde Apache 2.4.0 la bandera [QSD] hace lo mismo (banderas de RewriteRule). La condición (^|&)id=1(&|$) evita que la regla capture también id=10, id=123 o pid=1.
Coincidencia de patrones con regex en RewriteRule
Las expresiones regulares proporcionan una potente coincidencia de patrones:
# Redirigir URLs basadas en fechas
RewriteEngine On
RewriteRule ^blog/([0-9]{4})/([0-9]{2})/(.+)$ /noticias/$1/$2/$3 [R=301,L]
# Redirigir con comodines
RewriteEngine On
RewriteRule ^productos/(.*)$ /tienda/$1 [R=301,L]
# Coincidencias de múltiples patrones
RewriteEngine On
RewriteRule ^(blog|noticias|artículos)/(.+)$ /contenido/$2 [R=301,L]Cambio de dominio con preservación de ruta
Al migrar a un dominio completamente nuevo:
# Dominio antiguo a nuevo dominio con ruta completa
RewriteEngine On
RewriteCond %{HTTP_HOST} ^dominio-antiguo.com$ [OR]
RewriteCond %{HTTP_HOST} ^www.dominio-antiguo.com$
RewriteRule (.*)$ https://www.dominio-nuevo.com/$1 [R=301,L]
# Con ajuste de subdirectorio
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/blog/(.*)$
RewriteRule ^(.*)$ https://www.nuevodominio.com/noticias/%1 [R=301,L]Cómo probar redirecciones antes de publicarlas
Qué comprobar antes de publicar una redirección
- Usa 302 temporal primero: Prueba con redirecciones 302 antes de hacerlas permanentes
- Verifica cadenas de redirección: Asegúrate de no crear bucles de redirección
- Prueba múltiples escenarios: Verifica con y sin www, HTTP y HTTPS
- Válida con herramientas: Usa verificadores de redirección en línea
Probar una redirección 301 con curl
# Usando curl para verificar cabeceras
curl -I http://tudominio.com/página-antigua
# Seguir redirecciónes con curl
curl -L http://tudominio.com/página-antigua
# Verificar códigos de estado específicos
curl -I -L http://tudominio.com/página-antigua 2>&1 | grep HTTPHerramientas en línea para comprobar redirecciones
- Verificador de redirecciones y estado HTTP: httpstatus.io
- Google Search Console: Verifica errores de rastreo después de la implementación
Solución de problemas de bucles de redirección
Qué causa un bucle de redirección
- Referencias circulares: La regla A redirige a B, la regla B redirige de vuelta a A
- Discrepancias de protocolo: Regla HTTP conflictuando con forzado de HTTPS
- Conflictos WWW: Reglas www y sin www luchando entre sí
- Conflictos de WordPress: Redirecciónes de plugins conflictuando con .htaccess
Registrar mod_rewrite para encontrar el bucle
# Configuración del servidor o del virtual host (LogLevel no está permitido en .htaccess)
LogLevel alert rewrite:trace3# Filtrar solo las entradas de mod_rewrite
tail -f error_log | fgrep '[rewrite:'Nota: RewriteLog y RewriteLogLevel desaparecieron en Apache 2.4; ahora se usa LogLevel por módulo (mod_rewrite). La documentación advierte que los niveles por encima de trace2 ralentizan mucho el servidor: úsalos solo para depurar y quítalos después.
Cómo detener un bucle de redirección en .htaccess
# Prevenir bucles verificando si ya fue redirigido
RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
# Manejo específico de protocolo para evitar bucles
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]Impacto en el rendimiento de las reglas .htaccess
Cuánto cuesta .htaccess en cada petición
Según la guía de .htaccess de Apache, cuando AllowOverride permite usar .htaccess, httpd busca el archivo en cada directorio y en todos los directorios superiores en cada petición, y lo carga cada vez que se pide un documento. Para un archivo en /www/htdocs/example eso supone 4 búsquedas adicionales en el sistema de archivos. Las cadenas de redirecciones añaden además latencia para el usuario, con los límites que Google fija más abajo.
Orden de reglas y regex para redirecciones más rápidas
- El orden importa: Coloca las reglas que coinciden más frecuentemente primero
- Usa patrones específicos: Los patrones más específicos son más rápidos
- Limita la complejidad del regex: Evita cuantificadores codiciosos donde sea posible
- Considera la configuración a nivel de servidor: Para sitios de alto tráfico, usa httpd.conf en su lugar
# Ejemplo de ordenamiento optimizado
RewriteEngine On
# Redirecciónes más comunes primero
RewriteRule ^wp-admin$ /wp-admin/ [R=301,L]
# Luego patrones específicos
RewriteRule ^producto-antiguo/(.*)$ /tienda/$1 [R=301,L]
# Patrones genéricos al final
RewriteRule ^blog/(.*)$ /noticias/$1 [R=301,L]Activar mod_expires y compresión
# Habilitar mod_expires para mejor caché
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 month"
</IfModule>
# Habilitar compresión
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css application/javascript
</IfModule>Redirección 301 en WordPress con .htaccess
Redirecciones personalizadas antes del bloque de WordPress
WordPress usa sus propias reglas de reescritura en .htaccess. Siempre coloca las reglas personalizadas antes del bloque de WordPress:
# BEGIN Redirecciónes personalizadas
RewriteEngine On
# Tus reglas personalizadas aquí
# END Redirecciónes personalizadas
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPressRedirigir adjuntos, archivos de autor y de categoría
# Redirigir páginas de adjuntos a la página de inicio
RewriteEngine On
RewriteRule ^attachment/(.*)$ / [R=301,L]
# Redirigir archivos de autor a la página de inicio
RewriteEngine On
RewriteRule ^author/(.*)$ / [R=301,L]
# Redirigir archivos de categorías a una página específica
RewriteEngine On
RewriteRule ^category/categoria-antigua$ /nueva-sección/ [R=301,L]Proteger wp-config.php y bloquear peticiones maliciosas
Proteger archivos sensibles de WordPress
# Bloquear acceso a archivos sensibles de WordPress
<FilesMatch "^\.">
Require all denied
</FilesMatch>
# Proteger wp-config.php
<Files wp-config.php>
Require all denied
</Files>
# Bloquear acceso al propio .htaccess
<Files .htaccess>
Require all denied
</Files>Order y Deny from son sintaxis de Apache 2.2: en 2.4 solo funcionan a través de mod_access_compat y su uso está desaconsejado (actualizar a 2.4). Require all denied es la forma actual.
Bloquear cadenas de consulta maliciosas en .htaccess
# Bloquear intentos de inyección SQL
RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
RewriteRule ^(.*)$ index.php [F,L]Sintaxis de Redirect, RedirectMatch y RewriteRule
Sintaxis de la directiva Redirect
# Redirección 301 básica
Redirect 301 /antigua /nueva
# Redirección temporal 302
Redirect 302 /mantenimiento /página-temporal
# Redirección con URL completa
Redirect 301 /página http://ejemplo.com/nueva-páginaSintaxis de RedirectMatch
# Redirección basada en patrones
RedirectMatch 301 ^/blog/(.*)$ http://ejemplo.com/noticias/$1
# Insensible a mayúsculas
RedirectMatch 301 (?i)^/carpeta-antigua/(.*)$ /carpeta-nueva/$1Sintaxis RewriteRule
# Estructura básica
RewriteRule Patrón Sustitución [Banderas]
# Banderas comunes:
# [R=301] - Redirección permanente
# [R=302] - Redirección temporal
# [L] - Última regla (detener procesamiento)
# [NC] - Sin mayúsculas (insensible a mayúsculas)
# [QSA] - Añadir cadena de consulta
# [NE] - Sin escapeLista de verificación de redirecciones .htaccess
Antes de implementar redirecciones:
- Prueba las redirecciones con 302 antes de convertirlas en 301
- Verifica que no existan bucles de redirección
- Comprueba el comportamiento en móvil y escritorio
- Prueba con URLs tanto con www como sin www
- Verifica el forzado de HTTPS si aplica
- Monitorea Google Search Console para errores de rastreo
- Actualiza los enlaces internos para apuntar a las nuevas URLs
- Crea una copia de seguridad de tu archivo .htaccess
Recuerda: Una estrategia de redirección bien planificada preserva tu posicionamiento SEO y asegura una experiencia de usuario fluida durante las migraciones o reestructuraciones del sitio.
Redirecciones .htaccess en Nginx, registros y plugins
Cómo convertir redirecciones .htaccess a Nginx
Aunque .htaccess es específico de Apache, entender cómo se traducen las reglas a NGINX es crucial para los desarrolladores que trabajan en múltiples plataformas:
Conversión de Apache (.htaccess) a NGINX:
| Directiva Apache | Equivalente NGINX |
|---|---|
Redirect 301 /antigua /nueva | return 301 /nueva; |
RewriteRule ^antigua$ /nueva [R=301,L] | location = /antigua { return 301 /nueva; } |
RewriteCond %{HTTPS} off | if ($scheme != 'https') |
Ejemplo de bloque de servidor NGINX:
server {
listen 80;
server_name ejemplo.com;
# Redirección permanente a HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name ejemplo.com;
# Reglas de reescritura de WordPress
location / {
try_files $uri $uri/ /index.php?$args;
}
}Redirecciones por idioma y detrás de balanceadores de carga
1. Redirecciónes para sitios multilingües
# Redirigir basándose en la cabecera Accept-Language
RewriteEngine On
RewriteCond %{HTTP:Accept-Language} ^de [NC]
RewriteRule ^$ /de/ [L,R=302]
RewriteCond %{HTTP:Accept-Language} ^fr [NC]
RewriteRule ^$ /fr/ [L,R=302]Google desaconseja redirigir automáticamente según el idioma que supones del usuario, porque puede impedir que usuarios y buscadores lleguen a todas las versiones; es mejor enlazar a las demás versiones (sitios multirregionales).
2. Detección de HTTPS/HTTP para balanceadores de carga
# Manejar la cabecera X-Forwarded-Proto de balanceadores de carga
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]Depurar .htaccess con el registro de reescritura
Habilitar registro detallado de reescritura:
# Configuración del virtual host (no .htaccess), Apache 2.4+
LogLevel alert rewrite:trace3
# Registro a archivo
ErrorLog ${APACHE_LOG_DIR}/rewrite.logComandos comunes de depuración:
# Comprobar si mod_rewrite está cargado
apache2ctl -M | grep rewrite
# Probar la sintaxis de la configuración principal (no lee .htaccess)
apache2ctl configtest
# Activar mod_rewrite (Debian/Ubuntu)
a2enmod rewrite
# Ver registros de error de Apache
tail -f /var/log/apache2/error.logapache2ctl configtest solo revisa la configuración principal. Apache lee .htaccess en cada petición, así que un error de sintaxis en ese archivo aparece como un error 500 al pedir una página (guía de .htaccess).
Lista de verificación de migración para sitios grandes
Al migrar un sitio WordPress grande con miles de redirecciones:
Fase de inventario
- Exporta todas las URLs de Google Analytics
- Rastrea el sitio existente con Screaming Frog
- Exporta todas las páginas indexadas de Google Search Console
- Documenta todos los errores 404
Fase de planificación
- Mapea las URLs antiguas a las nuevas
- Agrupa las redirecciones por patrón
- Prioriza las páginas de alto tráfico
- Planifica las cadenas de redirección para minimizar saltos
Fase de implementación
- Prueba las redirecciones en el entorno de staging
- Implementa en lotes
- Monitorea los tiempos de respuesta del servidor
- Verifica la ausencia de bucles de redirección
Fase de verificación
- Usa verificadores de redirección en línea
- Monitorea Search Console para errores de rastreo
- Verifica los tiempos de respuesta del servidor
- Comprueba que Google muestra las URL nuevas en los resultados
Fase de limpieza
- Elimina las redirecciones temporales 302 (conviérte a 301)
- Elimina reglas innecesarias
- Documenta todas las redirecciones para referencia futura
- Actualiza el sitemap XML con las nuevas URLs
Alternativas con plugins de WordPress
Para usuarios no técnicos o gestión compleja de redirecciones:
Redirection
- Gratuito, ampliamente utilizado
- Soporta patrones regex
- Registro de 404
- Funcionalidad de importación/exportación
Yoast SEO Premium
- Gestor de redirecciones integrado
- Monitoreo automático de 404
- Integración fluida con WordPress
- Soporte de regex
Safe Redirect Manager
- Plugin de WordPress.org
- Redirecciónes basadas en lista blanca
- Selección de código de estado HTTP
- Compatible con multisitio
Rank Math SEO
- Suite SEO completa
- Redirecciónes 301/302/307
- Monitoreo de 404
- Importación masiva de 301
Estructura predeterminada de .htaccess en WordPress
WordPress genera un archivo .htaccess predeterminado cuando habilitas los permalinks bonitos. Entender su estructura es crucial para añadir reglas personalizadas:
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPressReglas .htaccess para WordPress Multisitio
WordPress Multisitio requiere configuración adicional:
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
RewriteRule . index.php [L]Caché del navegador y compresión en .htaccess
Cabeceras de caché del navegador
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
</IfModule>
# Compresión GZIP
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript
</IfModule>Redirecciones al migrar a HTTPS o a otro dominio
Migración de HTTP a HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]Migración de dominio
RewriteEngine On
RewriteCond %{HTTP_HOST} ^dominio-antiguo\.com$ [NC]
RewriteRule (.*)$ https://www.dominio-nuevo.com/$1 [R=301,L]Cambios en la estructura de URL
RewriteEngine On
RewriteRule ^blog/([0-9]{4})/([0-9]{2})/(.+)$ /artículos/$3 [R=301,L]Bloquear hotlinking y forzar descargas con .htaccess
Protección contra hotlinking
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https://(www\.)?tudominio\.com [NC]
RewriteRule \.(jpg|jpeg|png|gif|webp)$ - [F]La condición !^$ deja pasar las peticiones sin cabecera Referer, así que no bloquea las visitas directas ni los navegadores que la suprimen. Ten en cuenta que Referer es opcional y se puede falsificar (cuándo no usar mod_rewrite).
Forzar descarga de archivos
<FilesMatch "\.pdf$">
Header set Content-Disposition attachment
</FilesMatch>Cómo trata Google las redirecciones 301, 302, 307 y 308
La documentación de Google sobre redirecciones (Redirecciones y la Búsqueda de Google) las divide en dos grupos. Googlebot sigue las redirecciones permanentes (301 y 308), y el sistema de indexación las usa como señal de que el destino debe ser la URL canónica, de modo que en los resultados aparece la URL nueva. Las temporales (302, 303 y 307) también se siguen, pero no cuentan como señal canónica, así que la URL de origen suele quedarse en los resultados. Google ordena además los métodos según la fiabilidad con que puede interpretarlos: una redirección del lado del servidor, como la de .htaccess, va por delante de meta refresh y de JavaScript.
Esto afecta directamente a .htaccess, porque los dos módulos de Apache usan por defecto el código temporal. Redirect sin argumento de estado envía una 302 (mod_alias), y RewriteRule con la bandera [R] sin valor también envía una 302 (banderas de RewriteRule). Una migración escrita con [R,L] en lugar de [R=301,L] publica redirecciones temporales sin ningún mensaje de error.
Las cadenas también tienen un límite. Los rastreadores de Google siguen por defecto hasta 10 saltos de redirección (códigos de estado HTTP), y la guía de cambios de sitio pide no pasar de 3 saltos y quedarse por debajo de 5 como máximo, porque cada salto añade latencia (traslado de sitios con cambios de URL). La misma guía recomienda mantener las redirecciones de una migración el mayor tiempo posible, en general al menos un año.
Por qué una RewriteRule con barra inicial no coincide en .htaccess
La causa más habitual de que una regla copiada de la configuración del servidor no haga nada en .htaccess es la barra inicial. En contexto de directorio (.htaccess y <Directory>), Apache quita la ruta del directorio, incluida la barra final, antes de comparar el patrón. La documentación de mod_rewrite lo dice claramente: esa cadena nunca empieza por barra, de modo que un patrón que empieza por ^/ nunca coincide en contexto de directorio.
# Configuración del virtual host: coincide con /pagina-antigua/
RewriteRule ^/pagina-antigua/$ /pagina-nueva/ [R=301,L]
# .htaccess en la raíz del documento: la misma redirección
RewriteRule ^pagina-antigua/$ /pagina-nueva/ [R=301,L]El patrón tampoco ve el nombre del host ni la cadena de consulta. Para comprobarlos usa una RewriteCond sobre %{HTTP_HOST} o %{QUERY_STRING}, y %{REQUEST_URI} cuando necesites la ruta completa. Dos ajustes del servidor deciden si las reglas llegan a ejecutarse: la reescritura en .htaccess necesita Options FollowSymLinks o SymLinksIfOwnerMatch, y tanto RewriteRule como Redirect necesitan AllowOverride FileInfo. El tutorial de .htaccess de Apache propone una prueba rápida: escribe una palabra sin sentido, por ejemplo TestMe, en el archivo y recarga la página. Si no aparece un error 500, está activo AllowOverride None y Apache ignora el archivo.
Diferencia entre las banderas L y END en .htaccess
En .htaccess, [L] solo termina la pasada actual por las reglas. La documentación de banderas explica que, en contexto de directorio, la petición reescrita puede volver a procesarse desde la primera regla, y eso puede provocar bucles. [END] detiene todo el procesamiento de reescritura para esa petición, y la documentación la considera la bandera preferible para la mayoría de las reglas de directorio. En la configuración del servidor o del virtual host las dos se comportan igual.
La misma página aclara tres detalles que explican muchas redirecciones rotas:
[R]sin[L]pasa la URL reescrita a la regla siguiente, lo que puede generar avisosInvalid URI in requesten el registro; por eso conviene escribir[R=301,L].- Con un estado fuera del rango 300 a 399, por ejemplo
[R=410], la sustitución se descarta y la URL de destino nunca se envía.[G]y[F]expresan esa intención con más claridad. [QSD](Apache 2.4.0 y posteriores) descarta la cadena de consulta, y si se combinan[QSD]y[QSA], prevalece[QSD].
Cuándo recomienda Apache Redirect en lugar de RewriteRule
La guía del propio equipo de Apache, Cuándo no usar mod_rewrite, presenta mod_rewrite como último recurso y dice que las redirecciones simples de una URL o de un grupo de URL deben hacerse con Redirect o RedirectMatch. Redirect conserva la información de ruta, así que Redirect "/one/" "http://one.example.com/" también traslada /one/two.html. Solo coincide con segmentos de ruta completos: /service no captura /servicefoo.txt. Pasa los parámetros GET al destino, mientras que el cuerpo de las peticiones POST se descarta (mod_alias).
Para fijar un nombre de host canónico, la misma guía usa un bloque <If> en lugar de RewriteCond:
<If "req('Host') != 'www.example.com'">
Redirect "/" "https://www.example.com/"
</If>También señala que el patrón de controlador frontal que WordPress escribe en .htaccess (enviar a index.php todo archivo y directorio que no exista) puede expresarse como FallbackResource /index.php, que funciona también en .htaccess. En una instalación de WordPress deja intacto el bloque generado, porque WordPress reescribe todo lo que hay entre # BEGIN WordPress y # END WordPress. La regla práctica es usar un solo módulo por URL: no dejes que un Redirect y una RewriteRule coincidan con la misma ruta.
Dónde guardar miles de redirecciones en lugar de .htaccess
Una lista larga de redirecciones en .htaccess se paga en cada petición. Según el tutorial de .htaccess, cuando se permiten estos archivos, httpd los busca en cada directorio y en todos los superiores, y vuelve a cargar el archivo cada vez que se pide un documento, incluidas las imágenes, el CSS y el JavaScript. La misma página recomienda la configuración principal del servidor siempre que tengas acceso a ella.
La respuesta de Apache para tablas grandes es RewriteMap. Solo puede declararse en la configuración del servidor o del virtual host, nunca en .htaccess (guía de RewriteMap). Un mapa de texto plano contiene una clave y un valor separados por un espacio en cada línea, y httpd guarda en caché las claves consultadas hasta que cambia la fecha de modificación del archivo. Un mapa dbm: generado con httxt2dbm está indexado, así que las búsquedas son más rápidas que en un archivo de texto:
# Configuración del virtual host, no .htaccess
RewriteEngine On
RewriteMap redirects "dbm:/etc/apache2/redirects.map"
RewriteCond "${redirects:%{REQUEST_URI}}" !=""
RewriteRule "^" "${redirects:%{REQUEST_URI}}" [R=301,L]# redirects.txt: un par "ruta-antigua ruta-nueva" por línea
# /pagina-antigua/ /pagina-nueva/
httxt2dbm -i redirects.txt -o redirects.mapLa condición omite la redirección cuando la ruta no está en el mapa, de modo que las URL sin entrada siguen hasta WordPress como siempre.
Explora nuestra optimización de velocidad WordPress para llevar tu proyecto más lejos.






