nginx try_files перенаправляет по неверной схеме, когда стоит за балансировщиком нагрузки завершения SSL (haproxy)
У меня есть сервер nginx 1.6.2, работающий в качестве бэкэнда за балансировщиком нагрузки, который выполняет завершение SSL. Все общение с внутренними серверами происходит через HTTP.
Схема того, что происходит:
/--http---> frontend:80 --\
client --+ +--http---> backend:8000
\--https--> frontend:443 --/
LOAD BALANCER BACKENDS
Для тестирования у меня только один бэкэнд на данный момент. Балансировщик нагрузки запускает HAProxy 1.5, над которым у меня есть некоторый контроль.
У меня довольно типичный try_files
директива в моем server
блок в конфигурации бэкэнда nginx:
server {
server_name frontend;
...
try_files $uri $uri/ =404;
...
}
Теперь по умолчанию, когда я получаю доступ к каталогу без косой черты, например https://frontend/somedir
, nginx хочет отправить перенаправление HTTP 301 на абсолютный URL, например http://frontend:8000/somedir/
,
Я могу сделать nginx опустить номер порта 8000, используя port_in_redirect off
,
Однако я не могу исправить http://
Схема в начале перенаправления генерирует nginx. Лучшее, что я могу получить от nginx - это перенаправление с https://frontend/somedir
в http://frontend/somedir/
, эффективно зачищая SSL!
Балансировщик нагрузки отправляет X-Forwarded-Proto
заголовок, но я не вижу возможности для nginx обращаться к нему при создании своего редиректа; на самом деле есть ответ 2012 года о том, что nginx не может этого сделать, и решение состоит в том, чтобы заменить балансировщик нагрузки на nginx. ИМХО, это слишком тривиальная вещь, чтобы оправдать такое резкое изменение стека.
Здесь что-то изменилось с 2012 года? Я действительно не хочу переписывать эти перенаправления на уровне HAProxy: фактические преднамеренные перенаправления HTTPS на HTTP из веб-приложения могут получить "re-HTTPSed", если я просто всегда переписываю схему для Location:
заголовок ответа должен совпадать со схемой, с которой был сделан запрос.
РЕДАКТИРОВАТЬ:
Вот минимизированный конфиг, показывающий, что nginx производит абсолютно Location:
URL-адрес. Обратите внимание, что нет переписывает.
user nobody nobody;
worker_processes auto;
worker_rlimit_nofile 4096;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# TODO: Tune fastcgi_buffers/other buffers
# Configure keepalive connections
keepalive_timeout 15;
keepalive_requests 1000;
# Hide server version.
server_tokens off;
# Do not allow any directory indexes anywhere.
# This is the default, but it is here for extra paranoia.
autoindex off;
# gzip text content.
gzip on;
gzip_vary on;
gzip_disable "msie6";
gzip_comp_level 2;
gzip_min_length 1024;
gzip_types text/css
text/plain
text/xml
application/json
application/javascript;
server {
listen 8000 default_server;
root /usr/share/nginx/html;
index index.html index.htm;
server_name localhost;
location / {
try_files $uri $uri/ =404;
}
}
}
И если вы используете curl для просмотра заголовков - обратите внимание, я создал каталог testdir
под /usr/share/nginx/html
:
[myuser@dev nginx]$ curl -i http://localhost:8000/testdir
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 26 Mar 2015 14:35:49 GMT
Content-Type: text/html
Content-Length: 178
Location: http://localhost:8000/testdir/
Connection: keep-alive
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
1 ответ
Перемотка вперед до 2019 года, и вы можете:
absolute_redirect off;
port_in_redirect off;
Из документов дляabsolute_redirect
:
Если отключено, перенаправления, выпущенные nginx, будут относительными.
Вы должны сказать балансировщику нагрузки переписать http
в https
в запросах вроде так:
proxy_redirect http:// $scheme://;