Nginx Redirect via Proxy, Rewrite and Preserve URL

71

Dans Nginx, nous avons essayé de rediriger une URL de la manière suivante:

http://example.com/some/path -> http://192.168.1.24

où l'utilisateur voit toujours l'URL d'origine dans son navigateur. Une fois que l'utilisateur est redirigé, disons qu'il clique sur le lien vers /section/index.html, nous voudrions que cela fasse une demande qui mène à la redirection

http://example.com/some/path/section/index.html -> http://192.168.1.24/section/index.html

et encore conservez l'URL d'origine.

Nos tentatives ont impliqué diverses solutions utilisant des proxies et des règles de réécriture. Le schéma ci-dessous illustre la configuration qui nous a rapprochés le plus d'une solution (notez qu'il s'agit de la configuration de serveur Web pour le example.comserveur Web). Cependant, il y a toujours deux problèmes avec ceci:

  • La réécriture n’est pas effectuée correctement car l’URL de la demande reçue par le serveur Web http://192.168.1.24inclut /some/pathet ne peut donc pas servir la page requise.
  • Lorsque vous passez la souris sur un lien une fois qu'une page a été servie, il /some/pathmanque l'URL

    server {
        listen          80;
        server_name     www.example.com;
    
        location /some/path/ {
            proxy_pass http://192.168.1.24;
            proxy_redirect http://www.example.com/some/path http://192.168.1.24;
            proxy_set_header Host $host;
        }
    
        location / {
            index index.html;
            root  /var/www/example.com/htdocs;
        }
    }
    

Nous recherchons une solution qui implique uniquement de modifier la configuration du serveur Web example.com. Nous sommes en mesure de modifier la configuration sur 192.168.1.24(également sur Nginx), mais nous souhaitons éviter cela car nous devrons répéter cette configuration pour des centaines de serveurs différents dont l'accès est contrôlé par proxy example.com.

robjohncox
la source

Réponses:

59

Premièrement, vous ne devriez pas utiliser de rootdirective dans le bloc d’emplacement, c’est une mauvaise pratique. Dans ce cas, cela n'a pas d'importance.

Essayez d’ajouter un deuxième bloc d’emplacement:

location ~ /some/path/(?<section>.+)/index.html {
    proxy_pass http://192.168.1.24/$section/index.html;
    proxy_set_header Host $host;
}

Cela capture la partie après / some / path / et before index.html dans une variable $ section, qui est ensuite utilisée pour définir la destination proxy_pass. Vous pouvez rendre la regex plus spécifique si vous en avez besoin.

Tero Kilkanen
la source
1
Toutes mes excuses pour la réponse tardive - cela nous rapproche tellement de ce que nous recherchons. Le seul inconvénient est que, une fois la page cible servie, les URL des liens dans le navigateur n'incluent pas «/ some / path /», ce qui signifie qu'elles ne fonctionnent pas si un utilisateur clique dessus. Si nous pouvons trouver un moyen de surmonter ce problème, je mettrai à jour et accepterai cette réponse, car elle en est presque là.
robjohncox
8
Les liens que le navigateur voit sont générés par le logiciel exécuté sur le serveur 192.168.1.24. Vous devez modifier ce logiciel pour obtenir ce que vous voulez.
Tero Kilkanen
Je ne suis pas sûr de suivre votre avertissement concernant le blocage de la racine à l'intérieur du bloc d'emplacement. lire la documentation nginx est la bonne façon de faire des choses. ils préviennent seulement les mauvaises pratiques de ne pas avoir de racine par défaut en dehors de tous les emplacements. nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/…
guy mograbi
Eh bien, il est plus facile d’avoir une règle empirique à ne pas utiliser rootdans un locationbloc, vous n’obtiendrez donc aucun comportement inattendu pour les emplacements par défaut. Si vous devez modifier la valeur rootpar défaut pour chaque emplacement, vous pouvez l'utiliser.
Tero Kilkanen le
1
Qu'entendez-vous par recevoir l'hôte $ comme nom ? Quel est l'en-tête HTTP exact qui a été envoyé et que voulez-vous qu'il envoie exactement?
Tero Kilkanen
65

Vous devez utiliser la partie URI de la proxy_passdirective. En outre, vous avez mélangé les arguments d'ordre de proxy_redirectdirective et vous n'en avez probablement pas besoin du tout. Nginx a une valeur par défaut raisonnable pour cette directive.

Dans ce cas, votre locationbloc pourrait être très simple:

location /some/path/ {
    proxy_pass http://192.168.1.24/;
    # note this slash  -----------^
    proxy_set_header Host $host;
}
Alexey Ten
la source
1
Toutes mes excuses pour la réponse tardive. J'ai essayé cela et malheureusement, cela ne fonctionne pas pour notre cas d'utilisation. Le problème est que, lorsque la demande est faite sur le serveur cible, la /some/path/partie de l'URL est conservée dans la demande, ce qui n'est pas une URL valide (nous devons également réécrire l'URL pour la supprimer).
robjohncox
@ robjohncox qu'avez-vous essayé exactement?
Alexey Ten
10
la barre oblique a fait l'affaire pour moi. Maintenant, mydomain.com/some/path/* est correctement
envoyé au
8
Est-ce que je peux upvoter le commentaire "# not this this slash" dans cette réponse? Trois acclamations pour ce commentaire!
8one6
Vous ne savez pas comment cela fonctionne pour vous tous. C'est ce que j'essaie de réaliser. Toutefois, lorsqu'un utilisateur clique sur un lien qui redirigerait, par exemple, 192.168.1.24/login sur le service local, il est redirigé sur mydomain.com/login au lieu de mydomain.com/some/path/login
mueslo le
4

Vous pouvez utiliser la configuration suivante pour obtenir un mappage transparent à 100% entre /some/path/le serveur frontal et /le serveur principal.

Notez que c’est la seule réponse jusqu’à présent qui prendrait également en charge de manière transparente les chemins absolus générant des 404 Not Founderreurs, à condition que le bon en- Referertête HTTP soit envoyé par le navigateur. Ainsi, tous ces gifs devraient continuer à se charger sans qu'il soit nécessaire de modifier le code HTML sous-jacent. (ce qui est non seulement coûteux, mais n’est pas non plus pris en charge sans modules supplémentaires non compilés par défaut).

location /some/path/ {
    proxy_pass http://192.168.1.24/; # note the trailing slash!
}
location / {
    error_page 404 = @404;
    return 404; # this would normally be `try_files` first
}
location @404 {
    add_header Vary Referer; # sadly, no effect on 404
    if ($http_referer ~ ://[^/]*(/some/path|/the/other)/) {
        return 302 $1$uri;
    }
    return 404 "Not Found\n";
}

Vous trouverez la preuve complète de concept et produit viable minimale dans le https://github.com/cnst/StackOverflow.cnst.nginx.conf référentiel.

Voici un test visant à confirmer que tous les cas extrêmes semblent fonctionner:

curl -v -H 'Referer: http://example.su/some/path/page.html' localhost:6586/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location
> GET /and/more.gif HTTP/1.1
> Referer: http://example.su/some/path/page.html
< HTTP/1.1 302 Moved Temporarily
< Location: http://localhost:6586/some/path/and/more.gif
< Vary: Referer

curl -v localhost:6586/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location
> GET /and/more.gif HTTP/1.1
< HTTP/1.1 404 Not Found

curl -v localhost:6586/some/path/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location -e uri
> GET /some/path/and/more.gif HTTP/1.1
< HTTP/1.1 200 OK
request_uri:    /and/more.gif

PS Si vous avez beaucoup de chemins différents à la carte, puis au lieu de faire une comparaison regex $http_refererdans un au ifsein location @404, vous voudrez peut - être utiliser la base global mapdirective à la place.

Notez également que les barres obliques finales dans le proxy_pass, ainsi que dans le locationcontenu, sont assez importantes selon une réponse associée .

Références:

cnst
la source
2

Lorsque cette barre oblique est ajoutée à un jenkins mandaté par Nginx, le message d'erreur «Il semble que la configuration de votre proxy inverse soit défectueuse».

proxy_pass          http://localhost:8080/;

Remove this -----------------------------^

Il faut lire

proxy_pass          http://localhost:8080;
Tomdunn
la source
Je ne pense pas que cela corresponde à la question du PO ni à la résolution des problèmes mentionnés.
Cory Robinson