htaccess - rewrite mod apache
Referencia: mod_rewrite, reescritura de URL y "enlaces bonitos" explicados (4)
Alternativas a mod_rewrite
Se pueden lograr muchos esquemas básicos de URL virtuales sin utilizar RewriteRules. Apache allows PHP scripts to be invoked without .php
extension, and with a virtual PATH_INFO
argument.
Use the PATH_INFO , Luke
Nowadays
AcceptPathInfo On
is often enabled by default. Which basically allows.php
and other resource URLs to carry a virtual argument:http://example.com/script.php/virtual/path
Now this
/virtual/path
shows up in PHP as$_SERVER["PATH_INFO"]
where you can handle any extra arguments however you like.This isn''t as convenient as having Apache separate input path segments into
$1
,$2
,$3
and passing them as distinct$_GET
variables to PHP. It''s merely emulating "pretty URLs" with less configuration effort.Enable MultiViews to hide the
.php
extensionThe simplest option to also eschew
.php
"file extensions" in URLs is enabling:Options +MultiViews
This has Apache select
article.php
for HTTP requests on/article
due to the matching basename. And this works well together with the aforementioned PATH_INFO feature. So you can just use URLs likehttp://example.com/article/virtual/title
. Which makes sense if you have a traditional web application with multiple PHP invocation points/scripts.Note that MultiViews has a different/broader purpose though. It incurs a very minor performance penalty, because Apache always looks for other files with matching basenames. It''s actually meant for Content-Negotiation , so browsers receive the best alternative among available resources (such as
article.en.php
,article.fr.php
,article.jp.mp4
).SetType or SetHandler for extensionless
.php
scriptsA more directed approach to avoid carrying around
.php
suffixes in URLs is configuring the PHP handler for other file schemes. The simplest option is overriding the default MIME/handler type via.htaccess
:DefaultType application/x-httpd-php
This way you could just rename your
article.php
script to justarticle
(without extension), but still have it processed as PHP script.Now this can have some security and performance implications, because all extensionless files would be piped through PHP now. Therefore you can alternatively set this behaviour for individual files only:
<Files article> SetHandler application/x-httpd-php # or SetType </Files>
This is somewhat dependent on your server setup and the used PHP SAPI. Common alternatives include
ForceType application/x-httpd-php
orAddHandler php5-script
.Again take note that such settings propagate from one
.htaccess
to subfolders. You always should disable script execution (SetHandler None
andOptions -Exec
orphp_flag engine off
etc.) for static resources, and upload/ directories etc.Other Apache rewriting schemes
Among its many options, Apache provides
mod_alias
features - which sometimes work just as well asmod_rewrite
s RewriteRules. Note that most of those must be set up in a<VirtualHost>
section however, not in per-directory.htaccess
config files.ScriptAliasMatch
is primarily for CGI scripts, but also ought to works for PHP. It allows regexps just like anyRewriteRule
. In fact it''s perhaps the most robust option to configurate a catch-all front controller.And a plain
Alias
helps with a few simple rewriting schemes as well.Incluso una
ErrorDocument
directiva simple podría usarse para permitir que un script PHP maneje rutas virtuales. Tenga en cuenta que esta es una solución arriesgada, sin embargo, prohíbe todo excepto las solicitudes GET e inunda el error.log por definición.
Consulte http://httpd.apache.org/docs/2.2/urlmapping.html para obtener más sugerencias.
"Enlaces bonitos" es un tema que a menudo se solicita, pero rara vez se explica completamente. mod_rewrite es una forma de hacer "enlaces bonitos", pero es compleja y su sintaxis es muy escueta, difícil de asimilar y la documentación asume un cierto nivel de competencia en HTTP. ¿Puede alguien explicar en términos simples cómo funcionan los "enlaces bonitos" y cómo se puede usar mod_rewrite para crearlos?
Otros nombres comunes, alias, términos para URLs limpias: URLs RESTful, URL fáciles de usar, URLs amigables con SEO, Slugging, URL MVC (probablemente un nombre inapropiado)
Referencias
tiene muchos otros recursos excelentes para comenzar:
- Serverfault: Todo lo que siempre quisiste saber sobre mod_rewrite
(Tenga en cuenta que debe eliminar la barra inclinada en los prefijos^/
pattern para el uso de.htaccess
). - Qué hacer y qué no en las funciones ocultas de mod_rewrite .
- Mire a través de nuestras preguntas y respuestas más populares sobre reescritura .
- Guía de redirección y reasignación de Apache.
- AskApache ultimate .htaccess guide
- Y la etiqueta mod-rewrite wiki hace referencia .
Y resúmenes de expresiones regulares amigables para recién llegados incluso:
- Nuestra wiki de etiquetas regex para un compendio de sintaxis.
- Y el breve resumen de expresiones regulares de Apache .
- Else regexp.info para obtener información regexp.info fácil de entender.
Marcadores de posición utilizados a menudo
-
.*
coincide con cualquier cosa, incluso una cadena vacía. No desea utilizar este patrón en todas partes, pero a menudo en la última regla alternativa. -
[^/]+
se usa con más frecuencia para segmentos de ruta. Coincide con cualquier cosa que no sea la barra diagonal. -
/d+
solo coincide con cadenas numéricas. -
/w+
coincide con caracteres alfanuméricos. Básicamente es una abreviatura de[A-Za-z0-9_]
. -
[/w/-]+
para segmentos de ruta estilo "slug", usando letras, números, guiones-
y_
-
[/w/-.,]+
agrega puntos y comas. Prefiere un/-
dash escapado[…]
clases. -
/.
denota un período literal. De lo contrario.
fuera de[…]
es marcador de posición para cualquier símbolo.
Cada uno de estos marcadores de posición por lo general está envuelto en (…)
paréntesis como grupo de captura. Y todo el patrón a menudo en ^………$
start + end marker. Citar "patrones" es opcional.
RewriteRules
Los siguientes ejemplos están centrados en PHP y son un poco más incrementales, más fáciles de adaptar para casos similares. Son solo resúmenes, a menudo vinculan a más variaciones o preguntas y respuestas detalladas.
Mapeo estático
/contact
,/about
Acortar algunos nombres de página a esquemas de archivos internos es muy simple:
RewriteRule ^contact$ templ/contact.html RewriteRule ^about$ about.php
Identificadores numéricos
/object/123
También es fácil introducir accesos directos como
http://example.com/article/531
a las secuencias de comandos PHP existentes. El marcador de posición numérico solo se puede reasignar a un parámetro$_GET
:RewriteRule ^article/(/d+)$ article-show.php?id=$1 # └───────────────────────────┘
Marcadores de posición tipo Slug
/article/with-some-title-slug
Puede extender fácilmente esa regla para permitir marcadores de posición
/article/title-string
:RewriteRule ^article/([/w-]+)$ article-show.php?title=$1 # └────────────────────────────────┘
Tenga en cuenta que su script debe poder (o adaptarse) mapear esos títulos a los ID de la base de datos. RewriteRules por sí solo no puede crear o adivinar información de la nada.
Babosas con prefijos numéricos
/readable/123-plus-title
Por lo tanto, a menudo verá rutas mixtas
/article/529-title-slug
usadas en la práctica:RewriteRule ^article/(/d+)-([/w-]+)$ article.php?id=$1&title=$2 # └───────────────────────────────┘
Ahora bien, podría omitir pasar el
title=$2
todos modos, ya que normalmente su script se basará en la id de la base de datos. El-title-slug
ha convertido en una decoración de URL arbitraria.Uniformidad con listas alternativas
/foo/…
/bar/…
/baz/…
Si tiene reglas similares para varias rutas de página virtuales, puede hacer coincidirlas y compactarlas con
|
listas alternativas Y nuevamente solo reasignarlos a los parámetros GET internos:# ┌─────────────────────────┐ RewriteRule ^(blog|post|user)/(/w+)$ disp.php?type=$1&id=$2 # └───────────────────────────────────┘
Puede dividirlos en
RewriteRule
individuales si esto seRewriteRule
demasiado complejo.Envío de URL relacionadas a diferentes backends
/date/SWITCH/backend
Un uso más práctico de las listas alternativas es asignar rutas de solicitud a distintas secuencias de comandos. Por ejemplo, para proporcionar URL uniformes para una aplicación web más antigua y más nueva en función de las fechas:
# ┌─────────────────────────────┐ # │ ┌───────────┼───────────────┐ RewriteRule ^blog/(2009|2010|2011)/([/d-]+)/?$ old/blog.php?date=$2 RewriteRule ^blog/(/d+)/([/d-]+)/?$ modern/blog/index.php?start=$2 # └──────────────────────────────────────┘
Esto simplemente reasigna las publicaciones 2009-2011 en una secuencia de comandos, y todos los demás años implícitamente a otro controlador. Tenga en cuenta que la regla más específica viene primero . Cada script puede usar diferentes parámetros GET.
Otros delimitadores que solo
/
barras diagonales
/user-123-name
Lo más habitual es que veas RewriteRules para simular una estructura de directorio virtual. Pero no estás obligado a ser poco creativo. También puede usar
-
guiones para segmentar o estructurar.RewriteRule ^user-(/d+)$ show.php?what=user&id=$1 # └──────────────────────────────┘ # This could use `(/w+)` alternatively for user names instead of ids.
Para el esquema common
/wiki:section:Page_Name
scheme de/wiki:section:Page_Name
:RewriteRule ^wiki:(/w+):(/w+)$ wiki.php?sect=$1&page=$2 # └─────┼────────────────────┘ │ # └────────────────────────────┘
Ocasionalmente, es adecuado alternar entre
/
-delimiters y:
o.
en la misma regla, incluso. O tenga dos RewriteRules nuevamente para asignar variantes a diferentes scripts.Trailing
/
slash opcional
/dir
=/dir/
Al optar por rutas de estilo de directorio, puede hacer que sea alcanzable con y sin una final /
RewriteRule ^blog/([/w-]+)/?$ blog/show.php?id=$1 # ┗┛
Ahora esto maneja tanto
http://example.com/blog/123
como/blog/123/
. Y el enfoque/?$
Es fácil de agregar a cualquier otra RewriteRule.Segmentos flexibles para rutas virtuales
.*/.*/.*/.*
La mayoría de las reglas con las que se encontrará mapean un conjunto restringido de
/…/
segmentos de ruta de recursos a parámetros GET individuales. Sin embargo, algunos scripts manejan una cantidad variable de opciones . El motor Apachege regexp no permite la opción de un número arbitrario de ellos. Pero puede expandirlo fácilmente en un bloque de reglas usted mismo:Rewriterule ^(/w+)/?$ in.php?a=$1 Rewriterule ^(/w+)/(/w+)/?$ in.php?a=$1&b=$2 Rewriterule ^(/w+)/(/w+)/(/w+)/?$ in.php?a=$1&b=$2&c=$3 # └─────┴─────┴───────────────────┴────┴────┘
Si necesita hasta cinco segmentos de ruta, copie este esquema en cinco reglas. Por supuesto, puede usar un marcador de posición
[^/]+
más específico cada uno. Aquí el orden no es tan importante, ya que ninguno se superpone. Por lo tanto, tener las rutas más frecuentemente usadas primero está bien.Alternativamente, puede utilizar los parámetros de matriz de PHP a través de
?p[]=$1&p[]=$2&p[]=3
cadena de consulta aquí - si su secuencia de comandos simplemente los prefiere pre-divididos. (Aunque es más común usar solo una regla catch-all, y dejar que el script expanda los segmentos del REQUEST_URI).Vea también: ¿Cómo transformo mis segmentos de ruta de URL en pares de clave-valor de cadena de consulta?
Segmentos opcionales
prefix/opt?/.*
Una variación común es tener prefijos opcionales dentro de una regla. Esto generalmente tiene sentido si tiene cadenas estáticas o marcadores de posición más restringidos:
RewriteRule ^(/w+)(?:/([^/]+))?/(/w+)$ ?main=$1&opt=$2&suffix=$3
Ahora el patrón más complejo
(?:/([^/])+)?
simplemente envuelve un grupo que no captura(?:…)
y lo hace opcional)?
. El marcador de posición contenido([^/]+)
sería un patrón de sustitución$2
, pero estará vacío si no hay una ruta/…/
media.Captura el resto
/prefix/123-capture/…/*/…whatever…
Como se dijo antes, a menudo no desea patrones de reescritura genéricos. Sin embargo, tiene sentido combinar comparaciones estáticas y específicas con
.*
veces.RewriteRule ^(specific)/prefix/(/d+)(/.*)?$ speci.php?id=$2&otherparams=$2
Esto opcionalmente cualesquiera
/…/…/…
segmentos de ruta final. Lo cual, por supuesto, requiere que el script de manejo los divida, y variabl-ify extrajo los parámetros por sí mismo (que es lo que hacen los frameworks "MVC" ).Archivo de rastreo "extensiones"
/old/path.HTML
Las URL realmente no tienen extensiones de archivo. De esto se trata toda esta referencia (= las URL son localizadores virtuales, no necesariamente una imagen directa del sistema de archivos). Sin embargo, si antes tenía una asignación de archivos 1: 1, puede crear reglas más simples:
RewriteRule ^styles/([/w/./-]+)/.css$ sass-cache.php?old_fn_base=$1 RewriteRule ^images/([/w/./-]+)/.gif$ png-converter.php?load_from=$2
Otros usos comunes son volver a
.html
rutas.html
obsoletas a manejadores.php
más nuevos, o simplemente aliasar nombres de directorios solo para archivos individuales (reales / reales).Ping-Pong (redirige y reescribe al unísono)
/ugly.html
← →/pretty
Entonces, en algún momento estás reescribiendo tus páginas HTML para llevar solo enlaces bonitos, como lo describe deceze . Mientras tanto, seguirás recibiendo solicitudes para las rutas antiguas , a veces incluso desde marcadores. Como solución alternativa , puede hacer ping-pong navegadores para mostrar / establecer las nuevas URL.
Este truco común implica enviar un redireccionamiento de 30x / ubicación siempre que una URL entrante siga el esquema de nombres obsoleto / feo. Los navegadores luego volverán a solicitar la URL nueva / bonita, que luego se reescribirá (solo internamente) en la ubicación original o nueva.
# redirect browser for old/ugly incoming paths RewriteRule ^old/teams/.html$ /teams [R=301,QSA,END] # internally remap already-pretty incoming request RewriteRule ^teams$ teams.php [QSA,END]
Tenga en cuenta que este ejemplo solo utiliza
[END]
lugar de[L]
para alternar de forma segura. Para versiones anteriores de Apache 2.2 puede usar otras soluciones, además de reasignar parámetros de cadena de consulta, por ejemplo: redirigir feo a URL bonita, reasignar de nuevo a la ruta fea, sin bucles infinitosEspacios ␣ en patrones
/this+that+
No es tan bonito en las barras de direcciones del navegador, pero puede usar espacios en las URL. Para los patrones de reescritura use
/␣
escapados/␣
escapados con barras invertidas. De lo contrario, solo"
-quote todo el patrón o sustitución:RewriteRule "^this [/w ]+/(.*)$" "index.php?id=$1" [L]
Los clientes serializan URL con
+
o%20
para espacios. Sin embargo, en RewriteRules se interpretan con caracteres literales para todos los segmentos de ruta relativos.
Duplicados frecuentes:
Captura todo para una secuencia de comandos central de despachador / controlador frontal
RewriteCond %{REQUEST_URI} !-f RewriteCond %{REQUEST_URI} !-d RewriteRule ^.*$ index.php [L]
Que a menudo es utilizado por frameworks de PHP o scripts de WebCMS / portal. La división de la ruta real se maneja en PHP usando
$_SERVER["REQUEST_URI"]
. Así que, conceptualmente, es más bien lo opuesto a la gestión de URL "por mod_rewrite". (Simplemente useFallBackResource
enFallBackResource
lugar).Eliminar
www.
del nombre de hostTenga en cuenta que esto no copia una cadena de consulta a lo largo, etc.
# ┌──────────┐ RewriteCond %{HTTP_HOST} ^www/.(.+)$ [NC] │ RewriteRule ^(.*)$ http://%1/$1 [R=301,L] │ # ↓ └───┼────────────┘ # └───────────────┘
Ver también:
· Reescritura de URL para diferentes protocolos en .htaccess
· Htaccess genérico redirecciona www a no www
· .htaccess - cómo forzar "www." de una manera genérica?Tenga en cuenta que los combos RewriteCond / RewriteRule pueden ser más complejos, con coincidencias (
%1
y$1
) que interactúan en ambas direcciones, incluso:
Manual de Apache - introducción mod_rewrite , Copyright 2015 The Apache Software Foundation, AL-2.0Redirigir a
HTTPS://
RewriteCond %{SERVER_PORT} 80 RewriteRule ^(.*)$ https://example.com/$1 [R,L]
Ver también: https://wiki.apache.org/httpd/RewriteHTTPToHTTPS
"Eliminar" la extensión de PHP
RewriteCond %{REQUEST_FILENAME}.php -f RewriteRule ^(.+)$ $1.php [L] # or [END]
Vea también: Eliminar la extensión .php con mod_rewrite
Aliasing antiguas rutas .html a scripts .php
Ver: http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility
Vuelva a escribir desde la URL como "/ página" a un script como "/index.php/page"
Ver mod_rewrite, php y el archivo .htaccess
Redirigir el subdominio a una carpeta
Consulte ¿Cómo puedo hacer que mi htaccess funcione (subdominios)?
Prevalent .htaccess
trampas
Ahora toma esto con un grano de sal. No todos los consejos se pueden generalizar a todos los contextos. Esto es solo un simple resumen de obstáculos conocidos y algunos no evidentes:
Habilita
mod_rewrite
y.htaccess
Para usar RewriteRules en los archivos de configuración por directorio, debe:
Verifique que su servidor tenga
AllowOverride All
habilitado . De lo contrario, las directivas.htaccess
por directorio se ignorarán y RewriteRules no funcionará.Obviamente tiene
mod_rewrite
habilitado en su sección de móduloshttpd.conf
.Prefiere cada lista de reglas con
RewriteEngine On
. Mientras mod_rewrite está implícitamente activo en las secciones<VirtualHost>
y<Directory>
, los archivos.htaccess
por directorio necesitan que se convoque de forma individual.
La barra inclinada
^/
no coincidiráNo debe iniciar sus patrones
.htaccess
RewriteRule con^/
normalmente:RewriteRule ^/article//d+$ … ↑
Esto se ve a menudo en los tutoriales antiguos. Y solía ser correcto para versiones antiguas de Apache 1.x. En la actualidad, las rutas de solicitud están convenientemente relacionadas con el directorio en
.htaccess
RewriteRules. Solo deja la entrada/
salida.· Tenga en cuenta que la barra diagonal sigue siendo correcta en las secciones
<VirtualHost>
. ¿Por qué a menudo lo ves^/?
opcionalizado para la paridad de reglas.
· O al usar unRewriteCond %{REQUEST_URI}
, igual coincidirías para un/
.
· Consulte también Webmaster.SE: ¿Cuándo es necesaria la barra diagonal (/) en los patrones de mod_rewrite?<IfModule *>
wrappers begone!Probablemente hayas visto esto en muchos ejemplos:
<IfModule mod_rewrite.c> Rewrite… </IfModule>
- Tiene sentido en las secciones
<VirtualHost>
- si se combinó con otra opción alternativa, como ScriptAliasMatch. (Pero nadie hace eso) - Y se distribuye comúnmente para los conjuntos de reglas
.htaccess
predeterminados con muchos proyectos de código abierto. Ahí solo se entiende como alternativa y mantiene las URL "feas" como predeterminadas.
Sin embargo , no desea eso normalmente en sus propios archivos
.htaccess
.- En primer lugar, mod_rewrite no se desconecta aleatoriamente. (Si lo hiciera, tendrías problemas mayores).
- Si realmente estuviera deshabilitada, sus RewriteRules aún no funcionarían de todos modos.
- Está destinado a prevenir errores HTTP
500
. Lo que generalmente logra es agraciar a los usuarios con errores HTTP404
lugar. (No mucho más fácil de usar si lo piensas). - Prácticamente solo suprime las entradas de registro más útiles o los correos de notificación del servidor. No sabría por qué sus RewriteRules nunca funcionan.
Lo que parece atractivo como salvaguardia generalizada, a menudo resulta ser un obstáculo en la práctica.
- Tiene sentido en las secciones
No use
RewriteBase
menos que sea necesarioMuchos ejemplos de copiar y pegar contienen una directiva
RewriteBase /
. Que pasa a ser el valor predeterminado implícito de todos modos. Entonces realmente no necesitas esto. Es una solución alternativa para los sofisticados esquemas de reescritura de VirtualHost y las rutas DOCUMENT_ROOT mal analizadas para algunos proveedores de servicios compartidos.Tiene sentido usar con aplicaciones web individuales en subdirectorios más profundos. Puede acortar los patrones RewriteRule en tales casos. En general, es mejor preferir los especificadores de ruta relativos en los conjuntos de reglas por directorio.
Consulte también Cómo funciona RewriteBase en .htaccess
Deshabilitar
MultiViews
cuando las rutas virtuales se superponenLa reescritura de URL se usa principalmente para admitir rutas entrantes virtuales . Comúnmente, solo tiene un script de despachador (
index.php
) o algunos manejadores individuales (articles.php
,wiki.php
,wiki.php
, ...). Esto último puede chocar con rutas RewriteRule virtuales similares.Una solicitud para
/article/123
por ejemplo, podríaarticle.php
aarticle.php
con un/123
PATH_INFO implícitamente. Tendría que proteger sus reglas con elRewriteCond
!-f
+!-d
, y / o desactivar el soporte PATH_INFO, o simplemente deshabilitar lasOptions -MultiViews
.Lo cual no quiere decir que siempre tienes que hacerlo . Content-Negotiation es solo un automatismo para recursos virtuales.
Ordenar es importante
Mira todo lo que siempre quisiste saber sobre mod_rewrite si aún no lo has hecho. La combinación de múltiples RewriteRules a menudo conduce a la interacción. Esto no es algo para evitar habitualmente por
[L]
bandera, sino un esquema que abarcará una vez versado. Puede volver a reescribir rutas virtuales de una regla a otra, hasta que llegue a un controlador de destino real.Aún así, a menudo querría tener las reglas más específicas (patrones fijos de cadena
/forum/…
, o marcadores de posición más restrictivos[^/.]+
) En las reglas iniciales . Las reglas genéricas slurp-all (.*
) Se dejan mejor a las posteriores . (Una excepción esRewriteCond -f/-d
guard como bloque principal).Las hojas de estilo y las imágenes dejan de funcionar
Cuando introduce estructuras de directorio virtual
/blog/article/123
esto afecta las referencias de recursos relativos en HTML (como<img src=mouse.png>
). Que puede ser resuelto por:- Solo utilizando referencias absolutas del servidor
href="/old.html"
osrc="/logo.png"
- A menudo simplemente agregando
<base href="/index">
en su sección HTML<head>
. Esto revuelve implícitamente referencias relativas a lo que eran antes.
De forma alternativa, puede crear más RewriteRules para volver a vincular las rutas .css o
.png
a sus ubicaciones originales. Pero eso es innecesario, o incurre en redirecciones adicionales y dificulta el almacenamiento en caché.Ver también: CSS, JS e imágenes no se muestran con bonita URL
- Solo utilizando referencias absolutas del servidor
RewriteConds simplemente enmascara una RewriteRule
Una mala interpretación común es que RewriteCond bloquea múltiples RewriteRules (porque están organizadas visualmente juntas):
RewriteCond %{SERVER_NAME} localhost RewriteRule ^secret admin/tools.php RewriteRule ^hidden sqladmin.cgi
Lo cual no ocurre por defecto. Puede encadenarlos usando el indicador
[S=2]
. De lo contrario, tendrás que repetirlos. Aunque a veces puedes crear una regla primaria "invertida" para [END] reescribir el procesamiento temprano.QUERY_STRING exento de RewriteRules
No puede coincidir con
RewriteRule index.php/?x=y
, porque mod_rewrite se compara solo con rutas relativas por defecto. Puede unirlos por separado, sin embargo, a través de:RewriteCond %{QUERY_STRING} /b(?:param)=([^&]+)(?:&|$) RewriteRule ^add/(.+)$ add/%1/$1 # ←──﹪₁──┘
Consulte también ¿Cómo puedo unir las variables de cadena de consulta con mod_rewrite?
.htaccess
vs.<VirtualHost>
Si está utilizando RewriteRules en un archivo de configuración por directorio, preocuparse por el rendimiento de expresiones regulares no tiene sentido. Apache conserva patrones PCRE compilados más tiempo que un proceso PHP con un marco de enrutamiento común. Para sitios con mucho tráfico, sin embargo, debes considerar mover los conjuntos de reglas a la configuración del servidor vhost, una vez que hayan sido probados en batalla.
En este caso, prefiera el
^/?
prefijo del separador de directorio. Esto permite mover RewriteRules libremente entre PerDir y los archivos de configuración del servidor.Cuando algo no funciona
No temas.
Compare
access.log
yerror.log
A menudo puede averiguar cómo una RewriteRule se comporta mal solo con mirar su
error.log
yaccess.log
. Correlacione los tiempos de acceso para ver qué ruta de solicitud originalmente entró, y qué ruta / archivo Apache no pudo resolver (error 404/500).Esto no te dice que RewriteRule es el culpable. Pero los caminos finales inaccesibles como
/docroot/21-.itle?index.php
pueden/docroot/21-.itle?index.php
dónde inspeccionar más. De lo contrario, deshabilite las reglas hasta que obtenga algunas rutas predecibles.Habilita RewriteLog
Ver los documentos Apache RewriteLog . Para la depuración puede habilitarlo en las secciones vhost:
# Apache 2.2 RewriteLogLevel 5 RewriteLog /tmp/rewrite.log # Apache 2.4 LogLevel alert rewrite:trace5 #ErrorLog /tmp/rewrite.log
Eso produce un resumen detallado de cómo las rutas de solicitud entrantes son modificadas por cada regla:
[..] applying pattern ''^test_.*$'' to uri ''index.php'' [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php [..] applying pattern ''^index/.php$'' to uri ''index.php''
Lo cual ayuda a reducir las reglas excesivamente genéricas y los contratiempos de expresiones regulares.
Ver también:
· .htaccess no funciona (mod_rewrite)
· Consejos para depurar las reglas de reescritura de .htaccessAntes de hacer tu propia pregunta
Como ya sabrás, es muy adecuado para hacer preguntas sobre mod_rewrite. Ponlos on-topic al incluir investigaciones previas e intentos (evitar respuestas redundantes), demostrar comprensión básica de regex , y:
- Incluya ejemplos completos de URL de entrada, rutas de destino falsificadas nuevamente, su estructura de directorio real.
- El conjunto RewriteRule completo, pero también el único presunto defectuoso.
- Las versiones de Apache y PHP, el tipo de sistema operativo, el sistema de archivos, el entorno DOCUMENT_ROOT y el entorno
$_SERVER
PHP si se trata de una discrepancia de parámetros. - Un extracto de
access.log
yerror.log
para verificar a qué se resolvieron las reglas existentes. Mejor aún, un resumen derewrite.log
.
Esto genera respuestas más rápidas y más exactas, y las hace más útiles para los demás.
Comenta tu
.htaccess
Si copia ejemplos de algún lugar, tenga cuidado de incluir un
# comment and origin link
. Si bien es de mala educación omitir la atribución, a menudo realmente daña el mantenimiento más adelante. Documente cualquier código o fuente de tutorial. En particular, aunque no esté actualizado, debería estar aún más interesado en no tratarlos como cajas negras mágicas.No es "SEO" -URL
Descargo de responsabilidad: solo una mascota molesta. A menudo escuchas esquemas de reescritura de URL bastante conocidos como enlaces "SEO" o algo así. Si bien esto es útil para buscar en Google ejemplos, es un nombre inapropiado de fecha.
Ninguno de los motores de búsqueda modernos está realmente perturbado por
.html
y.php
en segmentos de ruta, o?id=123
cadenas de consulta para el caso. Los motores de búsqueda antiguos, como AltaVista, evitaron rastrear sitios web con rutas de acceso potencialmente ambiguas. Los rastreadores modernos a menudo incluso anhelan recursos web profundos.A qué URLs "bonitas" se debe usar desde el punto de vista conceptual es hacer que los sitios web sean fáciles de usar .
- Tener esquemas de recursos legibles y obvios.
- Asegurar que las URL sean de larga duración (AKA permalinks ).
- Proporcionando visibilidad a través de
/common/tree/nesting
.
Sin embargo, no sacrifique requisitos únicos para el conformismo.
Herramientas
Hay varias herramientas en línea para generar RewriteRules para la mayoría de las URL con parámetros GET:
- http://www.generateit.net/mod-rewrite/index.php
- http://www.ipdistance.com/mod_rewrite.php
- http://webtools.live2support.com/misc_rewrite.php
En su mayoría, simplemente genera [^/]+
marcadores de posición genéricos, pero es suficiente para sitios triviales.
Para ampliar la respuesta de deceze , quería brindar algunos ejemplos y explicaciones de algunas otras funcionalidades de mod_rewrite.
Todos los ejemplos siguientes suponen que ya ha incluido RewriteEngine On
en su archivo .htaccess
.
Reescribir el ejemplo
Tomemos este ejemplo:
RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-/+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]
La regla se divide en 4 secciones:
-
RewriteRule
: inicia la regla de reescritura -
^blog/([0-9]+)/([A-Za-z0-9-/+]+)/?$
- Esto se llama patrón, sin embargo, solo me referiré a él como el lado izquierdo de la regla: de qué quieres volver a escribir -
blog/index.php?id=$1&title=$2
- llamado la sustitución, o el lado derecho de una regla de reescritura - lo que desea volver a escribir -
[NC,L,QSA]
son indicadores de la regla de reescritura, separados por una coma, que explicaré más adelante
La reescritura anterior le permitiría vincular a algo como /blog/1/foo/
y realmente cargaría /blog/index.php?id=1&title=foo
.
Lado izquierdo de la regla
-
^
indica el inicio del nombre de la página, por lo que reescribiráexample.com/blog/...
pero noexample.com/foo/blog/...
- Cada conjunto de
(…)
paréntesis representa una expresión regular que podemos capturar como una variable en el lado derecho de la regla. En este ejemplo:- El primer conjunto de corchetes -
([0-9]+)
- coincide con una cadena con un mínimo de 1 carácter de longitud y con solo valores numéricos (es decir, 0-9). Esto se puede referenciar con$1
en el lado derecho de la regla - El segundo conjunto de paréntesis coincide con una cadena con un mínimo de 1 carácter de longitud, que contiene solo caracteres alfanuméricos (AZ, az, o 0-9) o
-
o+
(nota+
se escapó con una barra invertida, ya que sin escabullir esto se ejecutará como un personaje de repetición de expresiones regulares ). Esto se puede referenciar con$2
en el lado derecho de la regla
- El primer conjunto de corchetes -
-
?
significa que el carácter anterior es opcional, por lo que en este caso ambos/blog/1/foo/
y/blog/1/foo
se reescribirán en el mismo lugar -
$
indica que este es el final de la cadena que queremos unir
Banderas
Estas son opciones que se agregan entre corchetes al final de la regla de reescritura para especificar ciertas condiciones. Nuevamente, hay muchos indicadores diferentes que puede leer en la documentación , pero revisaré algunos de los indicadores más comunes:
NC
La bandera sin mayúsculas significa que la regla de reescritura no distingue entre mayúsculas y minúsculas, por lo que para la regla de ejemplo anterior, esto significaría que ambos /blog/1/foo/
y /BLOG/1/foo/
(o cualquier variación de esto) coincidirían.
L
La última bandera indica que esta es la última regla que debe procesarse. Esto significa que si y solo si esta regla coincide, no se evaluarán otras reglas en la ejecución de procesamiento de reescritura actual. Si la regla no coincide, todas las otras reglas se probarán en orden como de costumbre. Si no establece el indicador L
, todas las reglas siguientes se aplicarán a la URL reescrita posteriormente.
END
Desde Apache 2.4 también puede usar el indicador [END]
. Una regla de coincidencia con ella terminará por completo el procesamiento adicional de alias / reescritura. (Mientras que la bandera [L]
menudo puede desencadenar una segunda ronda, por ejemplo cuando se reescribe dentro o fuera de subdirectorios).
QSA
El indicador de apendizar de la cadena de consulta nos permite pasar variables adicionales a la URL especificada que se agregarán a los parámetros de obtención originales. Para nuestro ejemplo, esto significa que algo como /blog/1/foo/?comments=15
cargaría /blog/index.php?id=1&title=foo&comments=15
R
Esta bandera no es una que utilicé en el ejemplo anterior, pero creo que vale la pena mencionarla. Esto le permite especificar un redireccionamiento http, con la opción de incluir un código de estado (por ejemplo, R=301
). Por ejemplo, si quisieras hacer un redireccionamiento 301 en / myblog / a / blog / simplemente escribirías una regla como esta:
RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]
Reescribir las condiciones
Las condiciones de reescritura hacen que las reescrituras sean aún más potentes, lo que le permite especificar reescrituras para situaciones más específicas. Hay muchas condiciones sobre las que puede leer en la documentación , pero tocaré algunos ejemplos comunes y los explicaré:
# if the host doesn''t start with www. then add it and redirect
RewriteCond %{HTTP_HOST} !^www/.
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Esta es una práctica muy común, que precederá a su dominio con www.
(si no está ya allí) y ejecuta un redireccionamiento 301. Por ejemplo, cargar http://example.com/blog/
te redirigiría a http://www.example.com/blog/
# if it cant find the image, try find the image on another domain
RewriteCond %{REQUEST_URI} /.(jpg|jpeg|gif|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)$ http://www.example.com/$1 [L]
Esto es un poco menos común, pero es un buen ejemplo de una regla que no se ejecuta si el nombre del archivo es un directorio o archivo que existe en el servidor.
-
%{REQUEST_URI} /.(jpg|jpeg|gif|png)$ [NC]
solo ejecutará la reescritura para archivos con una extensión de archivo de jpg, jpeg, gif o png (sin distinción entre mayúsculas y minúsculas). -
%{REQUEST_FILENAME} !-f
verificará si el archivo existe en el servidor actual y solo ejecutará la reescritura si no lo hace -
%{REQUEST_FILENAME} !-d
verificará si el archivo existe en el servidor actual y solo ejecutará la reescritura si no lo hace - La reescritura intentará cargar el mismo archivo en otro dominio
Para comprender qué mod_rewrite necesita primero entender cómo funciona un servidor web. Un servidor web responde a las solicitudes HTTP . Una solicitud HTTP en su nivel más básico se ve así:
GET /foo/bar.html HTTP/1.1
Esta es la simple solicitud de un navegador a un servidor web que solicita la URL /foo/bar.html
desde allí. Es importante destacar que no solicita un archivo , solo solicita una URL arbitraria. La solicitud también puede verse así:
GET /foo/bar?baz=42 HTTP/1.1
Esta es una solicitud tan válida para una URL, y obviamente no tiene nada que ver con los archivos.
El servidor web es una aplicación que escucha en un puerto, acepta las solicitudes HTTP que entran en ese puerto y devuelve una respuesta. Un servidor web es totalmente libre de responder a cualquier solicitud de la manera que considere conveniente / de cualquier forma que usted haya configurado para responder. Esta respuesta no es un archivo, es una respuesta HTTP que puede o no tener que ver con archivos físicos en cualquier disco. Un servidor web no tiene que ser Apache, hay muchos otros servidores web que son solo programas que se ejecutan persistentemente y están conectados a un puerto que responde a las solicitudes HTTP. Puedes escribir uno tú mismo. Este párrafo tenía la intención de separarlo de cualquier idea de que las URL sean directamente iguales a los archivos, lo cual es realmente importante de entender. :)
La configuración predeterminada de la mayoría de los servidores web es buscar un archivo que coincida con la URL en el disco duro. Si la raíz del documento del servidor está configurada, por ejemplo, /var/www
, puede ver si el archivo /var/www/foo/bar.html
existe y, si es así, se lo /var/www/foo/bar.html
. Si el archivo termina en ".php" invocará al intérprete de PHP y luego devolverá el resultado. Toda esta asociación es completamente configurable; un archivo no tiene que terminar en ".php" para que el servidor web lo ejecute a través del intérprete de PHP, y la URL no tiene que coincidir con ningún archivo particular en el disco para que algo suceda.
mod_rewrite es una forma de reescribir el manejo interno de solicitudes. Cuando el servidor web recibe una solicitud de URL /foo/bar
, puede volver a escribir esa URL en otra cosa antes de que el servidor web busque un archivo en el disco para que coincida. Ejemplo simple:
RewriteEngine On
RewriteRule /foo/bar /foo/baz
Esta regla dice que cada vez que una solicitud coincida con "/ foo / bar", reescríbala a "/ foo / baz". La solicitud se manejará como si /foo/baz
hubiera solicitado en su lugar. Esto se puede usar para varios efectos, por ejemplo:
RewriteRule (.*) $1.html
Esta regla coincide con cualquier cosa ( .*
) Y la captura ( (..)
), luego la reescribe para agregar ".html". En otras palabras, si /foo/bar
era la URL solicitada, se manejará como si se hubiera solicitado /foo/bar.html
. Consulte http://regular-expressions.info para obtener más información sobre la coincidencia de expresiones regulares, la captura y los reemplazos.
Otra regla que se encuentra a menudo es esta:
RewriteRule (.*) index.php?url=$1
Esto, nuevamente, coincide con cualquier cosa y la reescribe en el archivo index.php con la URL solicitada originalmente adjuntada en el parámetro de consulta url
. Es decir, para todas y cada una de las solicitudes que ingresen, se ejecutará el archivo index.php y este archivo tendrá acceso a la solicitud original en $_GET[''url'']
, para que pueda hacer lo que quiera con él.
Principalmente usted pone estas reglas de reescritura en su archivo de configuración del servidor web . Apache también le permite * ponerlos en un archivo llamado .htaccess
dentro de la raíz de su documento (es decir, al lado de sus archivos .php).
* Si lo permite el archivo de configuración principal de Apache; es opcional, pero a menudo está habilitado.
Lo que mod_rewrite no hace
mod_rewrite no convierte mágicamente todas sus URL en "bonitas". Este es un malentendido común. Si tiene este enlace en su sitio web:
<a href="/my/ugly/link.php?is=not&very=pretty">
no hay nada que mod_rewrite pueda hacer para que sea bonito. Para hacer de esto un enlace bonito, debes:
Cambie el enlace a un enlace bonito:
<a href="/my/pretty/link">
Use mod_rewrite en el servidor para manejar la solicitud al URL
/my/pretty/link
utilizando cualquiera de los métodos descritos anteriormente.
(Se podría usar mod_substitute
conjuntamente para transformar páginas HTML salientes y sus enlaces contenidos. Aunque esto es usualmente más esfuerzo que simplemente actualizar sus recursos HTML).
Hay muchas cosas que mod_rewrite puede hacer y reglas de concordancia muy complejas que puedes crear, como encadenar varias reescrituras, derivar solicitudes a un servicio o máquina completamente diferente, devolver códigos de estado HTTP específicos como respuestas, redirigir solicitudes, etc. Es muy poderoso y se puede usar para Muy bien si entiende el mecanismo fundamental de solicitud-respuesta HTTP. No hace que tus enlaces sean bonitos de manera automática.
Consulte la documentación oficial de todos los posibles indicadores y opciones.