uwsgi_pass does not forward SCRIPT_NAME header

2019-08-16 03:21发布

问题:

I'm trying to make my web app (Django/wsgi-based) available from some subfolder of the main domain. I'm using docker for my app, and static files, so I have main nginx on my server as reverse proxy, another nginx in "nginx" container which routes the stuff for my app and uWSGI in the second container which serves actual Django data

And I want my app to be available externally as myserver.com/mytool, in the same time I do not want to hardcode mytool anywhere in my app. Usually SCRIPT_NAME header is used for this type of stuff, so here is nginx configuration on the host:

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
  }
}

Then in my docker-compose I expose 8000:80 for nginx and here is internal nginx configuration:

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

With this configuration I would expect that my Django app receives SCRIPT_NAME header, but apparently it does not.

In the same time if I define custom headers like proxy_set_header X-something something; then this gets forwarded correctly and I can see it from Django.

How should I pass SCRIPT_NAME to avoid path hardcode in my code?

回答1:

There are two problems here.

First is that nginx considers headers which contain underscores as invalid, so SCRIPT_NAME header is not being accepted by nginx in the container because it's invalid from nginx point of view. Luckily, nginx directive underscores_in_headers is here to help.

Just add underscores_in_headers on; to server section of nginx inside Docker (not to the host one).

When this is done here is yet another issue - nginx forwards header prepending HTTP in front of its name. So now from Django side you will see HTTP_SCRIPT_NAME instead of SCRIPT_NAME. But again, luckily for us, it can be easily fixed by using uwsgi_param SCRIPT_NAME $http_script_name; line in nginx inside Docker again.

Thus, final nginx config inside Docker should look like:

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