In Django, what context do the default 404.html and 500.html templates receive, and why is 500.html rendered so differently?
answer
- request_path and exception
- 404 has the request
- 500 gets an empty context
- avoid a second failure
basics
~20 sDjango renders 404.html with request_path and exception plus the request, so context processors run. 500.html gets an empty context and no request, so a broken database or context processor cannot crash the error page itself.
solid answer
~40 sWith `DEBUG = False`, the default handlers in `django.views.defaults` render templates named `404.html` and `500.html`. `page_not_found` passes `request_path` (the quoted path) and `exception` (the `Http404` message if it was a string, otherwise the exception class name), and renders with the request, so context processors add `user`, `messages` and the rest. `server_error` passes **no context and no request**, so `{{ user }}` and anything from context processors is empty; tags that need no context, such as `{% static %}`, still work. The reason is resilience: a 500 often means the database or a context processor just failed, and an error page that needs them would fail too. If a template is missing, Django serves a tiny built-in page. Keep `500.html` static and self-contained.
code
html · 13 lines{# templates/500.html: rendered with an empty context and no request #}
{% load static %}
<!doctype html>
<html lang="en">
<head>
<title>Something went wrong</title>
<link rel="stylesheet" href="{% static 'css/error.css' %}">
</head>
<body>
<h1>Something went wrong on our side</h1>
<p>The team has been notified. Please try again in a few minutes.</p>
</body>
</html>go deeper
Know that you customise error pages by adding 404.html and 500.html to a templates directory, and that you only see them with DEBUG = False.
Explain the 404 context (request_path, exception, request) versus the empty 500 context, and what each means for the template you write.
Design 500.html to survive a dead database or broken context processor, and keep internal detail out of Http404 messages that 404.html may print.
Treat error pages as part of failure design: static, testable fallbacks, owned alongside incident communication, not as a styling afterthought.
## Which views render these templates When `DEBUG = False`, Django turns exceptions into responses through error handlers. The defaults live in `django.views.defaults`, and each renders a template of a fixed name found through the normal template loaders, usually at the root of a project `templates/` directory: | Status | Default view | Template | Context | |---|---|---|---| | 404 | `page_not_found` | `404.html` | `request_path`, `exception`, plus the request | | 500 | `server_error` | `500.html` | nothing: empty context, no request | | 403 | `permission_denied` | `403.html` | `exception` as a string, plus the request | | 400 | `bad_request` | `400.html` | nothing but the request; no exception content, to avoid disclosure | While `DEBUG = True`, the 404 and 500 cases show Django's technical pages instead, so these templates are only seen in production-like runs. Replacing the handler views themselves is a different subject; here the defaults are assumed. ## What `404.html` receives `page_not_found(request, exception)` builds a context with: - **`request_path`**: the requested path, URL-quoted to prevent content injection; - **`exception`**: the first argument of the `Http404` if it is a string (for example the message you passed to `raise Http404("No order 7 for this customer")`), otherwise the exception class name, such as `Resolver404`. It renders the template **with the request**, so context processors run and the page can extend your normal base template, show the logged-in user, link home with `{% url %}` and use the messages framework. Two consequences follow: 1. Whatever message you pass to `Http404` can be printed to the visitor if the template shows `{{ exception }}`. Keep internal detail out of those messages. 2. The 404 page costs whatever your base template costs: queries in context processors run for every missing page, including bot traffic. If `404.html` does not exist, Django returns a built-in page titled 'Not Found'. ## Why `500.html` is different `server_error(request)` calls `template.render()` with **no context and no request**. Django's own documentation gives the reason: the 500 page is rendered with an empty context to lessen the chance of additional errors. Think about what usually causes a 500: - the database connection failed or a query raised; - a context processor or middleware broke; - a template tag or base template raised. An error page that extends the normal base template would hit the same failure while rendering and turn one error into two, leaving Django with nothing to send but its fallback. So the default design removes every dependency it can: - no `request`, so no `user`, no session, no messages; - no context processors, so no `STATIC_URL` or custom variables from them; - only template tags that need no context still work, for example `{% load static %}` with `{% static "css/error.css" %}`. If `500.html` is missing, Django returns a minimal HTML page headed 'Server Error (500)'. ## How to write them well - Make `500.html` **standalone**: its own small HTML, inline or static CSS, no `{% extends %}` of a base that queries or reads the user. - Let `404.html` extend the base template, since the site is healthy and navigation helps the visitor. - Do not print `{{ exception }}` in `404.html` unless every `Http404` message in the code base is safe for users. - Check both with `DEBUG = False` before launch, because debug mode never shows them. - Add `403.html` and `400.html` if the built-in one-liners do not fit the site. ## Checking them before launch Because debug mode hides both templates, test them deliberately: - run the site locally with `DEBUG = False` and a matching `ALLOWED_HOSTS`, then request a missing URL and a view that raises on purpose; - in a test (Django's test runner already runs with `DEBUG = False`), request a missing page and assert the 404 status and your page's content; - stop the database on a staging box and load a page: if `500.html` still renders, it truly has no dependencies. The last check is the one that proves the design. An error page that renders only when everything else works is not an error page.
- Why does {{ user.username }} print nothing in a Django 500.html even though it works in 404.html?The default `server_error` view renders `500.html` without a request, so context processors such as the auth one never run and `user` is not in the context. `page_not_found` renders with the request, so the same variable works there. Django does this on purpose, so the error page does not depend on the database or sessions that may have caused the 500.
- What text does Django put in the exception variable of 404.html?The first argument of the `Http404` if it is a string, for example the message from `raise Http404("No such order")`. Otherwise it is the exception class name, such as `Resolver404` when no URL pattern matched. Autoescaping protects the HTML, but the message itself is shown to visitors if the template prints it.
saying these in an interview costs you the question
- 500.html gets the same context as every other template
- 404.html is used for missing pages while DEBUG is True
- 500.html should extend the site's base template for a consistent look
- A missing 500.html makes Django crash with TemplateDoesNotExist
- Http404 messages are never visible to site visitors