Nginx proxy inverse + réécriture d'URL

149

Nginx fonctionne sur le port 80 et je l'utilise pour inverser les URL de proxy avec le chemin /food' accès au port de 3200cette façon:

location /foo {
                proxy_pass http://localhost:3200;
                proxy_redirect     off;
                proxy_set_header   Host $host;
}

Cela fonctionne bien, mais j'ai une application sur le port 3200, pour laquelle je ne veux pas que l'initiale /foosoit envoyée. C’est-à-dire que, lorsque j’accède http://localhost/foo/bar, je veux seulement /barêtre le chemin reçu par l’application. J'ai donc essayé d'ajouter cette ligne au bloc de localisation ci-dessus:

rewrite ^(.*)foo(.*)$ http://localhost:3200/$2 permanent;

Cela provoque 302 redirection (changement d'URL), mais je veux 301. Que dois-je faire?

Jeffreyveon
la source
Si vous avez un problème avec le boîtier Grafana, vous devez utiliser la recette suivante: docs.grafana.org/installation/behind_proxy/…
mohsen saeedi

Réponses:

168

Toute redirection vers localhost n'a pas de sens depuis un système distant (par exemple, le navigateur Web du client). Ainsi, les indicateurs de réécriture permanents (301) ou de redirection (302) ne sont pas utilisables dans votre cas.

Essayez de suivre la configuration en utilisant une règle de réécriture transparente:

location  /foo {
  rewrite /foo/(.*) /$1  break;
  proxy_pass         http://localhost:3200;
  proxy_redirect     off;
  proxy_set_header   Host $host;
}

Utilisez curl -ipour tester vos réécritures. Un changement très subtil de la règle peut amener nginx à effectuer une redirection.

Jens Bradler
la source
1
Le chemin de l'URL commence toujours par / foo dans mon application quand je fais ça ...
jeffreyveon
Il doit y avoir un problème différent. J'ai reproduit ce scénario avec succès, il y a quelques minutes à peine. URL originale: http: // development / foo / testme / 1234 - REQUEST_URI d'un script PHP s'exécutant sur un Apache connecté en tant que serveur proxy: '/ testme / 1234'
Jens Bradler le
9
La regex devrait probablement être /foo(.*), sinon example.com/foone seront pas appariés. (ce qui est probablement ce que jeffreyveon a connu)
Benno
Ce type de travail fonctionne, mais mon corps que je suis en train de définir avec proxy_set_body est en train d'être supprimé.
Justin Thomas
réécrire /(.*) /socket.io/ break; GAGNEZ MON JOUR POUR SOCKET.IO
user956584
124

La correspondance de préfixe d'emplacement simple fonctionne pour cela sans utiliser de règle de réécriture tant que vous spécifiez un URI dans la directive proxy_pass:

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

Remarquez le supplément /à la fin de la proxy_passdirective. NGINX supprimera le préfixe correspondant /fooet transmettra le reste au serveur principal à l'URI /. Par conséquent, http://myserver:80/foo/barpublierons sur le backend à http://localhost:3200/bar.

À partir de la documentation NGINX sur proxy_pass :

Si la directive proxy_pass est spécifiée avec un URI, 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:

DanArl
la source
12
Fonctionne pour moi que je n'ai ajouté / à l'emplacement / foo / {
Andrei N
C'était précisément ce que je cherchais!
Anbiniyar
6
C'est une solution très propre, je préférerais que ce soit la réponse canonique à la question.
ralien
2
Il a fallu trop longtemps pour comprendre l’importance de conserver ou de supprimer les barres obliques.
Parvez
5
Cela passera réellement //xyzà l'hôte si vous faites cela.
Archimedes Trajano
60

La manière la plus correcte et la meilleure pratique sont généralement les suivantes:

location /foo/ {
    proxy_pass http://localhost:3200/; # note the trailing slash!
}

  • Notez la très grande importance de la barre oblique finaleproxy_pass , qui modifie automatiquement la $urivariable pour faire correspondre le /foo/côté frontal avec /le fond. Pas besoin de rewritedirective explicite .

  • De plus, notez que la fin /de lalocation est aussi très importante dans le - sans cela, vous risqueriez d’avoir des URL bizarres sur votre site à un moment donné (par exemple, un travail /fooenen plus de /foo/en).

    De plus, la fin /dans le locationavec proxy_passassure également un traitement spécial , conformément à la documentation de la locationdirective, pour générer efficacement une implicite location = /foo {return 301 /foo/;}.

    Ainsi, en définissant un locationavec la barre oblique finale comme ci-dessus, vous garantissez non seulement que les URL de type suffixe sans barre oblique ne /fooenseront pas valables, mais également qu’une /foobarre oblique sans fin continuera à fonctionner également.


Documentation de référence:

cnst
la source
On dirait qu'ils $argssont perdus: ils http://frontend/foo?bar=bazseront représentés par un proxy http://backend/. Notez que les arguments ne font pas partie de l'URL
Vanuan
@Vanuan, êtes-vous sûr de cela? Je suis presque certain que vous $argsdevriez quand même utiliser le code ci-dessus, car ils sont distincts $uriet doivent être réassemblés, sauf si vous utilisez des variables explicites dans votre proxy_pass.
cnst
@ cnst Oh, je vois. J'utilise la variable hôte. C'est contre-intuitif.
Vanuan
1
@ArchimedesTrajano, vous avez tort, car il existe un traitement spécial pour la /fooredirection vers /foo/. Par conséquent, à moins que vous ne fassiez quelque chose de bizarre dans le backend, même les /foodemandes fonctionneront toujours avec le code ci-dessus. (C'est en fait déjà une partie de la réponse, BTW.)
cnst
3
Bien que cette solution "semble" s'imposer sur tous ces forums, il convient de noter dans la réponse elle-même que Nginx décodera l'URL de l'URL et transmettra l'URL décodée au serveur mandaté. Donc, cette solution ne fonctionnera pas si votre URL contient des parties encodées.
codage
1

essayer

location /foo {
    proxy_pass http://localhost:3200/;
    ....

ou

location ^~ /foo {
    proxy_pass http://localhost:3200/;
    ....
DerGeh
la source
13
Cette réponse serait utile si vous expliquez pourquoi elle doit être configurée comme ci-dessus.
masegaloeh
Cela passera réellement //xyzà l'hôte si vous faites cela.
Archimedes Trajano
1

@Terabuck Désolé de ne pas avoir encore répondu.

Vous ne devez pas utiliser localhost car vous dépendez du fait que l'application s'exécute sur un serveur avec un fichier hosts. hôte local est seulement une traduction par défaut à 127.0.0.1. Rien n'indique que vous devez avoir ces fichiers hôtes. C'est très courant d'en avoir un.

Avoir une interface de bouclage est encore une autre chose commune, mais vous dépendez toujours de l'interface de bouclage sur la pile de mise en réseau. C'est un cas rare de ne pas avoir ces deux. Si jamais vous vous inquiétez de cela. Au moins sur unix / linux, vous avez l’option pour les sockets. Cela éliminera le besoin de la pile réseau pour atteindre l'hôte local. Soyez prudent avec cette approche car il y a quelques facteurs qui seront mis en place sur le système d'exploitation hôte. Tels que le nombre de fichiers ouverts, etc.

Cody Van Lith
la source