..index:: reverse, proxy, TLS, SSL, https

Using Behind a Reverse Proxy

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 "live" 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.

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:

  1. You can pass a url_scheme configuration variable to the waitress.serve function.
  2. You can configure the proxy reverse server to pass a header, X_FORWARDED_PROTO, whose value will be set for that request as the wsgi.url_scheme environment value. Note that you must also conigure waitress.serve by passing the IP address of that proxy as its trusted_proxy.
  3. You can use Paste's PrefixMiddleware in conjunction with configuration settings on the reverse proxy server.

Using url_scheme to set wsgi.url_scheme

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.

Passing the X_FORWARDED_PROTO header to set wsgi.url_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, also set up your proxy to send a X-Forwarded-Proto with the original URL scheme along with each proxied request. For example, when using nginx:

proxy_set_header        X-Forwarded-Proto $scheme;

or via Apache:

RequestHeader set X-Forwarded-Proto https

Note

You must also configure the Waitress server's trusted_proxy to contain the IP address of the proxy in order for this header to override the default URL scheme.

Using url_prefix to influence SCRIPT_NAME and PATH_INFO

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.

Using Paste's PrefixMiddleware to set wsgi.url_scheme

If only some of the URLs generated by your application should use the https scheme (and some should use http), you'll need to use Paste's PrefixMiddleware as well as change some configuration settings on your proxy. To use PrefixMiddleware, wrap your application before serving it using Waitress:

from waitress import serve
from paste.deploy.config import PrefixMiddleware
app = PrefixMiddleware(app)
serve(app)

Once you wrap your application in the the PrefixMiddleware, the middleware will notice certain headers sent from your proxy and will change the wsgi.url_scheme and possibly other WSGI environment variables appropriately.

Once your application is wrapped by the prefix middleware, you should instruct your proxy server to send along the original Host header from the client to your Waitress server, as well as sending along a X-Forwarded-Proto header with the appropriate value for wsgi.url_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, also set up your proxy to send a X-Forwarded-Proto with the original URL scheme along with each proxied request. For example, when using nginx.

proxy_set_header        X-Forwarded-Proto $scheme;

It's permitted to set an X-Forwarded-For header too; the PrefixMiddleware uses this to adjust other environment variables (you'll have to read its docs to find out which ones, I don't know what they are). For the X-Forwarded-For header.

proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

Note that you can wrap your application in the PrefixMiddleware declaratively in a PasteDeploy configuration file too, if your web framework uses PasteDeploy-style configuration:

[app:myapp]
use = egg:mypackage#myapp

[filter:paste_prefix]
use = egg:PasteDeploy#prefix

[pipeline:main]
pipeline =
    paste_prefix
    myapp

[server:main]
use = egg:waitress#main
listen = 127.0.0.1:8080

Note that you can also set PATH_INFO and SCRIPT_NAME using PrefixMiddleware too (its original purpose, really) instead of using Waitress' url_prefix adjustment. See the PasteDeploy docs for more information.