带有 Nginx 的 Django 的 build_absolute_uri 中的 localhost

Posted

技术标签:

【中文标题】带有 Nginx 的 Django 的 build_absolute_uri 中的 localhost【英文标题】:localhost in build_absolute_uri for Django with Nginx 【发布时间】:2020-01-22 11:16:16 【问题描述】:

在生产中,我使用链 Django - UWSGI - Docker - Nxing。 UWSGI 使用 50012 端口,Ngxin 配置为:

proxy_pass http://localhost:50012;

Django进程认为它的宿主是localhost:50012而不是nginx监听的域。因此,当调用函数 build_absolute_uri 时,会出现 localhost:50012 而不是我的域。有没有办法让 Django 在调用build_absolute_uri 时使用自定义主机名?

注意:在某些库中 build_absolute_uri 被隐式调用(例如 social-django 或示例),因此在我的情况下避免使用此函数不是解决方案。

【问题讨论】:

我猜你想看看sites framework。你确定this question 的答案都不适用于这里吗? @PauloScardine 它应该如何提供帮助?在文档中,据说应该在多个域的情况下使用这个框架,并将对象与每个域关联起来。就我而言,我不关心任何对象,我只有一个域。 该页面上特别是"Getting the current domain for full URLs"。 @PauloScardine 我认为这与我所问的无关...... 在设置中设置 USE_X_FORWARDED_HOST = True 并配置 nginx 以传递标头。否则 django 将使用request.META['HTTP_HOST']request.META['SERVER_NAME']。设置的 nginx 部分可能最适合网络中的另一个站点,因为它更多的是关于基础设施管理而不是编程。 【参考方案1】:

问题

当您用于访问代理的公共主机名与应用程序服务器的内部主机名不同时,Django 无法知道原始请求中使用了哪个主机名,除非代理正在传递此信息。

可能的解决方案

1) 设置代理传递原主机

来自MDN:

X-Forwarded-Host (XFH) 标头是事实上的标准标头,用于在 Host HTTP 请求标头中标识客户端请求的原始主机。

反向代理(负载平衡器、CDN)的主机名和端口可能与处理请求的源服务器不同,在这种情况下,X-Forwarded-Host 标头有助于确定最初使用的是哪个主机。

你应该做两件事:

    确保 Django 前面的所有代理都通过 X-Forwarded-Host 标头 在设置中开启USE_X_FORWARDED_HOST 如果内部和外部方案也不同,请将SECURE_PROXY_SSL_HEADER设置为有意义的值并设置服务器发送相应的标头

settings.py 中的USE_X_FORWARDED_HOST 设置为True 时,HttpRequest.build_absolute_uri 使用X-Forwarded-Host 标头而不是request.META['HTTP_HOST']request.META['SERVER_NAME']

我不会过多地研究代理设置部分(因为它与专业网络管理有关,而不是与本网站范围内的编程有关),但对于 nginx,它应该是这样的:

location / 
    ...
    proxy_set_header X-Forwarded-Host $host:$server_port;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    ...
    proxy_pass http://upstream:port;
    

可能是最好的解决方案,因为它是完全动态的,如果将来公共方案/主机名发生变化,您无需更改任何内容。

如果内部和外部方案也不同,您可能希望将settings.py 中的SECURE_PROXY_SSL_HEADER 设置为如下所示:

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

然后将以下内容添加到服务器配置中:

proxy_set_header X-Forwarded-Proto https;

2) 公共服务器和私有服务器使用相同的主机名

假设您的公共主机名是“host.example.com”:您可以在/etc/hosts 中添加这样的一行(在Windows 上为%windir%\System32\drivers\etc\hosts):

127.0.0.1    host.example.com

现在您可以在 nginx 配置中使用主机名:

proxy_pass http://host.example.com:port;

当内部和外部方案也不同时(外部 https、内部 http),您可能需要按照第一个解决方案中的说明设置 SECURE_PROXY_SSL_HEADER

每次公共主机名更改时,您都必须更新配置,但我想这对于小型项目来说是可以的。

【讨论】:

谢谢,这对我来说效果很好!如果需要,我会在您的答案中添加的一项可选内容是X-Forwarded-Proto: https 当内部和外部方案也不同时,使用推荐的SECURE_PROXY_SSL_HEADER 设置更新答案。【参考方案2】:

我的工作是使用proxy_redirect

假设您有一个名为appcontainerupstream,并且您希望它返回127.0.0.1 作为主机,那么您的配置应包括:

server 
  listen 80;

  location / 
    proxy_pass http://app:8000;

    proxy_redirect http://app:8000 http://127.0.0.1:8000;
  

这是我的最终配置:

server 
  listen 80;

  location / 
    proxy_pass http://app:8000;

    proxy_set_header X-Forwarded-Host $host:$server_port;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_redirect http://app:8000 http://127.0.0.1:8000;
  

也可以查看这篇文章以获得详细解释https://mattsegal.dev/nginx-django-reverse-proxy-config.html

【讨论】:

【参考方案3】:

我遇到了与问题相同的问题,我在没有 docker 的情况下在生产环境中使用了 nginx、gunicorn 和 django。

get_current_site(request).domain

返回 localhost 所以我在 drf yasg 基本 url 中有一些问题。我只是通过添加解决了它

include proxy_params;

到 nginx 配置。

【讨论】:

以上是关于带有 Nginx 的 Django 的 build_absolute_uri 中的 localhost的主要内容,如果未能解决你的问题,请参考以下文章

Nginx、uwsgi、django、ubuntu 16 带有静态文件的问题

带有 Nginx 和 uWSGI 的 Django CDN

带有 nginx 和 unicorn 的 Django 静态文件

Amazon EC2 上带有 Nginx + uWSGI 的 Django 应用程序

docker容器中的django + nginx:无法上传任何带有表单的文件

带有 ingress-nginx 的 kubernetes 中的 Django 不提供静态文件