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?

Arpit Jalan
fuente
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;
}
Matthias Bayer
fuente
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;
  }
}
Max Gashkov
fuente
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({})
c17r
fuente
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}";
sanmai
fuente
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.

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

Ibu
fuente