Обратный прокси Nginx + перезапись URL

149

Nginx работает на порте 80, и я использую его для реверсирования URL прокси с путем /fooк порту 3200следующим образом:

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

Это работает нормально, но у меня есть приложение на порт 3200, для которого я не хочу /fooотправлять инициалы. То есть - когда я получаю доступ http://localhost/foo/bar, я хочу /barбыть тем путем, который получит приложение. Поэтому я попытался добавить эту строку в блок местоположения выше:

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

Это вызывает 302 редирект (изменение в URL), но я хочу 301. Что мне делать?

jeffreyveon
источник
Если у вас есть какие-либо проблемы с делом Графана, вы должны использовать этот рецепт: docs.grafana.org/installation/behind_proxy/…
mohsen saeedi

Ответы:

168

Любое перенаправление на localhost не имеет смысла с удаленной системы (например, веб-браузера клиента). Таким образом, флаги перезаписи перманент (301) или перенаправление (302) в вашем случае непригодны.

Пожалуйста, попробуйте выполнить следующие настройки, используя прозрачное правило перезаписи:

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

Используйте, curl -iчтобы проверить ваши переписывает. Очень тонкое изменение правила может привести к тому, что nginx выполнит перенаправление.

Дженс Брэдлер
источник
1
Путь URL все еще начинается с / foo в моем приложении, когда я это делаю ...
jeffreyveon
Должна быть другая проблема. Я успешно воспроизвел этот сценарий, всего несколько минут назад. Исходный URL: http: // development / foo / testme / 1234 - REQUEST_URI PHP-скрипта, работающего на Apache, подключенном как прокси-сервер: '/ testme / 1234'
Jens Bradler
9
Регулярное выражение, вероятно, должно быть /foo(.*), иначе не example.com/fooбудет совпадать. (что, вероятно, пережил Джеффривеон)
Бенно
Это работает, но мое тело, которое я настраиваю с помощью proxy_set_body, удаляется.
Джастин Томас
переписать /(.*) /socket.io/ break; СОХРАНИТЕ МОЙ ДЕНЬ ДЛЯ SOCKET.IO
user956584
124

Простое сопоставление префиксов местоположения работает для этого без использования правила перезаписи, если вы указали URI в директиве proxy_pass:

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

Обратите внимание на дополнительное /в конце proxy_passдирективы. NGINX удалит соответствующий префикс /fooи передаст остаток на внутренний сервер по URI /. Поэтому http://myserver:80/foo/barбудет публиковать в бэкэнд на http://localhost:3200/bar.

Из документов NGINX на proxy_pass :

Если директива proxy_pass указана с помощью URI, то при передаче запроса на сервер часть нормализованного URI запроса, соответствующая местоположению, заменяется на URI, указанный в директиве:

DanArl
источник
12
Работает для меня, чем я добавил / к местоположению / foo / {
Андрей Н
Это было именно то, что я искал!
Анбинияр
6
Это очень чистое решение, я бы предпочел, чтобы это был канонический ответ на вопрос.
Ралиен
2
Потребовалось слишком много времени, чтобы осознать важность сохранения или удаления косой черты.
Парвиз
5
Это на самом деле передается //xyzхосту, если вы это сделаете.
Архимед Траяно
60

Абсолютно самый правильный путь и лучшая практика обычно таковы:

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

  • Обратите внимание на крайнюю важность конечной косой чертыproxy_pass , которая автоматически изменяет $uriпеременную, чтобы она /foo/соответствовала /на внешнем интерфейсе на внутреннем. Нет необходимости в явной rewriteдирективе.

  • Кроме того, обратите внимание, что также очень важен трейлинг /вlocation - без него вы рискуете иметь странные URL на вашем сайте в один момент (например, работать /fooenв дополнение к /foo/en).

    Кроме того, трейлинг /в locationwith proxy_passтакже обеспечивает некоторую особую обработку в соответствии с документацией locationдирективы, чтобы эффективно вызывать и неявное location = /foo {return 301 /foo/;}.

    Таким образом, определяя a locationс помощью завершающей косой черты, как указано выше, вы не только гарантируете, что URL-адреса с суффиксами без косой черты, например /fooen, не будут действительными, но также и то, что /fooбез завершающей косой черты будет также работать.


Справочная документация:

CNST
источник
Похоже $args, потерялись: http://frontend/foo?bar=bazбудут прокси на http://backend/. Обратите внимание, что арги не являются частью URL
Вануан
@ Вануан, ты уверен в этом? Я почти уверен, $argsчто все равно следует обращаться надлежащим образом, если вы используете приведенный выше код, поскольку они отделены от $uriи должны быть собраны обратно, если только вы не используете явные переменные в своем proxy_pass.
CNST
@ cnst О, я вижу. Я использую переменную хоста. Это противоречит интуиции.
Вануан
1
@ArchimedesTrajano, вы не правы, поскольку существует специальная обработка для /fooперенаправления /foo/, поэтому, если вы не делаете что-то странное в бэкэнде, даже /fooзапросы будут работать с приведенным выше кодом. (Это на самом деле уже часть ответа, кстати.)
cnst
3
Хотя это решение «кажется» выигрывает на всех этих форумах, в самом ответе следует отметить, что Nginx будет URL-декодировать URL-адрес и передавать декодированный URL-адрес проксируемому серверу. Таким образом, это решение не будет работать, если ваш URL содержит части, закодированные в URL.
кодирование
1

пытаться

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

или же

location ^~ /foo {
    proxy_pass http://localhost:3200/;
    ....
DerGeh
источник
13
Этот ответ был бы хорош, если бы вы дали некоторое объяснение, почему он должен быть настроен, как указано выше.
masegaloeh
Это на самом деле передается //xyzхосту, если вы это сделаете.
Архимед Траяно
1

@Terabuck Извините, что не ответил ни одного представителя еще.

Вы не должны использовать localhost, потому что вы зависите от того факта, что приложение работает на сервере с файлом hosts. локальный хост - это только перевод по умолчанию на 127.0.0.1. Ничто не говорит о том, что у вас должны быть эти файлы хостов. Просто очень часто есть один.

Наличие интерфейса с обратной связью - это еще одна распространенная вещь, от которой зависит зависимость, но вы все еще зависите от интерфейса с обратной связью в сетевом стеке. Это редкий случай не иметь эти два. Если вы когда-нибудь беспокоитесь об этом. По крайней мере, в Unix / Linux у вас есть возможность для сокетов. Это исключит необходимость того, чтобы сетевой стек достиг локального хоста. Будьте осторожны с этим подходом, так как в хост-системе появятся несколько факторов. Например, количество открытых файлов и т. Д.

Коди Ван Лит
источник