使用Nginx的Django中build_absolute_uri中的本地主机

时间:2019-09-21 22:26:05

标签: django docker nginx

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

proxy_pass http://localhost:50012;

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

通知:在某些隐式调用的库build_absolute_uri中(例如social-django或示例),因此就我而言,避免使用此功能不是解决方案。

3 个答案:

答案 0 :(得分:1)

问题

当您用来访问代理服务器的公共主机名与应用程序服务器的内部主机名不同时,除非代理将这些信息一起传递,否则Django无法知道原始请求中使用了哪个主机名。

可能的解决方案

1)设置代理以传递原始主机

来自MDN

  

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

     

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

您应该做两件事:

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

USE_X_FORWARDED_HOST中将True设置为settings.py时,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;
}    

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

如果内部和外部方案也有所不同,则可能需要将SECURE_PROXY_SSL_HEADER中的settings.py设置为以下形式:

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

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

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

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

答案 1 :(得分:1)

我使用 proxy_redirect

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

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

答案 2 :(得分:0)

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

get_current_site(request).domain

返回了 localhost 所以我在 drf yasg base url 中有一些问题。简单地我通过添加解决它

include proxy_params;

到 nginx 配置。