have nginx proxy properly pass ssl/https to apache mod_wsgi url_scheme for django/pyramid/flask etc ...
Aug
09
1
3
I've had problems getting mod_wsgi to properly set the url_scheme to https when running under SSL. This causes references to fully-qualified urls in statements like redirects and static file locations resolve incorrectly to http when I am viewing the ssl page through https.
I have an nginx web server hosting static files and proxy apache mod_wsgi for the python applications which includes any combination of django, pyramid, and flask frameworks, although this will also work for any mod_wsgi app running under the nginx/apache stack.
I've seen examples of how to set up the apache config, and other examples of how to set up the nginx config, but I have yet to see someone explain how to set up the two together nor has anyone explained what is actually going on.
I am not a server admin so it's a struggle for me to wrangle things like this. I am by no means an expert on this subject but I am documenting my process on what I have found to work in hopes that it'll help others save some pain and frustration that I went through.
Short Version
Add set_proxy_header to the nginx settings after the proxy_pass statement in the server location directive.
proxy_set_header X-Forwarded-Protocol $scheme;
Add the SetEnvIf statement to set the HTTPS environment variable in the apache settings to pass to the wsgi process.
SetEnvIf X-Forwarded-Protocol https HTTPS=1
Long Version
My nginx site config typically looks similar to:
upstream example_backend { server 127.0.0.1:8080; } server { listen 80; server_name example.com; rewrite ^ https://$server_name$request_uri? permanent; } server { listen 443; server_name example.com; ssl on; ssl_certificate /path/to/example_combined.crt; ssl_certificate_key /path/to/example.key; ssl_session_timeout 5m; ssl_protocols SSLv2 SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; ssl_prefer_server_ciphers on; access_log /var/log/nginx/example.access.log; error_log /var/log/nginx/example.error.log; location / { # auth_basic "Restricted"; # auth_basic_user_file /path/to/htpasswd; proxy_pass http://example_backend; include /etc/nginx/proxy.conf; # set a proxy header to pass tell apache if we're operating under ssl proxy_set_header X-Forwarded-Protocol $scheme; # you can use this header instead, or anyone you want, but coordinate it with the apache config # proxy_set_header X-Forwarded-HTTPS on; } location ~ ^/(media|static)/ { root /path/to/html/root; #index index.html index.htm; } }
My /etc/nginx/proxy.conf file contains:
proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 10m; client_body_buffer_size 128k; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_read_timeout 90; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k;
The key entry is the proxy header:
proxy_set_header X-Forwarded-Protocol $scheme;
This sets the X-Forwarded-Protocol header to http or https depending on the scheme. Since you're under SSL it should be set to https. An alternative is you can set a pre-defined header instead like:
proxy_set_header X-Forwarded-HTTPS on;
Choose one or the other, not both. You will check the header value in the apache config and set the HTTPS environment variable to pass to the mod_wsgi process which will set the wsgi.url_scheme variable for the wsgi app.
My apache config typically looks similar to:
WSGIPythonHome /path/to/virtualenv WSGIPythonPath /path/to/virtualenv/lib/python2.7/site-packages <VirtualHost *:8000> ServerAdmin user@example.com ServerName example.com # ServerAlias www.example.com #DocumentRoot /path/to/html WSGIDaemonProcess example user=example group=example processes=4 threads=4 WSGIApplicationGroup %{GLOBAL} WSGIProcessGroup example WSGIPassAuthorization on WSGIScriptAlias / /path/to/production.wsgi # check the proxy header passed to us from nginx to set the HTTPS environment variable SetEnvIf X-Forwarded-Protocol https HTTPS=1 # use this alternative if it was set in nginx # SetEnvIf X-Forwarded-HTTPS on HTTPS=1 # use this if you do not want to check the headers with SetEnfIf (not recommended) # SetEnv HTTPS 1 <Directory /path/to/wsgi/folder> Order deny,allow Allow from all </Directory> # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn ErrorLog /var/log/apache2/example.error.log CustomLog /var/log/apache2/example.access.log combined </VirtualHost>
You need to make sure the setenvif apache mod is enabled (on Ubuntu it is on by default). The key line here is making sure you're setting the HTTPS environment variable:
SetEnvIf X-Forwarded-Protocol https HTTPS=1
If you set X-Forwarded-HTTP on in the nginx as an alternative you should alter the conditional accordingly:
SetEnvIf X-Forwarded-HTTPS on HTTPS=1
Again,you should be using one or the other, not both.
If you want to force HTTPS onto mod-wsgi without checking for the proxy header for testing then you can manually set the variable:
SetEnv HTTPS 1
This is highly not recommended as it's a potential security hole.