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


Answers

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:

  1. RewriteRule : inicia la regla de reescritura
  2. ^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
  3. 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
  4. [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 no example.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
  • ? 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
Question

"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)




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.

  1. 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.

  2. Enable MultiViews to hide the .php extension

    The 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 like http://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 ).

  3. SetType or SetHandler for extensionless .php scripts

    A 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 just article (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 or AddHandler php5-script .

    Again take note that such settings propagate from one .htaccess to subfolders. You always should disable script execution ( SetHandler None and Options -Exec or php_flag engine off etc.) for static resources, and upload/ directories etc.

  4. Other Apache rewriting schemes

    Among its many options, Apache provides mod_alias features - which sometimes work just as well as mod_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 any RewriteRule . 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 ErrorDocumentdirectiva 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.