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

.. _using-behind-a-reverse-proxy:

============================
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 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.

.. code-block:: nginx

    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 pass certain well known proxy headers from your proxy server and
    use waitress's ``trusted_proxy`` support to automatically configure the
    WSGI environment.

Using ``url_scheme`` to set ``wsgi.url_scheme``
-----------------------------------------------

You can have the Waitress server use the ``https`` url scheme by default.:

.. code-block:: python

   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 proxy headers to setup the WSGI environment
-------------------------------------------------------

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
<https://tools.ietf.org/html/rfc7239>`_ 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.


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.:

.. code-block:: python

   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.
