Django REST Framework для ссылок на страницы не использует HTTPS
Я настраиваю разбивку на страницы для определенной конечной точки DRF, которая работает хорошо, однако при развертывании на моем сервере, который использует HTTPS, ссылки на следующую и предыдущие страницы формируются с помощью http://
вместо https://
. Это заставляет браузер блокировать следующие/предыдущие страницы.
Я дважды проверил, что был отправлен исходный запрос, с HTTPS, а второй ответ на этот вопрос утверждает, что он должен использовать HTTPS в сформированных URL-адресах поскольку запрос пришел через HTTPS.
Первый ответ на тот же вопрос тоже не помог - я добавил строку X-Forwarded-Proto
в мою конфигурацию nginx и перезагрузился, но безрезультатно.
В документах DRF упоминается, что reverse() должен вести себя как базовый Django reverse, однако кажется довольно очевидным, что первоначальный запрос является HTTPS while возвращаемый URL-адрес - HTTP.
Вот несколько скриншотов, которые показывают начальный запрос (https://<domain>.com/api/leaderboard/
):
![введите описание изображения здесь]()
С ответом, содержащим next: http://<domain>.com/api/leaderboard/?page=2
):
![введите описание изображения здесь]()
Я понял, что это будет простая настройка, но не удалось найти что-либо после поиска как этого сайта, так и сайта DRF.
Это моя конфигурация nginx:
location / {
# proxy_pass http://127.0.0.1:9900;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
root /opt/app/client/dist;
index index.html index.htm;
}
Этот вопрос содержит довольно подробный ответ, но в конечном итоге говорит, что URL-адреса сформированы с тем же протоколом, что и запрос, что, похоже, не имеет места здесь, Нужно ли устанавливать этот Django SECURE_PROXY_SSL_HEADER? Я не был уверен, учитывая предупреждение, что это потенциально небезопасно.
Ответы
Ответ 1
Нужно ли устанавливать этот Django SECURE_PROXY_SSL_HEADER? Я не был уверен, учитывая предупреждение, что это потенциально небезопасно.
Да, да.
Однако вам нужно позаботиться о том, что вы делаете. В частности, убедитесь, что он отключил X-Forwarded-Proto снаружи.
Ответ 2
Так как это первый SO пост, который появляется в результатах поиска Google, я думаю, что было бы полезно поделиться, как я решил это. У меня есть вкус Kubernetes, но под капотом логика не так отличается от других.
Я использую входной контроллер Kubernetes, расположенный перед Django, так что все может отличаться от вас, но я кратко изложу свой контекст, чтобы вы могли увидеть, полезен ли он для вас:
Проблема
Таким образом, у меня возникла та же проблема, что и у OP, ссылка разбиения на страницы из DRF всегда http
, даже если я использую конечную точку API с помощью https
. Это станет более понятным, если вы настроили API-интерфейс браузера, который поставляется вместе с Django REST Framework (DRF), и перейдете на корневую страницу API. Я вижу, что все ссылки, сгенерированные DRF, являются http
независимо от того, какой протокол я использую при посещении сайта.
Мой контекст
Стек по заказу из внешнего мира до Джанго:
- Kuberenetes Nginx Ingress Controller (с настройкой SSL с использованием letsencrypt)
- Правило входа указывает на сервис Django
- Gunicorn запускает сервер Django
- Джанго читает
settings.py
и начинает обслуживать запросы
Как я отлаживал и выяснял причину
- Дамп
nginx.conf
входного контроллера. В случае, если вы не знаете, как это сделать: сначала определите имя модуля с помощью kuberctl get pods -n <namespace of your ingress controller>
, запишите это имя, а затем сохраните файл с помощью kubectl exec -it -n ingress_controller_namespace ingress_controller_pod_name cat /etc/nginx/nginx.conf > nginx.conf
.
- Посмотрите на
nginx.conf
, проверьте, есть ли у вас proxy_set_header X-Forwarded-Proto $scheme;
в разделе location
для домена вашего сайта, или похожая вещь, которая правильно устанавливает заголовок прокси X-Forwarded-Proto
. По умолчанию входной контроллер Kubernetes nginx должен уже иметь эту строку (или эквивалентную) для вас.
- Далее запрос направляется в Gunicorn. Одно замечание: вы захотите добавить
--forwarded-allow-ips="*"
к вашей команде gunicorn, или, если вы знаете свой IP-адрес сервера nginx, вы можете ограничиться этим, так что gunicorn будет перенаправлять вам заголовки, в противном случае gunicorn будет снимать эти заголовки, Таким образом, вы получаете команду типа gunicorn django_server.wsgi:application --forwarded-allow-ips="*" --workers=${PROPER_WORKER_NUM} --log-level info --bind 0.0.0.0:8001
. У вас может быть 4 рабочих, указав workers=4
, обычно это зависит от того, какой процессор у вас на сервере. --log-level info
только для отладки, он необязательный.
- В Django вам нужно иметь
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
в settings.py
, о котором упоминал другой ответ. Вы можете удивиться, почему префикс HTTP_
в то время как в nginx наш заголовок называется X-Forwarded-Proto
. Это потому, что WSGI добавит префикс к заголовкам, которые он распознает. Эта строка в основном говорит, что если заголовок HTTP_X_FORWARDED_PROTO
равен строке "https"
, то Django распознает запрос как безопасный. Это повлияет на некоторые виды поведения в Джанго, например, вы получите request.is_secure == True
, request.build_absolute_uri(None) == 'https://...'
и, самое главное, для нумерации страниц Django REST Framework теперь будет использоваться https
! (Пока вы действительно ударили API https
)
Хорошо, теперь вы можете проверить снова. Если DRF дает вам https сейчас - поздравляю. Или, если вы такой же, как я, после попытки выше, все равно не повезло - DRF все еще генерирует чертовы ссылки http
. Я хочу поделиться некоторыми советами, когда я отлаживал как ад:
Распечатайте следующие значения в Django, либо print()
, если у вас есть DEBUG=True
и у вас есть доступ к журналу сервера, либо просто передайте их в контекст шаблона и покажите их на html-странице, если это облегчает вам для тестирования в производственной среде, где включен SSL.
request.is_secure()
: если вы не можете заставить DRF использовать http
, скорее всего, вы получаете False
.
request.META
дает вам все заголовки, которые получил Джанго. Заголовок HTTP_X_FORWARDED_PROTO
появился? Какое значение это?
- Хочу поделиться, что для меня это момент а-ха: значение
HTTP_X_FORWARDED_PROTO
равно http,https
, что говорит мне о том, что оно как-то имеет дублированные значения, и я, вероятно, дважды установил proxy_set_header
, и Бинго! По умолчанию входной контроллер K8 уже имеет эту строку, и, поскольку я снова добавил ее через location-snippet
, теперь у меня всего две строки proxy_set_header X-Forwarded-Proto $scheme;
. После удаления моего фрагмента, DRF показывает https
, и я был так счастлив сделать это. Это было не так просто, потому что я думал, что proxy_set_header
перезапишет и "установит" значение заголовка. Но, похоже, он просто продолжает добавляться.