uwsgi_pass不转发SCRIPT_NAMEheader

uwsgi_pass does not forward SCRIPT_NAME header

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

而且我希望我的应用程序可以作为 myserver.com/mytool 在外部使用,同时我不想在我的应用程序中的任何地方进行硬编码 mytool。通常 SCRIPT_NAME header 用于这种类型的东西,所以这里是主机上的 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 header,但显然它没有收到。

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

我应该如何传递 SCRIPT_NAME 以避免在我的代码中进行路径硬编码?

这里有两个问题。

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

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

这里完成后还有另一个问题 - nginx 转发 header 在其名称前面添加 HTTP 前缀。所以现在从 Django 端你会看到 HTTP_SCRIPT_NAME 而不是 SCRIPT_NAME。但同样,对我们来说幸运的是,再次使用 Docker 内的 nginx 中的 uwsgi_param SCRIPT_NAME $http_script_name; 行可以轻松修复它。

因此,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)        
  }
}

在 Django 中 settings.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