Opened 6 weeks ago

Closed 6 weeks ago

Last modified 4 weeks ago

#36514 closed New feature (wontfix)

Improve ALLOWED_HOSTS error message: show both values

Reported by: Klaas van Schelven Owned by:
Component: HTTP handling Version: 5.2
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When Django rejects a request because the Host header doesn't match ALLOWED_HOSTS, the resulting error message shows
only the incoming header value:

Invalid HTTP_HOST header: 'localhost:8000'. You may need to add 'localhost' to ALLOWED_HOSTS.

This makes it harder to determine whether the problem is a misconfigured proxy (not setting the correct Host) or simply
a missing hostname in ALLOWED_HOSTS. In many cases, the advice to "add localhost" is actively unhelpful: production
deployments rarely serve requests at localhost, and the real fix is usually to configure the proxy correctly.

I’ve implemented a more helpful version of this logic in Bugsink (a Django app installed by users who may not be Django
developers). I describe that implementation in more detail in [this blog post about improving Django’s ALLOWED_HOSTS error messages](https://www.bugsink.com/blog/better-allowed-hosts/#better-error-messages).

In the "bugsink approach" the error:

  • shows the actual Host header and the configured ALLOWED_HOSTS
  • suggests the likely cause
  • avoids pointing users towards "add localhost" as the default fix

Some of what I did in Bugsink likely goes beyond what Django should adopt by default (e.g. showing the error in the
browser), but the idea of displaying both values (the incoming host and the configured list) would be a broadly useful
change on its own.

If desired, the message could also give more specific advice depending on the values (e.g. distinguish between loopback
and public domains), but that can be considered separately.

Security considerations

Showing both the incoming Host header and the configured ALLOWED_HOSTS reveals limited information:

  • The Host header is attacker-controlled — the client already knows what they sent.
  • _or_ The Host header is simply a misconfiguration (in which case it's on-broken-deploy only)
  • ALLOWED_HOSTS reveals the expected hostname(s). In most deployments, this is already visible in the TLS certificate.

Change History (5)

comment:1 by Natalia Bidart, 6 weeks ago

Component: UncategorizedHTTP handling
Resolution: wontfix
Status: newclosed
Type: UncategorizedNew feature

Hello Klaas van Schelven, thank you for taking the time to create this ticket and for the detailed write-up.

I appreciate the motivation to improve the clarity of this error message, I have fought with that error myself. That said, I believe the proposed change introduces security concerns with limited practical benefit. In particular, echoing the full ALLOWED_HOSTS list in the error message could unintentionally disclose internal configuration details. While it's true that many ALLOWED_HOSTS entries are public, that's not guaranteed. In real world deployments, it's common to include internal hostnames, IP addresses, or ephemeral domains that are not externally visible, for example in environments where SSL termination and routing are handled separately from the Django app itself. In such cases, ALLOWED_HOSTS reflects internal routing constraints rather than externally resolvable hostnames.

Given the above, I'll close this ticket as wontfix. If you disagree, and considering this a new feature request for Django, the feature idea should first be proposed and discussed with the community. To do that, please raise this on the new feature tracker.

comment:2 by Klaas van Schelven, 6 weeks ago

In real world deployments, it's common to include internal hostnames, IP addresses, or ephemeral domains that are not externally visible, for example in environments where SSL termination and routing are handled separately from the Django app itself.

That's a fair point for the "this isn't a good fit for Django" case.

I'd argue that the following point remains: for the most common misconfiguration at the proxy level, Django now explicitly suggests to add localhost to ALLOWED_HOSTS. This is generally exactly the opposite of what you want. Could we say _that_ is a bug?

Last edited 6 weeks ago by Klaas van Schelven (previous) (diff)

comment:3 by Klaas van Schelven, 6 weeks ago

this is a pretty good example of the danger I'm talking about with 40 upvotes on Stackoverflow:

literally as the error suggested! go ahead and add the line
0.0.0.0 to the ALLOWED_HOSTS in your settings.py

comment:4 by Klaas van Schelven, 6 weeks ago

echoing the full ALLOWED_HOSTS list in the error message

One more thing: the linked article combines the ideas of showing the DisallowedHost message in the browser with including ALLOWED_HOSTS in the output.

Could it not be argued that the latter is OK (even in the general case) provided the former isn't implemented?

comment:5 by Carlton Gibson, 4 weeks ago

The localhost suggestion is taking the aberrant case as normal. Standardly the Host header is the domain of the site being requested, so something like djangoproject.com, rather than localhost.

In that case, the error message makes perfect sense:

Invalid HTTP_HOST header: 'djangoproject.com'. You may need to add 'djangoproject.com' to ALLOWED_HOSTS.

If you really want to make requests to localhost, then absolutely it should be added to ALLOWED_HOSTS.

I don't think we can account for every possible misunderstanding here.

To the original point: the contents of ALLOWED_HOSTS should not be part of the error message.

Note: See TracTickets for help on using tickets.
Back to Top