Зворотний проксі-сервер 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. Що робити?


Якщо у вас є проблеми зі справою "Графана", скористайтеся цим рецептом: docs.grafana.org/installation/behind_proxy/…
mohsen saeedi

Відповіді:


169

Будь-яке переадресація на 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не буде збігатися. (що, мабуть, те, що пережив jeffreyveon)
Бенно

Цей вид працює, але моє тіло, яке я встановлюю за допомогою proxy_set_body, видаляється.
Джастін Томас

переписати /(.*) /socket.io/ перерва; Збережіть мій день для SOCKET.IO
користувач956584

122

Проста відповідність префікса місцеположення працює для цього без використання правила перезапису, якщо ви вказали 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, зазначеним у директиві:


12
Для мене працює, ніж я додав / до місцезнаходження / foo / {
Андрій N

Це саме те, що я шукав!
anbiniyar

6
Це дуже чисте рішення, я вважаю за краще це канонічна відповідь на питання.
ralien

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).

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

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


Довідкова документація:


Схоже $args, втрачено: http://frontend/foo?bar=bazбуде прокси http://backend/. Зауважте, що аргументи не є частиною URL-адреси
Вануан

@ Вануан, ти впевнений у цьому? Я впевнений, що $argsвсе-таки слід обробляти належним чином, якщо ви використовуєте код вище, оскільки вони відокремлені $uri, і вони повинні збиратися назад, якщо ви не використовуєте явні змінні у вашому proxy_pass.
cnst

@cnst О, я бачу. Я використовую змінну хоста. Це проти інтуїтивно зрозуміло.
Вануан

1
@ArchimedesTrajano, ви неправі, оскільки для /fooпереадресації є спеціальна обробка /foo/, тому, якщо ви не робите щось дивне на бекенді, навіть /fooзапити все ще працюватимуть із наведеним вище кодом. (Це насправді вже частина відповіді, BTW.)
cnst

3
Хоча це рішення «здається» виграшним на всіх цих форумах, у самій відповіді слід зазначити, що Nginx розшифрує URL-адресу та передасть декодовану URL-адресу проксі-серверу. Таким чином, це рішення не працюватиме, якщо у вашій URL-адресі є частини, що кодуються URL-адресою.
кодування

1

спробуйте

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

або

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

13
Ця відповідь буде добре, якщо ви дасте пояснення, чому вона повинна бути налаштована так, як вище.
masegaloeh

Це дійсно перейде //xyzдо хоста, якщо ви це зробите.
Архімед Траяно

1

@Terabuck Вибачте, що ще не відповів на відповідь.

Не слід використовувати localhost, оскільки це залежить від того, що програма працює на сервері з файлом хостів. локальний хост - це лише переклад за замовчуванням на 127.0.0.1. Нічого не говорить про те, що у вас повинні бути файли хостів. Просто дуже часто це є.

Наявність інтерфейсу зворотного зв'язку - це ще одна поширена річ, від якої залежать, але ви все ще залежите від інтерфейсного зворотного зв'язку в мережевому стеку. Рідкісний випадок, коли цих двох немає. Якщо ви коли-небудь турбуєтесь про це. Принаймні, на unix / linux у вас є можливість для сокетів. Це позбавить від необхідності мережевого стеку досягти локального хоста. Будьте обережні при такому підході, оскільки є кілька факторів, які будуть входити в хост ОС. Наприклад, кількість відкритих файлів тощо.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.