Regla de reescritura de Nginx para reemplazar el signo de interrogación de cadena de consulta con guión bajo


16

Para reflejar un sitio web completo como HTML estático,

Me gustaría convertir URLs como http://example.com/script.php?t=12a http://example.com/script.php_t=12.

El aviso ?en URL se está convirtiendo a _.

Esto permitirá que nginx o apache sirvan estos archivos del disco como HTML sin formato que obtuvimos y guardamos de wgetun archivo para cada URL, en lugar de un archivo PHP.

¿Es posible hacerlo a través de la reescritura de URL de Nginx?


Ciertamente es posible, pero realmente extraño. ¿Para qué lo desea?
Alexey Ten

2
Un problema con este enfoque es que varios parámetros GET en una URL pueden estar en cualquier orden, y cuando realice esta conversión, cambiará la semántica de la URL.
Tero Kilkanen

es para archivar un foro antiguo en html estático, pero sí tener este trabajo http://example.com/script.php?a=1&t=3necesitará una acción de reescritura súper elegante
Sam Saffron

1
@tero las URL de cadena de consulta son, en la práctica, siempre en el mismo orden. Entonces esto no es un problema.
Jeff Atwood

1
@chx es para archivar un foro de edad, por lo que el argumento puede ser que no sea tcomo f, u, etc.
Arpit Jalan

Respuestas:


15

Lo hice funcionar usando try_files:

location / {
    try_files "${uri}_${args}" 404.html;
}

Esto intentará encontrar un archivo en el disco con el nombre del patrón que proporcionó con una "_" en lugar de "?".

La configuración adicional depende de cómo guardó archivos estáticos como imágenes u hojas de estilo. Puede agregar un respaldo tratando de leerlos sin consultar el disco del formulario de cadena de la siguiente manera:

location / {
    try_files "${uri}_${args}" $uri 404.html;
}

1
Enfoque muy interesante, ¿causaría esto una posible lectura de disco y "archivo no encontrado" en todas las URL potenciales?
Jeff Atwood

De try_files : "Comprueba la existencia de archivos en el orden especificado y utiliza el primer archivo encontrado para el procesamiento de solicitudes". Por lo tanto, esto no provocará ninguna lectura de disco adicional para las URL en su pregunta.
Matthias Bayer

1
ok, si lo ponemos location ~ \.php$podemos ir try_filesa trabajar, pero no funcionalocation /
Jeff Atwood

+1, por ejemplo, de usar llaves :)
Danila Vershinin

4

Algo en la línea:

location ~ \.php$ {
  # only rewrite URL's with args
  if ($args != '') {
    rewrite .* "${uri}_${args}?" last;
  }
}

Dejaste de lado ?mi respuesta utilizada.
chx

¿Estás en lo correcto con respecto a?, En cuanto a tu respuesta, me tomó un tiempo probarlo en un servidor real, así que no he visto el tuyo hasta que publiqué el mío. Y el suyo reescribirá todas las URL, incluso sin argumentos para variar con "_", lo que puede ser indeseable.
Max Gashkov

La solución de Matthias con try_files es en realidad más preferible a esto.
Max Gashkov

podemos hacerlo sin el if, cuando especificamos una .*cláusula más estricta en el primer parámetro de reescritura. ¡Esto es de gran ayuda!
Jeff Atwood

@JeffAtwood No creo que sea posible: los patrones de reescritura de nginx (así como la ubicación) deberían aplicarse a parte de una URL antes de la cadena de consulta solamente.
Max Gashkov

1

No creo que pueda hacer esto con vanilla nginx, pero si está dispuesto a instalar el módulo Lua para nginx ( http://wiki.nginx.org/HttpLuaModule ) puede hacerlo.

server {
    server_name so.dev;
    listen 80;

    location / {

        root /tmp;

        rewrite_by_lua '
            local uri = ngx.var.uri
            local params = ngx.req.get_uri_args(0)

            for key, value in pairs(params) do
                uri = string.format("%s_%s=%s", uri, key, value)
            end

            ngx.req.set_uri(uri)
            ngx.req.set_uri_args({})
        ';

    }
}

Lo probé localmente y parece hacer lo que estás buscando. Si desea mantener otros parámetros separados por símbolos, cambie el bloque rewrite_by_lua para que sea

local uri = ngx.var.uri
local param_string = ""
local params = ngx.req.get_uri_args(0)
local separator = ""

for key, value in pairs(params) do
    param_string = param_string .. separator .. key .. "=" .. value
    separator = "&"
end

ngx.req.set_uri(uri .. "_" .. param_string)
ngx.req.set_uri_args({})

1

Esto funciona en nginx / 1.6.2.

rewrite ^/.*\.php$ "${uri}_${args}";

Pero personalmente usaría la try_filessolución con un respaldo a un URI original si hay alguno .

try_files $uri "${uri}_${args}";

Por ejemplo, si tiene script.phpen el disco, primero lo intentará, y luego, si no hay ninguno, lo buscará script.php_t=12. try_filesnecesita una versión de nginx lo suficientemente reciente.

Y si esto no es suficiente, puede hacer esto dentro de un if:

return 301 "${uri}_${args}";

Me gusta esto, pero no podemos hacer que el bit try_files funcione realmente, mientras que la reescritura funciona. (bueno, si agrega un ?al final de su reescritura allí para que no se agreguen los parámetros de consulta ...)
Jeff Atwood

@JeffAtwood try_filesse detendrá $urisi hay un bloque de ubicación que coincida con eso, o un archivo para ello (la solicitud sin obtener argumentos), ¿es ese el caso?
AD7six

@JeffAtwood ?no tiene ningún efecto visible en los archivos estáticos, solo verá una consulta adicional en sus registros de acceso; si te importa, seguro agrega un signo de interrogación
sanmai

0

La wiki dice

Si especificas un? al final de una reescritura, Nginx soltará los $ args (argumentos) originales.

Entonces rewrite ^ ${uri}_$args? last;debería funcionar.


nginx no se reinicia con ese conjunto de reglas -rewrite ^ $uri_$args? last;
Jeff Atwood

Fijo. El $ -bleeds-over-the-variables se siente como PHP, que sé que te encanta.
chx

incluso con la versión revisada, nginx no se reinicia
Jeff Atwood

0

Las respuestas sugeridas anteriormente deberían funcionar. Sin embargo, verá cuán sensible se vuelve su URL. Debido a que nginx intenta verificar si el nombre del archivo existe primero en el servidor, cualquier parámetro adicional lo descartará.

http://example.com/script.php?t=12 // works 
http://example.com/script.php?t=12&_utm=twitter // not work

Mi sugerencia es dejar la URL como está y enrutarla al archivo correcto con php. Tienes acceso al tparámetro.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.