Désactiver le décodage d'URL dans le proxy nginx

21

Lorsque je navigue vers cette URL: http://localhost:8080/foo/%5B-%5Dserver ( nc -l 8080) le reçoit tel quel:

GET /foo/%5B-%5D HTTP/1.1

Cependant, lorsque je proxy cette application via nginx (1.1.19):

location /foo {
        proxy_pass    http://localhost:8080/foo;
}

La même demande acheminée via le port nginx est transmise avec le chemin décodé:

GET /foo/[-] HTTP/1.1

Les crochets décodés dans le chemin GET sont à l'origine des erreurs sur le serveur cible ( état HTTP 400 - caractère illégal dans le chemin ... ) car ils arrivent sans échappement.

Existe-t-il un moyen de désactiver le décodage d'URL ou de le recoder afin que le serveur cible obtienne exactement le même chemin lorsqu'il est acheminé via nginx? Une règle de réécriture d'URL intelligente?

Tomasz Nurkiewicz
la source
Bogue
Tomasz Nurkiewicz

Réponses:

19

Citant Valentin V. Bartenev (qui devrait obtenir le crédit complet pour cette réponse):

Une citation de la documentation :

  • Si proxy_pass est spécifié avec l'URI , lors du passage d'une demande au serveur, une partie d'un URI de demande normalisé correspondant à l'emplacement est remplacée par un URI spécifié dans la directive

  • Si proxy_passest spécifié sans URI , un URI de demande est transmis au serveur sous la même forme que celle envoyée par un client lors du traitement d'une demande d'origine

La configuration correcte dans votre cas serait:

location /foo {
   proxy_pass http://localhost:8080;
}
Tomasz Nurkiewicz
la source
8
J'ai dû changer http://localhost:8080/pour http://localhost:8080au cas où quelqu'un aurait la même situation que moi.
herrtim
4
Pourquoi Nginx décode-t-il l'URI avant de le transmettre au serveur principal? Cela n'aurait-il pas plus de sens de ne pas toucher l'URI?
ornithorynque
@platypus, il reste intact jusqu'à ce que vous commenciez explicitement à effectuer les substitutions
cnst
2

Notez que le décodage d'URL, communément appelé $uri"normalisation" dans la documentation de nginx, se produit avant l'IFF backend:

  • soit n'importe quel URI est spécifié en proxy_passlui-même, même si seule la barre oblique de fin est isolée,

  • ou, l'URI est modifié pendant le traitement, par exemple, via rewrite.


Les deux conditions sont explicitement documentées sur http://nginx.org/r/proxy_pass (c'est moi qui souligne):

  • Si la proxy_passdirective est spécifiée avec un URI , alors lorsqu'une demande est transmise au serveur, la partie d'un URI de demande normalisée correspondant à l'emplacement est remplacée par un URI spécifié dans la directive

  • Si proxy_passest spécifié sans URI , l'URI de demande est transmis au serveur sous la même forme que celle envoyée par un client lorsque la demande d'origine est traitée, ou l' URI de demande normalisé complet est transmis lors du traitement de l' URI modifié


La solution consiste soit à omettre l'URI comme dans le cas des PO, soit à utiliser une rewriterègle intelligente :

# map `/foo` to `/foo`:
location /foo {
    proxy_pass  http://localhost:8080;  # no URI -- not even just a slash
}

# map `/foo` to `/bar`:
location /foo {
    rewrite  ^  $request_uri;            # get original URI
    rewrite  ^/foo(/.*)  /bar$1  break;  # drop /foo, put /bar
    return 400;   # if the second rewrite won't match
    proxy_pass    http://localhost:8080$uri;
}

Vous pouvez le voir en direct dans une réponse Stack Overflow associée , y compris un groupe de contrôle.

cnst
la source
La documentation est déroutante ici. Les deux formulaires contiennent un URI. C'est le composant de chemin d'accès qui est présent dans l'un et manquant dans l'autre.
Michael Hampton
@MichaelHampton, je ne suis pas d'accord - le CHEMIN est généralement appelé l'URI, donc celui sans le chemin ne contient pas l'URI.
2018
Un chemin relatif seul peut également être une URL valide, bien sûr. Le fait est que le reste est également un URI valide (par exemple http://localhost:8080). Si vous n'êtes pas d'accord, vous pouvez le reprendre avec les auteurs de la RFC 3986.
Michael Hampton
@MichaelHampton Malheureusement, il semble que le schéma et le chemin soient obligatoires pour être un URI, l'autorité, les arguments, le fragment sont facultatifs
Norman Xu