Referencia: mod_rewrite, reescritura de URL y "enlaces bonitos" explicados

142

"Enlaces bonitos" es un tema solicitado con frecuencia, pero rara vez se explica completamente. mod_rewrite es una forma de hacer "enlaces bonitos", pero es complejo y su sintaxis es muy concisa, difícil de asimilar, y la documentación asume un cierto nivel de competencia en HTTP. ¿Alguien puede 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 URL limpias: URL RESTful , URL fáciles de usar, URL amigables con SEO , slugging y URL MVC (probablemente un nombre inapropiado)

difunto
fuente
2
Slug o Slugging es otro alias / término común para urls bonitas.
Mike B
2
@Mike tipo de, pero las babosas a menudo son parte de URL bonitas. Una babosa es bastante específica cuando, por ejemplo, el título de un artículo se convierte en una forma amigable de URL que luego actúa como el identificador de ese artículo. Así reference-mod-rewrite-url-rewriting-explainedes la babosa, /questions/20563772/reference-mod-rewrite-url-rewriting-explainedes la bonita URL.
deceze
2
Creo que las etiquetas .htaccessy mod-rewritedeberían actualizarse para incluir un enlace a esta pregunta, ya que cubre gran parte de lo que se pide de forma regular. Pensamientos?
Mike Rockétt

Respuestas:

110

Para comprender qué mod_rewrite necesita primero comprender 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 le solicita la URL /foo/bar.html . 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 igual de 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 solicitudes HTTP que ingresan en ese puerto y devuelve una respuesta. Un servidor web es completamente libre de responder a cualquier solicitud de la forma que considere adecuada / de cualquier manera que haya configurado para responder. Esta respuesta no es un archivo, es una respuesta HTTP que puede o no tener algo 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 de manera persistente 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 divorciarte de cualquier noción de que las URL son 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 en, digamos, /var/wwwpuede ver si el archivo /var/www/foo/bar.htmlexiste y servirlo si es así. Si el archivo termina en ".php", invocará al intérprete 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 PHP, y la URL no tiene que coincidir con ningún archivo en 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 siempre que una solicitud coincida con "/ foo / bar", vuelva a escribirla en "/ foo / baz". La solicitud se manejará como si se /foo/bazhubiera 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/barfue la URL solicitada, se manejará como si se /foo/bar.htmlhubiera solicitado. Consulte http://regular-expressions.info para obtener más información sobre la coincidencia, captura y reemplazo de expresiones regulares.

Otra regla que se encuentra a menudo es esta:

RewriteRule (.*) index.php?url=$1

Esto, nuevamente, coincide con cualquier cosa y lo reescribe en el archivo index.php con la URL solicitada originalmente adjunta en el urlparámetro de consulta. Es decir, para todas y cada una de las solicitudes entrantes, el archivo index.php se ejecuta y este archivo tendrá acceso a la solicitud original $_GET['url'], por lo que puede hacer lo que quiera con él.

Principalmente pones estas reglas de reescritura en el archivo de configuración de tu servidor web . Apache también le permite * ponerlos en un archivo llamado .htaccessdentro 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 habilitado.

Lo mod_rewrite qué no hacer

mod_rewrite no hace mágicamente todas sus URL "bonitas". Este es un malentendido común. Si tiene este enlace en su sitio web:

<a href="https://stackoverflow.com/my/ugly/link.php?is=not&amp;very=pretty">

no hay nada que mod_rewrite pueda hacer para que sea bonito. Para que este sea un enlace bonito, debes:

  1. Cambie el enlace a un enlace bonito:

    <a href="https://stackoverflow.com/my/pretty/link">
    
  2. Use mod_rewrite en el servidor para manejar la solicitud a la URL /my/pretty/linkusando cualquiera de los métodos descritos anteriormente.

(Se podría usar mod_substituteen conjunto para transformar las páginas HTML salientes y sus enlaces contenidos. Aunque esto suele ser más esfuerzo que solo actualizar sus recursos HTML).

Hay muchas cosas que mod_rewrite puede hacer y reglas de coincidencia muy complejas que puede crear, incluyendo encadenar varias reescrituras, enviar solicitudes a un servicio o máquina completamente diferente, devolver códigos de estado HTTP específicos como respuestas, redireccionar solicitudes, etc. Es muy poderoso y puede usarse para muy bueno si entiendes el mecanismo fundamental de solicitud-respuesta HTTP. No , no significa automáticamente que sus enlaces bonita.

Consulte la documentación oficial para ver todas las banderas y opciones posibles.

difunto
fuente
66
Quizás mencione la directiva FallbackResource presentada en la versión 2.2.16 como la forma preferida de reescribir a un despachador.
Darsstar
78

Para ampliar la respuesta de deceze , quería proporcionar algunos ejemplos y explicaciones de alguna otra funcionalidad mod_rewrite.

Todos los ejemplos a continuación asumen que ya lo ha incluido RewriteEngine Onen su .htaccessarchivo.

Ejemplo de reescritura

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:

  1. RewriteRule - comienza la regla de reescritura
  2. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - Esto se llama patrón, sin embargo, me referiré a él como el lado izquierdo de la regla, de lo que desea reescribir
  3. blog/index.php?id=$1&title=$2 - llamado la sustitución, o el lado derecho de una regla de reescritura - a lo que desea reescribir
  4. [NC,L,QSA] son banderas para la regla de reescritura, separadas por una coma, que explicaré más adelante

La reescritura anterior le permitiría vincular a algo así /blog/1/foo/y en realidad se 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 se 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 solo con valores numéricos (es decir, 0-9). Se puede hacer referencia a esto $1en 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 +(la nota +se escapa con una barra invertida, ya que sin escapar se ejecutará como una expresión regular) personaje de repetición ). Se puede hacer referencia a esto $2en el lado derecho de la regla
  • ?medios que el carácter anterior es opcional, por lo tanto, en este caso /blog/1/foo/y /blog/1/foose re-escribir al mismo lugar
  • $ indica que este es el final de la cadena que queremos hacer coincidir

Banderas

Estas son opciones que se agregan entre corchetes al final de la regla de reescritura para especificar ciertas condiciones. Una vez más, hay muchos indicadores diferentes que puede leer en la documentación , pero revisaremos algunos de los indicadores más comunes:

NC

El indicador de no caso 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

El último indicador indica que esta es la última regla que debe procesarse. Esto significa que si y solo si esta regla coincide, no se evaluarán más reglas en la ejecución del proceso de reescritura actual. Si la regla no coincide, todas las demás reglas se probarán en el orden habitual. Si no establece el Lindicador, todas las siguientes reglas se aplicarán a la URL reescrita después.

END

Desde Apache 2.4 también puede usar la [END]bandera. Una regla que coincida con ella terminará completamente el procesamiento de alias / reescritura adicional. (Mientras que la [L]bandera a menudo puede desencadenar una segunda ronda, por ejemplo, al reescribir dentro o fuera de subdirectorios).

QSA

El indicador de cadena de consulta nos permite pasar variables adicionales a la URL especificada que se agregará a los parámetros de obtención originales. Para nuestro ejemplo, esto significa que algo así /blog/1/foo/?comments=15se cargaría/blog/index.php?id=1&title=foo&comments=15

R

Esta bandera no es una que usé en el ejemplo anterior, pero creo que vale la pena mencionarla. Esto le permite especificar una redirección http, con la opción de incluir un código de estado (por ejemplo R=301). Por ejemplo, si quisieras hacer una redirección 301 en / myblog / to / blog / simplemente escribirías una regla como esta:

RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]

Reescribir 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 mencionaré 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 antepondrá su dominio www.(si aún no está allí) y ejecutará una redirección 301. Por ejemplo, cargarlo http://example.com/blog/te redirigirá ahttp://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 de archivo es un directorio o archivo que existe en el servidor.

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] solo ejecutará la reescritura de archivos con una extensión de archivo de jpg, jpeg, gif o png (no distingue entre mayúsculas y minúsculas).
  • %{REQUEST_FILENAME} !-f comprobará si el archivo existe en el servidor actual y solo ejecutará la reescritura si no existe
  • %{REQUEST_FILENAME} !-d comprobará si el archivo existe en el servidor actual y solo ejecutará la reescritura si no existe
  • La reescritura intentará cargar el mismo archivo en otro dominio
Mella
fuente
39

Referencias

Stack Overflow tiene muchos otros excelentes recursos para comenzar:

Y descripciones de expresiones regulares amigables para los recién llegados, incluso:

Marcadores de posición usados ​​con frecuencia

  • .*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 más a menudo para segmentos de ruta. Coincide con cualquier cosa menos la barra diagonal.
  • \d+ solo coincide con cadenas numéricas.
  • \w+coincide con caracteres alfanuméricos. Básicamente es una forma abreviada de [A-Za-z0-9_].
  • [\w\-]+para segmentos de ruta de estilo "slug", usando letras, números, guiones - y _
  • [\w\-.,]+agrega puntos y comas. Prefiere un \-guión escapado en […]charclasses.
  • \.denota un período literal. De lo contrario, .fuera de […]es un marcador de posición para cualquier símbolo.

Cada uno de estos marcadores de posición suele estar entre (…)paréntesis como grupo de captura. Y todo el patrón a menudo en los ^………$marcadores de inicio + final. 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 vinculados 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 lo más simple:

     RewriteRule ^contact$  templ/contact.html
     RewriteRule ^about$    about.php
    
  • Identificadores numéricos
    /object/123

    Introducir accesos directos como http://example.com/article/531los scripts PHP existentes también es fácil. El marcador de posición numérico se puede reasignar a un $_GETparámetro:

     RewriteRule ^article/(\d+)$    article-show.php?id=$1
     #                      └───────────────────────────┘
    
  • Marcadores de posición de estilo babosa
    /article/with-some-title-slug

    Puede ampliar fácilmente esa regla para permitir /article/title-stringmarcadores de posición:

     RewriteRule ^article/([\w-]+)$    article-show.php?title=$1
     #                       └────────────────────────────────┘
    

    Tenga en cuenta que su script debe ser capaz (o estar adaptado) de asignar esos títulos de nuevo a los identificadores de la base de datos. RewriteRules 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á /article/529-title-slugcaminos mixtos utilizados en la práctica:

     RewriteRule ^article/(\d+)-([\w-]+)$    article.php?id=$1&title=$2
     #                      └───────────────────────────────┘
    

    Ahora puede omitir el paso de title=$2todos modos, ya que su script generalmente dependerá de la base de datos de identificación de todos modos. La -title-slugdecoración de URL se ha convertido en arbitraria.

  • Uniformidad con listas alternativas
    /foo/… /bar/… /baz/…

    Si tiene reglas similares para múltiples rutas de página virtuales, puede hacer coincidirlas y compactarlas con |listas alternativas. Y nuevamente, simplemente reasignarlos a los parámetros internos de GET:

     #                               ┌─────────────────────────┐
     RewriteRule ^(blog|post|user)/(\w+)$  disp.php?type=$1&id=$2
     #               └───────────────────────────────────┘
    

    Puede dividirlos en RewriteRules individuales si esto se vuelve demasiado complejo.

  • Envío de URL relacionadas a diferentes backends
    /date/SWITCH/backend

    Un uso más práctico de listas alternativas consiste en asignar rutas de solicitud a scripts distintos. 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 un script, y todos los demás años implícitamente en otro controlador. Tenga en cuenta la regla más específica que viene primero . Cada script puede usar diferentes parámetros GET.

  • Otros delimitadores además de los /cortes de ruta
    /user-123-name

    Lo más común es ver 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 /wiki:section:Page_Nameesquema también común :

     RewriteRule ^wiki:(\w+):(\w+)$  wiki.php?sect=$1&page=$2 
     #                   └─────┼────────────────────┘       │
     #                         └────────────────────────────┘
    

    Ocasionalmente, es adecuado alternar entre /-delimitadores y / :o .incluso en la misma regla. O tenga dos RewriteRules nuevamente para mapear variantes en diferentes scripts.

  • Barra /inclinada opcional
    /dir=/dir/

    Al optar por rutas de estilo de directorio, puede hacerlo accesible con y sin un final /

     RewriteRule ^blog/([\w-]+)/?$  blog/show.php?id=$1
     #                         ┗┛
    

    Ahora esto maneja ambos http://example.com/blog/123y /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 que encontrará asignan un conjunto restringido de /…/segmentos de ruta de recursos a parámetros GET individuales. Sin embargo, algunos scripts manejan un número variable de opciones . El motor regexp de Apache 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 para cada uno. Aquí el orden no es tan importante, como tampoco se superpone. Por lo tanto, tener los caminos más utilizados 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 script simplemente los prefiere previamente divididos. (Aunque es más común usar una regla general y dejar que el script expanda los segmentos fuera de REQUEST_URI).

    Consulte también: ¿Cómo transformo mis segmentos de ruta 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ía vacío si no hay una /…/ruta intermedia .

  • Captura el resto
    /prefix/123-capture/…/*/…whatever…

    Como se dijo antes, a menudo no quieres patrones de reescritura demasiado genéricos. Sin embargo, tiene sentido combinar comparaciones estáticas y específicas con .*algunas veces.

     RewriteRule ^(specific)/prefix/(\d+)(/.*)?$  speci.php?id=$2&otherparams=$2
    

    Esto opcionalizó cualquier /…/…/…segmento de ruta final. Lo cual, por supuesto, requiere la secuencia de comandos de manejo para dividirlos, y los parámetros extraídos de forma variable (que es lo que hacen los marcos web "MVC" ).

  • Archivo final "extensiones"
    /old/path.HTML

    Las URL realmente no tienen extensiones de archivo. De eso 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 la reasignación de .htmlrutas obsoletas a .phpcontroladores más nuevos , o simplemente alias de nombres de directorio solo para archivos individuales (reales / reales).

  • Ping-Pong (redirige y reescribe al unísono)
    /ugly.html← →/pretty

    Entonces, en algún momento, está reescribiendo sus páginas HTML para que solo contengan enlaces bonitos, como se describe en deceze . Mientras tanto, seguirá recibiendo solicitudes de los viejos caminos, a veces incluso de marcadores. Como solución alternativa , puede utilizar los navegadores de ping-pong para mostrar / establecer las nuevas URL.

    Este truco común implica enviar una redirección 30x / Location cada vez que una URL entrante sigue el esquema de nombres obsoleto / feo. Los navegadores volverán a solicitar la nueva / bonita URL, que luego se reescribe (solo internamente) a 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]
    

    Observe cómo este ejemplo solo se usa en [END]lugar de [L]alternar de manera segura. Para versiones anteriores de Apache 2.2, puede usar otras soluciones alternativas, además de reasignar los parámetros de la cadena de consulta, por ejemplo: Redirigir URL fea a bonita, reasignar nuevamente a la ruta fea, sin bucles infinitos

  • Espacios en patrones
    /this+that+

    No es tan bonito en las barras de direcciones del navegador, pero puede usar espacios en las URL. Para reescribir patrones, use \␣espacios con barra invertida . De lo contrario, "cite todo el patrón o sustitución:

     RewriteRule  "^this [\w ]+/(.*)$"  "index.php?id=$1"  [L]
    

    Los clientes serializan URL con +o %20para espacios. Sin embargo, en RewriteRules se interpretan con caracteres literales para todos los segmentos de ruta relativos.

Duplicados frecuentes:

prevalentes .htaccesstrampas

Ahora tome esto con un grano de sal. No todos los consejos se pueden generalizar a todos los contextos. Este es solo un resumen simple de obstáculos conocidos y algunos no obvios:

  • Habilitar mod_rewritey.htaccess

    Para usar RewriteRules en archivos de configuración por directorio, debe:

    • Comprueba que tu servidor esté AllowOverride Allhabilitado . De lo contrario, sus .htaccessdirectivas por directorio se ignorarán y RewriteRules no funcionará.

    • Obviamente lo has mod_rewritehabilitado en la httpd.confsección de tus módulos.

    • Anteponga cada lista de reglas con RewriteEngine Onstill. Mientras que mod_rewrite está implícitamente activo en <VirtualHost>y <Directory>secciones, los .htaccessarchivos por directorio lo necesitan convocar individualmente.

  • La barra diagonal ^/no coincidirá

    No debería comenzar sus .htaccesspatrones RewriteRule con ^/normalidad:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    Esto se ve a menudo en antiguos tutoriales. Y solía ser correcto para las antiguas versiones de Apache 1.x. Hoy en día, las rutas de solicitud están convenientemente relacionadas con el directorio en .htaccessRewriteRules. Solo deja la guía /.

    · Tenga en cuenta que la barra inclinada aún es correcta en las <VirtualHost>secciones. Es por eso que a menudo lo ves ^/?opcional para la paridad de reglas.
    · O cuando utilizas un RewriteCond %{REQUEST_URI}todavía te corresponde para un líder /.
    · Consulte también Webmaster.SE: ¿Cuándo se necesita la barra diagonal (/) en los patrones mod_rewrite?

  • <IfModule *> envoltorios se fueron!

    Probablemente has visto esto en muchos ejemplos:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • Se hace tiene sentido en <VirtualHost>secciones - si se combina con otra opción de reserva, como ScriptAliasMatch. (Pero nadie hace eso nunca).
    • Y se distribuye comúnmente para los .htaccessconjuntos de reglas predeterminados con muchos proyectos de código abierto. Ahí solo se entiende como reserva, y mantiene las URL "feas" funcionan por defecto.

    Sin embargo, generalmente no lo desea en sus propios .htaccessarchivos.

    • En primer lugar, mod_rewrite no se desactiva al azar. (Si lo hiciera, tendría mayores problemas).
    • Si realmente estuviera deshabilitado, sus RewriteRules aún no funcionarían de todos modos.
    • Está destinado a prevenir 500errores HTTP . Lo que generalmente logra es agradecer a sus usuarios con 404errores HTTP . (No es 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. Usted sería sin enterarse de por qué sus RewriteRules nunca funcionan.

    Lo que parece atractivo como salvaguardia generalizada, a menudo resulta ser un obstáculo en la práctica.

  • No lo use a RewriteBasemenos que sea necesario

    Muchos ejemplos de copiar + pegar contienen una RewriteBase /directiva. Que resulta ser el valor implícito predeterminado 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 erróneas para algunos servidores compartidos.

    Tiene sentido usarlo con aplicaciones web individuales en subdirectorios más profundos. Puede acortar los patrones de RewriteRule en tales casos. En general, es mejor preferir especificadores de ruta relativa en conjuntos de reglas por directorio.

    Consulte también Cómo funciona RewriteBase en .htaccess

  • Deshabilitar MultiViewscuando las rutas virtuales se superponen

    La reescritura de URL se utiliza principalmente para admitir rutas de entrada virtuales . Comúnmente sólo tiene un guión despachador ( index.php) o un par de manipuladores individuales ( articles.php, blog.php, wiki.php, ...). Este último podría chocar con rutas virtuales similares de RewriteRule.

    Una solicitud, por /article/123ejemplo, podría correlacionarse article.phpcon un /123PATH_INFO implícitamente. Tendría que proteger sus reglas con Commonplace RewriteCond !-f+ !-dy / o deshabilitar el soporte PATH_INFO, o tal vez simplemente deshabilitar Options -MultiViews.

    Lo que no quiere decir que siempre tengas que hacerlo . La negociación de contenido es solo un automatismo de los recursos virtuales.

  • Ordenar es importante

    Vea todo lo que siempre quiso saber sobre mod_rewrite si aún no lo ha 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 adoptará una vez versado. Usted puede re-re-re escritura caminos virtuales de una regla a otra, hasta que llega a un controlador objetivo real.

    Aún así, a menudo querrá tener las reglas más específicas ( /forum/…patrones de cadena fijos o marcadores de posición más restrictivos [^/.]+) en las primeras reglas. .*Es mejor dejar las reglas genéricas de slurp-all ( ) posteriores . (Una excepción es un RewriteCond -f/-dguardia como bloque primario).

  • Las hojas de estilo y las imágenes dejan de funcionar

    Cuando introduce estructuras de directorios virtuales /blog/article/123 esto afecta las referencias relativas de recursos en HTML (como <img src=mouse.png>). Que se puede resolver mediante:

    • Solo usando referencias absolutas de servidor href="https://stackoverflow.com/old.html" osrc="/logo.png"
    • A menudo simplemente agregando <base href="https://stackoverflow.com/index">a su HTML<head> sección . Esto implícitamente vuelve a unir referencias relativas a lo que eran antes.

    Alternativamente, podría crear más RewriteRules para volver a unir .css o .pngrutas a sus ubicaciones originales. Pero eso es innecesario o incurre en redirecciones adicionales y dificulta el almacenamiento en caché.

    Ver también: CSS, JS y las imágenes no se muestran con URL bonita

  • RewriteConds simplemente enmascara una RewriteRule

    Una mala interpretación común es que un RewriteCond bloquea múltiples RewriteRules (porque están visualmente organizadas juntas):

     RewriteCond %{SERVER_NAME} localhost
     RewriteRule ^secret  admin/tools.php
     RewriteRule ^hidden  sqladmin.cgi
    

    Que no lo hace por defecto. Puedes encadenarlos usando el[S=2] bandera. De lo contrario, tendrás que repetirlos. Si bien a veces puedes crear una regla primaria "invertida" para [FINALIZAR] el proceso de reescritura temprano.

  • QUERY_STRING exento de RewriteRules

    No puede coincidir RewriteRule index.php\?x=y, porque mod_rewrite se compara solo con las rutas relativas por defecto. Sin embargo, puede combinarlos por separado a través de:

     RewriteCond %{QUERY_STRING} \b(?:param)=([^&]+)(?:&|$)
     RewriteRule ^add/(.+)$  add/%1/$1  # ←──﹪₁──┘
    

    Consulte también ¿Cómo puedo hacer coincidir las variables de cadena de consulta con mod_rewrite?

  • .htaccess vs. <VirtualHost>

    Si está utilizando RewriteRules en un archivo de configuración por directorio, entonces no tiene sentido preocuparse por el rendimiento de expresiones regulares. Apache retiene los patrones PCRE compilados por más tiempo que un proceso PHP con un marco de enrutamiento común. Sin embargo, para los sitios de alto tráfico, debe 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 separador de directorio opcional . Esto permite mover RewriteRules libremente entre PerDir y los archivos de configuración del servidor.

  • Cuando algo no funciona

    No temas.

    • Compara access.logyerror.log

      A menudo, puedes descubrir cómo se comporta mal una RewriteRule solo con mirar tu error.logyaccess.log . Correlacione los tiempos de acceso para ver qué ruta de solicitud entró originalmente y qué ruta / archivo no pudo resolver Apache (error 404/500).

      Esto no te dice qué RewriteRule es el culpable. Pero los caminos finales inaccesibles como /docroot/21-.itle?index.phppueden revelar dónde inspeccionar más. De lo contrario, deshabilite las reglas hasta que obtenga algunas rutas predecibles.

    • Habilite RewriteLog

      Vea los documentos de Apache RewriteLog . Para la depuración, puede habilitarlo en las secciones de 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 se modifican 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 que ayuda a reducir las reglas demasiado genéricas y los contratiempos de expresiones regulares.

      Consulte también:
      · .htaccess no funciona (mod_rewrite)
      · Consejos para depurar reglas de reescritura de .htaccess

    • Antes de hacer tu propia pregunta

      Como ya sabrás, Stack Overflow es muy adecuado para hacer preguntas sobre mod_rewrite. Hazlos sobre el tema al incluir investigaciones e intentos previos (evite respuestas redundantes), demuestre lo básico comprensión y:

      • Incluya ejemplos completos de URL de entrada, rutas de destino reescritas falsamente, su estructura de directorio real.
      • El conjunto completo de RewriteRule, pero también destaca el supuesto defecto defectuoso.
      • Versiones de Apache y PHP, tipo de sistema operativo, sistema de archivos, DOCUMENT_ROOT y PHP $_SERVER entorno si se trata de una falta de coincidencia de parámetros.
      • Un extracto de su access.logy error.logpara verificar lo que resolvieron las reglas existentes. Mejor aún, un rewrite.logresumen.

      Esto proporciona respuestas más rápidas y exactas, y las hace más útiles para otros.

  • Comenta tu .htaccess

    Si copia ejemplos de algún lugar, tenga cuidado de incluir a # comment and origin link. Si bien es simplemente de mala educación omitir la atribución, a menudo realmente perjudica el mantenimiento más adelante. Documente cualquier código o fuente de tutorial. En particular, mientras no esté versado, debería estar más interesado en no tratarlos como cajas negras mágicas.

  • No es "SEO" -URLs

    Descargo de responsabilidad: solo un motivo favorito. A menudo escuchas bonitos esquemas de reescritura de URL conocidos como enlaces "SEO" o algo así. Si bien esto es útil para buscar ejemplos en Google, es un nombre inapropiado.

    Ninguno de los modernos motores de búsqueda son muy perturbado por .htmly .phpen los segmentos de trazado, o ?id=123cadenas de consulta para el caso. Los motores de búsqueda de la antigüedad, tales como AltaVista, hicieron evitar el rastreo de sitios web con las rutas de acceso potencialmente ambiguas. Los rastreadores modernos a menudo incluso anhelan recursos web profundos.

    Lo que las URL "bonitas" deberían usarse conceptualmente es hacer que los sitios web sean fáciles de usar .

    1. Tener esquemas de recursos legibles y obvios.
    2. Asegurar que las URL sean de larga duración (enlaces permanentes AKA ).
    3. Proporcionando capacidad de descubrimiento a través de /common/tree/nesting.

    Sin embargo, no sacrifique requisitos únicos para el conformismo.

Herramientas

Existen varias herramientas en línea para generar RewriteRules para la mayoría de las URL de parámetros GET:

En su mayoría solo genera [^/]+marcadores de posición genéricos, pero probablemente sea suficiente para sitios triviales.

mario
fuente
Todavía necesita un poco de reescritura, más enlaces, y los muchos subtítulos son algo desagradables. Hay cierta superposición con las otras respuestas aquí, por lo que tal vez se pueda reducir. Sin embargo, se trata principalmente de ejemplos visuales y de esa lista de problemas comunes.
mario
3
¡No vi una respuesta tan hermosa en mucho tiempo! Mis ojos brillan mientras lo leo. Por favor, no dejes de publicar esas respuestas :)
Rizier123
1
Excelente post. ¡Me hizo comprender los conceptos básicos de mod_rewrite muy rápidamente!
breez
6

Alternativas a mod_rewrite

Se pueden lograr muchos esquemas básicos de URL virtual sin usar RewriteRules. Apache permite invocar scripts PHP sin .phpextensión y con un PATH_INFOargumento virtual .

  1. Usa el PATH_INFO , Luke

    Hoy en día a AcceptPathInfo Onmenudo está habilitado de forma predeterminada. Lo que básicamente permite .phpy otras URL de recursos llevar un argumento virtual:

    http://example.com/script.php/virtual/path
    

    Ahora esto /virtual/path aparece en PHP como$_SERVER["PATH_INFO"] donde puedes manejar cualquier argumento extra como quieras.

    Esto no es tan conveniente como tener Apache segmentos de trayectoria de entrada separado en $1, $2,$3 y que pasa a ellos como distintas $_GETvariables en PHP. Simplemente está emulando "URL bonitas" con menos esfuerzo de configuración.

  2. Habilitar MultiViews para ocultar la .phpextensión.

    La opción más simple para evitar también .php "extensiones de archivo" en las URL es habilitar:

    Options +MultiViews
    

    Esto tiene Apache select article.php para solicitudes HTTP activadas /articledebido al nombre base coincidente. Y esto funciona bien junto con la función PATH_INFO antes mencionada. Así que solo puedes usar URL como http://example.com/article/virtual/title. Lo que tiene sentido si tiene una aplicación web tradicional con múltiples puntos / scripts de invocación PHP.

    Sin embargo, tenga en cuenta que MultiViews tiene un propósito diferente / más amplio. Incurre en una penalización de rendimiento muy pequeña , porque Apache siempre busca otros archivos con nombres de base coincidentes. De hecho, es destinado a negociación de contenido , por lo que los navegadores reciban la mejor alternativa entre los recursos disponibles (como article.en.php, article.fr.php,article.jp.mp4 ).

  3. SetType o SetHandler para sin extensión .php scripts sin

    Un enfoque más dirigido para evitar llevar .phpsufijos en las URL es configurar el controlador PHP para otros esquemas de archivos. La opción más simple es anular el tipo MIME / controlador predeterminado mediante .htaccess:

    DefaultType application/x-httpd-php
    

    De esta manera, podría cambiar el nombre de su article.phpscript a solo article(sin extensión), pero aún así procesarlo como script PHP.

    Ahora esto puede tener algunas implicaciones de seguridad y rendimiento, porque todos los archivos sin extensión se canalizarían a través de PHP ahora. Por lo tanto, también puede configurar este comportamiento solo para archivos individuales:

    <Files article>
      SetHandler application/x-httpd-php
      # or SetType 
    </Files>
    

    Esto depende en cierta medida de la configuración de su servidor y del PHP SAPI utilizado. Las alternativas comunes incluyen ForceType application/x-httpd-phpo AddHandler php5-script.

    Nuevamente, tenga en cuenta que tales configuraciones se propagan de una .htaccessa subcarpetas. Siempre debe deshabilitar la ejecución del script ( SetHandler Noney / Options -Execo php_flag engine offetc.) para recursos estáticos, y cargar / directorios, etc.

  4. Otros esquemas de reescritura de Apache

    Entre sus muchas opciones, Apache proporciona mod_aliascaracterísticas, que a veces funcionan tan bien como mod_rewritelas RewriteRules. Tenga en cuenta que la mayoría de ellos deben configurarse en una <VirtualHost>sección, no en .htaccessarchivos de configuración por directorio .

    • ScriptAliasMatches principalmente para scripts CGI, pero también debería funcionar para PHP. Permite expresiones regulares como cualquier otra RewriteRule. De hecho, es quizás la opción más robusta para configurar un controlador frontal universal.

    • Y una simple Aliasayuda con algunos esquemas simples de reescritura también.

    • Incluso una ErrorDocumentdirectiva simple podría usarse para permitir que un script PHP maneje rutas virtuales. Tenga en cuenta que esta es una solución poco clara, sin embargo, prohíbe todo menos 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.

mario
fuente