首页 > 解决方案 > uwsgi_pass 不转发 SCRIPT_NAME 标头

问题描述

我正在尝试使我的 Web 应用程序(基于 Django/wsgi)可从主域的某个子文件夹中使用。我在我的应用程序和静态文件中使用 docker,所以我的服务器上有主 nginx 作为反向代理,“nginx”容器中的另一个 nginx 为我的应用程序和 uWSGI 路由内容,第二个容器为实际的 Django 数据提供服务 方案

而且我希望我的应用程序可以在外部使用myserver.com/mytool,同时我不想mytool在我的应用程序的任何地方硬编码。通常SCRIPT_NAMEheader 用于这种类型的东西,所以这里是主机上的 nginx 配置:

server {
  listen 80; # Just for sake of simplicity, of course in production it's 443 with SSL
  location /mytool/ {
    proxy_pass http://127.0.0.1:8000/;
    include proxy_params;
    proxy_set_header SCRIPT_NAME /mytool;  # <--- Here I define my header which backend should use
  }
}

然后在我docker-compose为 nginx 公开 8000:80 ,这是内部 nginx 配置:

server {
  listen 80;
  location / {
    include uwsgi_params;
    uwsgi_pass web:3031;
  }
}

使用此配置,我希望我的 Django 应用程序收到 SCRIPT_NAME 标头,但显然它没有。

同时,如果我定义自定义标头,proxy_set_header X-something something;那么它会正确转发,我可以从 Django 中看到它。

我应该如何通过SCRIPT_NAME以避免代码中的路径硬编码?

标签: djangodockernginxuwsgi

解决方案


这里有两个问题。

首先是 nginx 认为包含下划线的SCRIPT_NAME标头是无效的,因此容器中的 nginx 不接受标头,因为从 nginx 的角度来看它是无效的。幸运的是,nginx 指令underscores_in_headers可以提供帮助。

只需添加underscores_in_headers on;Docker 内server的 nginx 部分(而不是主机)。

当这里完成时,另一个问题是 - nginx 转发HTTP在其名称前面附加的标头。所以现在从 Django 方面你会看到HTTP_SCRIPT_NAME而不是SCRIPT_NAME. uwsgi_param SCRIPT_NAME $http_script_name;但是,对我们来说幸运的是,再次在 Docker 内部的 nginx 中使用 line 可以很容易地修复它。

因此,Docker 中的最终 nginx 配置应如下所示:

server {
  underscores_in_headers on;  # <---- (1)
  listen 80;
  location / {
    include uwsgi_params;
    uwsgi_pass web:3031;
    uwsgi_param SCRIPT_NAME $http_script_name; # <--- (2)        
  }
}

在 Djangosettings.py


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

STATIC_URL = '/static/'
STATIC_ROOT = '/static'

# Bug in Django of not using SCRIPT_NAME header...
# See https://code.djangoproject.com/ticket/25598
# Let's implement dirty workaround for now
if os.getenv('SCRIPT_NAME'):
    STATIC_URL = os.getenv('SCRIPT_NAME') + STATIC_ROOT


推荐阅读