Personal Catalogue: Django & PostgreSQL — Chapter 9, Exercise 3 ==================================================== TASK Set DEBUG=False and a deliberately incomplete ALLOWED_HOSTS in a local test setup, then explain the real error Django returns when visiting the site through a hostname not included in that list. SOLUTION 1. In .env, set: DJANGO_DEBUG=False DJANGO_ALLOWED_HOSTS=example.com (deliberately not including 127.0.0.1 or localhost, the addresses the local dev server is actually reached through) 2. Run python manage.py runserver and visit the site at http://127.0.0.1:8000/. EXPECTED RESULT Django returns a real HTTP 400 Bad Request response. With DEBUG=False, the response body is deliberately minimal - just "Bad Request (400)" - rather than a detailed traceback. If DEBUG were temporarily set back to True to inspect the underlying cause, Django's own debug page would show a real, specific error: DisallowedHost at / Invalid HTTP_HOST header: '127.0.0.1:8000'. You may need to add '127.0.0.1' to ALLOWED_HOSTS. WHY THIS HAPPENS Django checks every incoming request's Host header against the ALLOWED_HOSTS list before doing anything else with the request. This check exists specifically to prevent a real class of attack (HTTP Host header poisoning) where a malicious or spoofed Host header could be used to trick a misconfigured application into generating incorrect links, cache entries, or password-reset URLs pointing at an attacker-controlled domain. Since '127.0.0.1' genuinely isn't in the configured ALLOWED_HOSTS list (only 'example.com' is), Django correctly refuses to process the request at all, rather than assuming it's safe. WHY THIS BEHAVIOR ONLY MATTERS WITH DEBUG=False With DEBUG=True, Django is more lenient about ALLOWED_HOSTS specifically to make local development easier - but that leniency is explicitly a development convenience, not a production behavior, which is why this real, strict enforcement only becomes fully visible (and fully important) once DEBUG is correctly set to False. ANSWER: With DEBUG=False and a hostname not present in ALLOWED_HOSTS, Django returns a real, minimal HTTP 400 Bad Request response, with the underlying cause being a DisallowedHost exception - a deliberate security check preventing HTTP Host header attacks, which only fully manifests in production mode since DEBUG=True relaxes the same check for local development convenience. WHY THIS WORKS AS AN ANSWER ---------------------------- It reports the actual real error (DisallowedHost, HTTP 400) rather than guessing at generic behavior, and explains both what the check protects against and why it behaves differently between DEBUG=True and DEBUG=False.