Django за обратным прокси NGINX и AWS Application Load Balancer не получает HTTPS перенаправление от клиента в HTTP_X_FORWARDED_PROTO

Я запускаю Django на Gunicorn за NGINX reverse proxy и AWS Application Load Balancer. ALB имеет 2 слушателя. Слушатель HTTPS перенаправляет на целевую группу в порт 80, а слушатель HTTP перенаправляет на HTTPS.

enter image description here

Таким образом, ALB соединяется с кластером AWS ECS, который запускает задачу с двумя контейнерами: один для приложения Django, а другой для NGINX, который действует как обратный прокси для приложения Django. Вот конфигурация для обратного прокси NGINX:

upstream django {
    server web:8000;
}

server {
    listen       80;
    listen  [::]:80;

    location / {
        proxy_pass http://django;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_ssl_server_name on;
        proxy_redirect off;
    }
}

Такая конфигурация гарантирует, что всякий раз, когда клиент пытается обратиться к приложению сайта с помощью HTTP-запроса, он будет перенаправлен на HTTPS. И все работает хорошо за одним исключением. В Django, когда я запускаю request.is_secure(), я получаю False вместо True, как ожидалось. Если я запускаю request.build_absolute_uri(), я получаю http://mywebsite.com, а не https://mywebsite.com, как ожидалось.

Я уже пробовал добавить следующие строки в settings.py:

USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

, как объясняется в документации, но это не работает. Всякий раз, когда я проверяю request.META (или необработанный request.headers), я вижу 'HTTP_X_FORWARDED_PROTO': 'http' (и эквивалентный необработанный 'X-Forwarded-Proto': 'http') вместо https, как ожидалось. Стек правильно пересылает 'HTTP_X_FORWARDED_HOST': 'mywebsite.com' от клиента, но схема игнорируется.

Может ли кто-нибудь помочь мне определить, что я делаю неправильно и как это исправить? Спасибо

В классическом ELB вы указываете "порт экземпляра" (см. вкладку listeners), и он управляет протоколом, который вы отправляете вниз на nginx. В этом сценарии обычно прикрепляют SSL-сертификат к 443 порту, но посылают HTTP на 80 порт в nginx. Слушатель 80 порта также посылает HTTP. В такой ситуации, когда от балансировщика нагрузки поступает только HTTP, ваша задача - проверить заголовок X-Forwarded-Proto и выполнить постоянное перенаправление на HTTPS. Это происходит потому, что классический ELB не мог перенаправить HTTP на HTTPS. С помощью Application Load Balancer (ALB), я полагаю, вы можете перенаправить HTTP на HTTPS, если хотите говорить 443 с nginx.

В вашем конкретном случае похоже, что вы прослушивали только порт 80, поэтому вы, вероятно, отправляли HTTP только с балансировщика нагрузки. Проверьте порт и протокол вашего экземпляра.

Вернуться на верх