reverse, proxy, TLS, SSL, https
Often people will set up "pure Python" web servers behind reverse proxies, especially if they need TLS support (Waitress does not natively support TLS). Even if you don't need TLS support, it's not uncommon to see Waitress and other pure-Python web servers set up to only handle requests behind a reverse proxy; these proxies often have lots of useful deployment knobs.
If you're using Waitress behind a reverse proxy, you'll almost always want your reverse proxy to pass along the Host
header sent by the client to Waitress, in either case, as it will be used by most applications to generate correct URLs. You may also use the proxy headers if passing Host
directly is not possible, or there are multiple proxies involved.
For example, when using nginx as a reverse proxy, you might add the following lines in a location
section.
proxy_set_header Host $host;
The Apache directive named ProxyPreserveHost
does something similar when used as a reverse proxy.
Unfortunately, even if you pass the Host
header, the Host header does not contain enough information to regenerate the original URL sent by the client. For example, if your reverse proxy accepts HTTPS requests (and therefore URLs which start with https://
), the URLs generated by your application when used behind a reverse proxy served by Waitress might inappropriately be http://foo
rather than https://foo
. To fix this, you'll want to change the wsgi.url_scheme
in the WSGI environment before it reaches your application. You can do this in one of three ways:
- You can pass a
url_scheme
configuration variable to thewaitress.serve
function. - You can pass certain well known proxy headers from your proxy server and use waitress's
trusted_proxy
support to automatically configure the WSGI environment.
You can have the Waitress server use the https
url scheme by default.:
from waitress import serve
serve(wsgiapp, listen='0.0.0.0:8080', url_scheme='https')
This works if all URLs generated by your application should use the https
scheme.
If your proxy accepts both HTTP and HTTPS URLs, and you want your application to generate the appropriate url based on the incoming scheme, you'll want to pass waitress X-Forwarded-Proto
, however Waitress is also able to update the environment using X-Forwarded-Proto
, X-Forwarded-For
, X-Forwarded-Host
, and X-Forwarded-Port
:
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Port $server_port;
when using Apache, mod_proxy
automatically forwards the following headers:
X-Forwarded-For
X-Forwarded-Host
X-Forwarded-Server
You will also want to add to Apache:
RequestHeader set X-Forwarded-Proto https
Configure waitress's trusted_proxy_headers
as appropriate:
trusted_proxy_headers = "x-forwarded-for x-forwarded-host x-forwarded-proto x-forwarded-port"
At this point waitress will set up the WSGI environment using the information specified in the trusted proxy headers. This will setup the following variables:
HTTP_HOST
SERVER_NAME
SERVER_PORT
REMOTE_ADDR
REMOTE_PORT (if available)
wsgi.url_scheme
Waitress also has support for the Forwarded (RFC7239) HTTP header which is better defined than the ad-hoc X-Forwarded-*
, however support is not nearly as widespread yet. Forwarded
supports similar functionality as the different individual headers, and is mutually exclusive to using the X-Forwarded-*
headers.
To configure waitress to use the Forwarded
header, set:
trusted_proxy_headers = "forwarded"
Note
You must also configure the Waitress server's trusted_proxy
to contain the IP address of the proxy.
You can have the Waitress server use a particular url prefix by default for all URLs generated by downstream applications that take SCRIPT_NAME
into account.:
from waitress import serve
serve(wsgiapp, listen='0.0.0.0:8080', url_prefix='/foo')
Setting this to any value except the empty string will cause the WSGI SCRIPT_NAME
value to be that value, minus any trailing slashes you add, and it will cause the PATH_INFO
of any request which is prefixed with this value to be stripped of the prefix. This is useful in proxying scenarios where you wish to forward all traffic to a Waitress server but need URLs generated by downstream applications to be prefixed with a particular path segment.