Nginx Redirect через прокси, переписать и сохранить URL

71

В Nginx мы пытались перенаправить URL следующим образом:

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

где пользователь все еще видит исходный URL в своем браузере. Как только пользователь будет перенаправлен, скажем, он щелкнет ссылку /section/index.html, мы бы хотели, чтобы он отправил запрос, который приведет к перенаправлению

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

и снова сохраняем исходный URL.

В наших попытках использовались различные решения, использующие прокси-серверы и правила перезаписи, и ниже показана конфигурация, которая приблизила нас к решению (обратите внимание, что это конфигурация веб-сервера для example.comвеб-сервера). Тем не менее, есть еще две проблемы с этим:

  • Он не выполняет перезапись должным образом, поскольку URL-адрес запроса, полученный веб-сервером, http://192.168.1.24содержит /some/pathи, следовательно, не обслуживает требуемую страницу.
  • При наведении на ссылку, когда страница обслуживается, /some/pathотсутствует в 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;
        }
    }
    

Мы ищем решение, которое включает только изменение конфигурации веб-сервера example.com. Мы можем изменить конфигурацию 192.168.1.24(также Nginx), однако мы хотим попытаться избежать этого, потому что нам нужно будет повторить эту настройку для сотен различных серверов, доступ к которым осуществляется через прокси example.com.

robjohncox
источник

Ответы:

59

Во-первых, вы не должны использовать rootдирективу внутри блока местоположения, это плохая практика. В этом случае это не имеет значения, хотя.

Попробуйте добавить второй блок местоположения:

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

Это захватывает часть после / some / path / и перед index.html в переменную $ section, которая затем используется для установки места назначения proxy_pass. Вы можете сделать регулярное выражение более конкретным, если вам нужно.

Теро Килканен
источник
1
Извините за поздний ответ - это так близко к достижению того, что мы ищем. Единственный недостаток заключается в том, что после обслуживания целевой страницы URL-адреса ссылок в браузере не включают в себя «/ some / path /», что означает, что они не работают, если пользователь нажимает на них. Если мы сможем решить, как преодолеть это, я обновлю и приму этот ответ, так как он почти готов.
robjohncox
8
Ссылки, которые видит браузер, генерируются программным обеспечением, работающим на сервере 192.168.1.24. Вы должны изменить это программное обеспечение для достижения того, что вы хотите.
Теро Килканен,
не уверен, что следую вашему предупреждению о корне внутри блока местоположения. чтение документации по nginx - это правильный путь. они только предупреждают о плохой практике, поскольку у них нет корневого каталога по умолчанию вне всех местоположений. nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/…
парень mograbi,
Что ж, проще иметь правило большого пальца, чтобы не использовать его rootвнутри locationблока, тогда вы не получите неожиданного поведения для местоположений по умолчанию. Только если вам нужно изменить настройки rootпо умолчанию для каждого местоположения, тогда вы можете использовать его.
Теро Килканен
1
Что вы подразумеваете под получением $ host в качестве имени ? Какой именно HTTP-заголовок был отправлен и что именно вы хотите, чтобы он отправил?
Теро Килканен
65

Вы должны использовать часть URI в proxy_passдирективе. Кроме того, вы перепутали порядок аргументов proxy_redirectдирективы, и, вероятно, вам это вообще не нужно. Nginx имеет разумное значение по умолчанию для этой директивы.

В этом случае ваш locationблок может быть очень простым:

location /some/path/ {
    proxy_pass http://192.168.1.24/;
    # note this slash  -----------^
    proxy_set_header Host $host;
}
Алексей Тен
источник
1
Извините за поздний ответ - я попробовал это, и, к сожалению, это не работает для нашего варианта использования. Проблема заключается в том, что при выполнении запроса на целевом сервере /some/path/часть URL-адреса сохраняется в запросе, который не является действительным URL-адресом (для его удаления нам также необходимо переписать URL-адрес).
robjohncox
@robjohncox что именно ты пробовал?
Алексей Тен
10
слеш сделал свое дело для меня. Теперь mydomain.com/some/path/* правильно проксируется на 192.168.1.24/*, а не на 192.168.1.24/some/path/*
Вадимо
8
Могу ли я добавить комментарий "# note this slash" в этом ответе? Три приветствия за этот комментарий!
8one6
Не уверен, как это работает для всех вас. Это то, что я пытаюсь достичь. Однако когда пользователь щелкает ссылку, которая перенаправляет, например, на 192.168.1.24/login в локальной службе, он перенаправляется на mydomain.com/login вместо mydomain.com/some/path/login
mueslo
4

Вы можете использовать следующую конфигурацию, чтобы иметь 100% бесшовное сопоставление между /some/path/интерфейсом и /бэкэндом.

Обратите внимание, что пока это единственный ответ, который также беспрепятственно позаботится об абсолютных путях, генерирующих 404 Not Foundошибки, при условии, что Refererбраузер отправляет правильный HTTP- заголовок, поэтому все эти картинки должны продолжать загружаться без какой-либо необходимости изменять базовый HTML. (что не только дорого, но и не поддерживается без дополнительных модулей, не скомпилированных по умолчанию).

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";
}

Вы можете найти полный проверочный концепт и минимально жизнеспособный продукт в репозитории https://github.com/cnst/StackOverflow.cnst.nginx.conf .

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

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 Если у вас есть много различных путей к карте, то вместо того , чтобы делать регулярные выражения сравнения в $http_refererпределах в ifпределах location @404, вы можете захотеть использовать глобальный основе mapдирективы вместо этого.

Также обратите внимание, что завершающие косые черты как в proxy_pass, так и в том виде, в котором locationон содержится, весьма важны в соответствии с соответствующим ответом .

Рекомендации:

CNST
источник
2

Когда эта косая черта добавляется в прокси-сервер nginx, у вас появляется сообщение «Похоже, что ваш обратный прокси-сервер сломан».

proxy_pass          http://localhost:8080/;

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

Следует читать

proxy_pass          http://localhost:8080;
tomdunn
источник
Я не думаю, что это то, что касалось вопроса ОП, и не решает ни одну из упомянутых проблем.
Кори Робинсон