===== Guía rápida de Nginx locations (Modificadores, prioridad y slash - proxy_pass) =====
==== Modificadores ====
**Prioridad**
El uso de modificadores y su prioridad en Nginx es algo confuso y esta pequeña guía intenta poder aclarar todos los casos posibles.
Las coincidencias exactas "=" tienen siempre prioridad, es decir, da igual donde se definan, que de existir y coincidir con alguna query, la localización será la usada. Después se tienen las localizaciones por prefijo "^~", el cual tiene menos prioridad que la coincidencia exacta pero más que las regex, por lo tanto, da lo mismo donde se definan en el fichero de configuración. De haber varias se usará la que mejor encaje con el prefijo.
Luego están las localizaciones que usan expresiones regulares "~" y "~*". La primera localización que coincida será elegida, aunque más adelante se encuentre otra más completa y que encaje mejor.
Si no se usa modificador, se está usando una localización por prefijo similar a ^~, pero no tiene su misma prioridad frente a las regex. Es decir, de haber una regex por delante que coincida, la localización sin prefijo no será evaluada. Estos prefijos sin modificador, de no tener regex por encima pero sí una o varias localizaciones con ^~, pueden ser elegidas si el prefijo coincide de forma más completa que en localizaciones con ^~. Es decir, el no usar modificador hace uso de prefijo y no compite en prioridad con ^~, simplemente se usa siempre el prefijo que coincida en mayor medida.
- Coincidencia exacta =.
- Coincidencia prefijo con prioridad ^~.
- Expresión regular (sensible a mayúsculas y no sensible a mayúsculas).
- Coincidencia prefijo “Ningún modificador”.
**=** El bloque se considerará una coincidencia si la URI de la solicitud coincide exactamente con la ubicación indicada. Es lo primero que busca Nginx, no pueden repetirse y tienen prioridad sobre cualquier otra location, usen o no expresiones regulares. El orden de estas location NO importa.
location = / { # Solo se usa si coincide al 100%, en este caso "/" únicamente. }
location = /img/ { # Solo se usa si coincide al 100%, en este caso "/img/" únicamente. Si la query es /img/algo_más NO se usará. }
# Query GET /cp/abc
location ~ ^cp/abc{}
location ^~ /cp {}
location ^~ /cp/abc {}
location = /cp/abc {} <--- location seleccionada.
**^~** No permite expresiones regulares. Actúa como un prefijo, de haber varias location, la que encaje con el prefijo más largo es la location elegida. Por lo tanto NO es importante el orden de este tipo de expresiones regulares.
# Query GET /cp/abc
location ^~ /cp/ab {}
location ~ ^/cp/abc {} # NO será seleccionada porque es una regex y NO tiene prioridad frente al uso de ^~.
location ^~ /cp/abc {} <--- location seleccionada.
NOTA: Es el método recomendado frente a usar "ningún modificador" cuando no se requieren expresiones regulares.
**(ninguno)**: Actúa como un prefijo exactamente igual que "^~" y tiene la misma prioridad frente a "^~" pero NO frente a regex. Por lo tanto, SÍ es importante el orden frente a otras location que usen modificador regex.
No puede haber dos location iguales basadas en prefijo, una sin modificador y otra con ^~.
# Query GET /cp/abc
location ~/cp/ab {} <----
location /cp/abc{} # No será usada porque no tiene prioridad frente a las regex que estén en una posición anterior.
NOTA: Es preferible usar el método de prefijo ^~ ya que internamente tiene un mejor rendimiento.
**~** (Modificador de expresión regular) La ubicación se interpretará como una coincidencia de expresión regular que distingue entre mayúsculas y minúsculas. SÍ importa el orden ya que el primero en coincidir será el usado.
location ~^/cp { # Distingue entre mayúsculas y minúsculas }
**~*** (Modificador de expresión regular) La ubicación se interpretará como una coincidencia de expresión regular que NO distingue entre mayúsculas y minúsculas. Al igual que el anterior modificador case sensitive, SÍ importa el orden ya que el primero en coincidir será el usado.
location ~* .(png|gif|ico|jpg|jpeg)$ { # Queries que terminen con png, gif, ico, jpg o jpeg. Al no ser que la query tenga un /img/ y exista un modificador de prefijo "location ^~ /img/" o "location /img/" }
location ~*^/cp { # cualquier cosa que empiece con /cp, /CP, /cP o /Cp }
location ~*/cp { # cualquier cosa que tenga /cp, /CP, /cP o /Cp en alguna parte de la URL, no necesariamente al principio. }
==== Modificadores en nested location ====
Recordemos la prioridad de los modificadores en Nginx.
- Coincidencia exacta =.
- Coincidencia prefijo con prioridad ^~.
- Expresión regular (sensible a mayúsculas y no sensible a mayúsculas): La primera en coincidir es la location usada.
- Coincidencia prefijo "Ningún modificador".
Cuando se anidan locations en nginx, se debe tener en cuenta que si el location raíz es una regex, sus location anidadas deben también usar modificadores de expresiones regulares. Si se anidan coincidencias exactas o de prefijo a una nested location con regex, estas nunca serán elegidas.
# Query GET /cp/abc
location ~ /cp {
...
location = /cp/abc {} # Nunca será seleccionada
location /cp/abc {} # Nunca será seleccionada
location ^~/cp/abc {} # Nunca será seleccionada
location ~/cp/abc {} <----- Sí (Debido a que es una regex).
}
En cambio con los modificadores de coincidencia exacta o mediante prefijo se puede combinar como se quiera. Dentro del nested location se usa la prioridad normal anteriormente vista.
location /cp {
...
location = /cp/abc {} # Query GET /cp/abc
location ~/cp/abcd {} # Query GET /cp/abcd
location /cp/abcd {} # Nunca será seleccionada porque la anterior (regex) es usada. De ser "location ^~ /cp/abcd {}" SÍ tendría prioridad frente a la regex.
}
==== Location sin expresiones regulares y proxy_pass ====
Ejemplos de comportamiento para location de Nginx que no usan regex, es decir modificador "ninguno" o "^~". El modificador "=" en esta sección no tiene sentido ya que no permite usarse de manera flexible.
location ^~/cp { proxy_pass http://192.168.178.25; }
http://XXX/cp/aaa ----> http://192.168.178.25/cp/aaaa
http://XXX/cp --------> http://192.168.178.25/cp
http://XXX/cp/ -------> http://192.168.178.25/cp/
location ^~/cp { proxy_pass http://192.168.178.25/; }
http://XXX/cp/aaa ---> http://192.168.178.25//aaa
http://XXX/cp/ ------> http://192.168.178.25//
http://XXX/cp -------> http://192.168.178.25/
location ^~/cp/ { proxy_pass http://192.168.178.25/; }
http://XXX/cp/aaa ---> http://192.168.178.25/aaa
http://XXX/cp/ ------> http://192.168.178.25/
http://XXX/cp -------> http://XXX/cp/ ----> http://192.168.178.25/
location ^~/cp/ { proxy_pass http://192.168.178.25; }
http://XXX/cp/aaa ---> http://192.168.178.25/cp/aaa
http://XXX/cp/ ------> http://192.168.178.25/cp
http://XXX/cp -------> http://XXX/cp/ --> http://192.168.178.25/cp
==== Location con expresiones regulares y proxy_pass sin URL ====
location ~ /c(p|a)/ { proxy_pass http://192.168.178.25; }
http://XXX/cp/aaa ---> http://192.168.178.25/cp/aaa
http://XXX/cp/ ------> http://192.168.178.25/cp/
http://XXX/cp -------> NOT FOUND
# Al no especificarse una URL (incluir al menos un slash) en proxypass, la variable $1 no tendrá ningún valor.
location ~ ^/c(p|a)/ { proxy_pass http://192.168.178.25; }
location ~ ^/cp/ { proxy_pass http://192.168.178.25$1; }
location ~ ^/cp/ { proxy_pass http://192.168.178.25; }
http://XXX/cp/aaa ---> http://192.168.178.25/cp/aaa
http://XXX/cp/ ------> http://192.168.178.25/cp/
http://XXX/cp -------> http://192.168.178.25/cp
==== Location con expresiones regulares y proxy_pass con URL ====
Si se usan URLs en el proxypass, deben usarse variables, $1, $2, $3,... Las cuales tendrán el valor resultante de cada regex para ser integradas en la URL del proxy_pass. Las variables pueden tener nombres personalizados. Si no se usan variables cuando se utilizan URLs (uso de slash) en proxy_pass, Nginx mostrará el siguiente error.
nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in
La obligación de usar variables, implica que para agrupar sus valores deben usarse paréntesis, veamos algunos ejemplos de uso de location con expresiones regulares y URLs en proxy_pass.
# No hay regex activa, por lo que $1 no contiene ningún valor.
location ~ ^/cp/ { proxy_pass http://192.168.178.25/$1; }
http://XXX/cp/bbbb ----> http://192.168.178.25/
http://XXX/ca/bbb ----> http://192.168.178.25/
location ~ ^/cp/ { proxy_pass http://192.168.178.25/$1/hola; }
http://XXX/cp/bbbb ----> http://192.168.178.25/hola
http://XXX/ca/bbb ----> http://192.168.178.25/hola
# La Expresión regular refiere a "p" o "a".
location ~ ^/c(p|a) { proxy_pass http://192.168.178.25/$1/$1; }
location ~ ^/c(p|a)/ { proxy_pass http://192.168.178.25/$1/$1; }
http://XXX/cp/bbbb ----> http://192.168.178.25/p/p
http://XXX/ca/bbb ----> http://192.168.178.25/a/a
# La Expresión regular refiere a "/cp/" o "/ca/".
location ~ ^(/cp/|/ca/) { proxy_pass http://192.168.178.25/dir1/dir2$1; }
http://XXX/cp/AAA/BBB/CCC ---> http://192.168.178.25/dir1/dir2/cp/
http://XXX/ca/AAA/BBB/CCC ---> http://192.168.178.25/dir1/dir2/ca/
# La expresión regular refiere a cualquier location.
location ~ ^/(.*) { proxy_pass http://192.168.178.25/dir1/$1/dir2; }
http://XXX/cp/AA/BBB ---> http://192.168.178.25/dir1/cp/AA/BBB/dir2
http://XXX ------------> http://192.168.178.25/dir1//dir2
http://XXX/ ------------> http://192.168.178.25/dir1//dir2
Varias regex y personalización de nombre de variables
# El valor obtenido con la primera regex se guarda en la variable "$width" y el segundo en "$height" para luego ser enviadas al host en la forma expuesta.
location ~/resize/(?(\d+))/(?(\d+)) { proxy_pass http://192.168.178.25/?ancho=$width&alto=$height&blabla=null;}
http://XXX/resize/300/500/ ----> http://192.168.178.25/?ancho=300&alto=500&blabla=null