Configuring trusted hosts

Whenever Expanse needs to generate absolute URLs, it uses the Host header from the incoming request. However, this header can be easily manipulated by an attacker by relying on inconsistencies in how different software (web servers, reverse proxies, web frameworks, etc.) handle it.

You can learn more about Host header attacks by reading this article.

To mitigate this risk, Expanse allows you to define a list of hosts that your application will trust. Everytime Expanse needs to use the Host header, it will check the host against this list and raise a SuspiciousOperationError if it's not on the list.

You can configure the trusted hosts by setting the HTTP_TRUSTED_HOSTS environment variable in your .env file. This variable should contain a comma-separated list of hosts that your application will trust.

HTTP_TRUSTED_HOSTS=example.com,www.example.com

You can trust a root domain and all its subdomains by prefixing the domain with a dot (.):

HTTP_TRUSTED_HOSTS=.example.com

If, instead, you want to trust all hosts (not recommended), you can set the variable to an asterisk (*):

HTTP_TRUSTED_HOSTS=*

During development, Expanse will automatically trust the localhost, 127.0.0.1 and ::1 hosts, so you don't need to add them to the HTTP_TRUSTED_HOSTS setting.

Trusting hosts manually

If you need an advanced configuration, or a custom logic to determine which hosts should be trusted, you should first remove the TrustHosts middleware from your middleware stack:

from expanse.core.http.middleware.middleware_stack import MiddlewareStack
from expanse.http.middleware.trust_host import TrustHosts


async def configure_middleware(stack: MiddlewareStack) -> None:
    stack.remove(TrustHosts)

Then, you can create your own middleware to implement the logic you need:

class CustomTrustHosts:
    async def handle(self, request: Request, call_next: RequestHandler) -> Response:
        trusted_host = ...  # Your custom logic to get the trusted hosts

        request.set_trusted_hosts(trusted_host)

        response = await call_next(request)

        return response

Finally, add your custom middleware to the middleware stack:

from expanse.core.http.middleware.middleware_stack import MiddlewareStack


async def configure_middleware(stack: MiddlewareStack) -> None:
    stack.remove(TrustHosts).add(CustomTrustHosts)