Referencia: mod_rewrite, reescritura de URL y “enlaces bonitos” explicados

“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 syntax 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)

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 progtwigs 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:

  

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

  1. Cambie el enlace a un enlace bonito:

      
  2. Use mod_rewrite en el servidor para manejar la solicitud al URL https://stackoverflow.com/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.

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

Referencias

Stack Overflow 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 tags regex para un compendio de syntax.
  • Y el breve resumen de expresiones regulares de Apache .
  • Else regexp.info para obtener información básica 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 se RewriteRule 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 rest
    /prefix/123-capture/…/*/…whatever…

    Como se dijo antes, a menudo no desea patrones de reescritura generics. 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 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 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 use FallBackResource en FallBackResource lugar).

  • Eliminar www. del nombre de host

    Tenga 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:

    Referencias% 1 y $ 2,% 3 entre RewriteRule y RewriteCond
    Manual de Apache – introducción mod_rewrite , Copyright 2015 The Apache Software Foundation, AL-2.0

  • Redirigir 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ódulos httpd.conf .

    • Prefiere cada lista de reglas con RewriteEngine On . Mientras mod_rewrite está implícitamente activo en las secciones y , 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 . ¿Por qué a menudo lo ves ^/? opcionalizado para la paridad de reglas.
    · O al usar un RewriteCond %{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?

  • wrappers begone!

    Probablemente hayas visto esto en muchos ejemplos:

      Rewrite…  
    • Tiene sentido en las secciones – 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 HTTP 404 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.

  • No use RewriteBase menos que sea necesario

    Muchos 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 superponen

    La 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ía article.php a article.php con un /123 PATH_INFO implícitamente. Tendría que proteger sus reglas con el RewriteCond !-f RewriteCond + !-d , y / o desactivar el soporte PATH_INFO, o simplemente deshabilitar las Options -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 es RewriteCond -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 ). Que puede ser resuelto por:

    • Solo utilizando referencias absolutas del servidor href="/old.html" o src="/logo.png"
    • A menudo simplemente agregando en su sección HTML . 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

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

    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 comstackdos 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 y error.log

      A menudo puede averiguar cómo una RewriteRule se comporta mal solo con mirar su error.log y access.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 .htaccess

    • Antes de hacer tu propia pregunta

      Como ya sabrás, Stack Overflow es muy adecuado para hacer preguntas sobre mod_rewrite. Ponlos sobre el tema al incluir investigaciones previas e bashs (evitar respuestas redundantes), demostrar comprensión básica de expresiones regulares , 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 y error.log para verificar a qué se resolvieron las reglas existentes. Mejor aún, un resumen de rewrite.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 .

    1. Tener esquemas de recursos legibles y obvios.
    2. Asegurar que las URL sean de larga duración (AKA permalinks ).
    3. 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:

En su mayoría, simplemente genera [^/]+ marcadores de posición generics, pero es suficiente para sitios triviales.

Alternativas a mod_rewrite

Se pueden lograr muchos esquemas básicos de URL virtuales sin utilizar RewriteRules. Apache permite invocar scripts PHP sin extensión .php y con un argumento PATH_INFO virtual.

  1. Usa el 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:

      SetHandler application/x-httpd-php # or SetType  

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

    • Even a plain ErrorDocument directive could be used to let a PHP script handle virtual paths. Note that this is a kludgy workaround however, prohibits anything but GET requests, and floods the error.log by definition.

    See http://httpd.apache.org/docs/2.2/urlmapping.html for further tips.