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
我正在尝试让我的网络应用程序 (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