Ответ
Когда Django-приложение работает за обратным прокси-сервером (например, Nginx), информация о реальном клиенте (IP-адрес, протокол) передается через HTTP-заголовки. Для корректной обработки этих данных необходимо настроить Django.
Ключевые заголовки:
X-Forwarded-For: Содержит исходный IP-адрес клиента.X-Forwarded-Proto: Содержит исходный протокол (httpилиhttpsси).X-Forwarded-Host: Содержит исходное имя хоста, запрошенное клиентом.
Настройка в settings.py:
Чтобы Django доверял этим заголовкам и использовал их для обновления объекта request, необходимо установить следующие параметры:
# settings.py
# Доверяем заголовку X-Forwarded-Proto, который устанавливает прокси-сервер
# Это необходимо, чтобы request.is_secure() возвращал True для HTTPS-запросов
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Доверяем заголовку X-Forwarded-Host
USE_X_FORWARDED_HOST = True
# Обязательно добавьте доменное имя или IP-адрес вашего прокси в ALLOWED_HOSTS
ALLOWED_HOSTS = ['your-domain.com', 'localhost', '127.0.0.1']
Пример конфигурации Nginx:
Прокси-сервер должен быть настроен для передачи этих заголовков.
location / {
# Передаем запрос на Gunicorn/uWSGI
proxy_pass http://unix:/run/gunicorn.sock;
# Устанавливаем заголовки, которые будет использовать Django
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
После этой настройки request.META['REMOTE_ADDR'] будет содержать реальный IP-адрес клиента, а request.is_secure() будет корректно работать для HTTPS-соединений.
Ответ 18+ 🔞
А, ну это же классика, блядь! Ситуация, когда твой Django-сервер сидит где-то в подвале за Nginx, как Герасим под лестницей, и нихуя не видит, кто к нему на самом деле стучится.
Смотри, чувак. Клиент приходит не к тебе, а к этому здоровенному прокси-серверу — Nginx, нашему немому, но сильному Герасиму. А тот уже передаёт запрос дальше, в твоё приложение. И вот тут начинается пиздец: для Django все запросы теперь как будто от самого Герасима, а не от реального юзера. IP-адрес? Протокол? Хост? Всё ебнулось!
Чтобы не было этой трагедии, как с Муму, нужно Герасиму (Nginx) оставить записку — специальные заголовки. А Django научить эти записки читать и доверять им.
Вот эти самые волшебные бумажки-заголовки:
X-Forwarded-For: Тут лежит настоящий IP-адрес того, кто изначально стучался. Типа «Муму пришла от Василия Пупкина».X-Forwarded-Proto: А тут написано, по какой дороге шли —httpилиhttps. Чтобы твоё приложение не думало, что все ходят по грязи, когда на самом деле ездят по асфальту.X-Forwarded-Host: Имя хоста, которое изначально просили. Чтобыrequest.get_host()не возвращал тебеlocalhost:8000, когда юзер заходил наsuper-site.com.
Теперь, сука, настраиваем Django (settings.py):
Нужно ему втолковать, что этим бумажкам от Герасима можно верить. А то он, блядь, максималист, как тот Герасим — либо верит только своему, либо нихуя.
# settings.py
# Говорим: "Слушай, Django, если в заголовке 'X-Forwarded-Proto' написано 'https' — значит, запрос безопасный, блядь. Доверяем!"
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Говорим: "А ещё смотри на заголовок 'X-Forwarded-Host', когда определяешь, кто тебя звал".
USE_X_FORWARDED_HOST = True
# И не забудь, ебушки-воробушки, вписать сюда всех, кто имеет право к тебе стучаться.
# И Герасима (прокси), и реальных клиентов (домены). А то нахуярит тебе в дверь, и ты будешь орать "Кто серит на моей крыше?!".
ALLOWED_HOSTS = ['your-domain.com', 'localhost', '127.0.0.1']
А теперь пишем инструкцию для Герасима (конфиг Nginx):
Чтобы он не молчал, как немой, а передавал эти важные записки.
location / {
# Шлём запрос дальше, на Gunicorn или куда ты там его засунул
proxy_pass http://unix:/run/gunicorn.sock;
# А вот и сами записки-заголовки, которые мы передаём
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Сюда цепочку IP-адресов пихает
proxy_set_header X-Forwarded-Proto $scheme; # А тут протокол — http или https
}
После этого, ёпта, в твоём Django request.META['REMOTE_ADDR'] будет показывать уже не Герасима (IP прокси), а реального клиента. И request.is_secure() перестанет врать про HTTPS. Всё, проблема решена, можно жить дальше, не топя свои проекты, как несчастную Муму.