Customizing Django 404 and 500 Error Pages
Updated
Table of Contents
For any web application, users will inevitably navigate to error pages like 404 (Page Not Found) or 500 (Server Error). Django ships with default error pages for both, but if you want a more user-friendly experience and to match your site's theme, it is better to customize these pages. This tutorial will run through a sample Django project to demonstrate the Django default 404 and 500 pages and how to customize them.
Project Set Up
Let's create a new Django project from scratch for demonstration purposes. We'll place the code on the Desktop in a directory called django_error_pages
. Next, we'll create a new virtual environment called .venv
, activate it, install Django, start a new project called django_project
, migrate our database, and finally start the local web server using the runserver
command.
# Windows
$ cd onedrive\desktop\
$ mkdir django_error_pages
$ cd django_error_pages
$ python -m venv .venv
$ .venv\Scripts\Activate.ps1
(.venv) $ python -m pip install django~=5.0.0
(.venv) $ django-admin startproject django_project .
(.venv) $ python manage.py migrate
(.venv) $ python manage.py runserver
# macOS
$ cd ~/desktop/
$ mkdir django_error_pages
$ cd django_error_pages
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $ python3 -m pip install django~=5.0.0
(.venv) $ django-admin startproject django_project .
(.venv) $ python manage.py migrate
(.venv) $ python manage.py runserver
You'll see the Django welcome screen if you navigate to http://127.0.0.1:8000 in your web browser.
Customizing the Default 404 Page
At this point, if you visit any page other than http://127.0.0.1:8000/admin
we'll see an error message.
This detailed message exists because we have DEBUG = True
in our Django settings file. In production, you should have DEBUG = False
for security reasons and must set a value for ALLOWED_HOSTS
. Therefore, to view the default 404 page locally, update both in the settings.py
file.
# django_project/settings.py
DEBUG = False # new
ALLOWED_HOSTS = ["*"] # new
We have set ALLOWED_HOSTS
to the wildcard character, *
, meaning accept all hosts, which is a massive security risk in production but valuable for testing purposes. If you now refresh the web page, we can see Django's production 404 default page.
If you're curious where this code comes from, it is all viewable in the Django source code. As we can see in this file, django/django/views/defaults.py
, Django is looking for a template set by the variable ERROR_404_TEMPLATE_NAME
, which currently is set to 404.html
. To update this, we can create our own 404.html
template, and Django will display it instead.
Create a new directory called templates
in your root project.
(.venv) $ mkdir templates
Then, update the TEMPLATES
setting so Django knows to look within it, too, for templates.
# django_project/settings.py
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / 'templates'] # new
...
Finally, create a new file called templates/404.html
and add the following code:
<!-- templates/404.html -->
This is our new 404 page!
Using template inheritance, you can have this 404 page match the theme of your website with a navbar and footer, a link to contact the administrator if this page should not be appearing, and so on.
Customizing the Default 500 Page
To test a 500 error page locally we need to mimic a production environment, just as we did for the custom 404 page, by setting DEBUG = False
and ALLOWED_HOSTS = ["*"]
. Then, we can raise an exception intentionally in one of our views.
For example, let's write a homepage_view
that deliberately raises an Exception. Then, we'll add it to the homepage view with a new URL path.
add a homepage view directly to our urls.py
file that outputs "Hello, World!" We can do this by importing HttpResponse
at the top, writing a new view called homepage_view
, and adding a path for it at ""
, aka the homepage.
# django_project/urls.py
from django.contrib import admin
from django.urls import path
# new view below
def homepage_view(request):
raise Exception("This is a test error")
urlpatterns = [
path("admin/", admin.site.urls),
path("", homepage_view), # new
]
Navigate in your web browser to http://127.0.0.1:8000/
and the result is the default Django 505 page.
To customize this page, we can create a new templates/500.html
file that will override the default template.
<!-- templates/500.html -->
This is a custom 500 page!
Stop the local server and restart it. Then visit the page at http://127.0.0.1:8000/
; the result is our new custom 500 page.
Next Steps
If you'd like further control over your custom error views, the docs provide examples of how to custom behavior as opposed to just a custom template.
This same process also works for 403 and 400 error codes. You can create a 403.html
and 400.html
template with your new code. Or you can customize the views themselves for greater flexibility.
A warning about testing custom error pages: in this tutorial, we modified both DEBUG
and ALLOWED_HOSTS
. You should always be cautious since misconfiguration of either can have adverse security and performance effects on your web application. Many real-world applications use environment variables to seamlessly toggle between local and production settings.
You also want to avoid the risk that your 500 error page triggers a 500 error and refuses to render. A good rule of advice is to keep it simple: avoid database queries, complex logic, template tags, media files, or anything else that can fail. Ideally, the 500 error page should be a static HTML page.
It is also recommended to add tests for new custom error page functionality.
Conclusion
Creating custom error pages improves your overall user experience, maintains consistent branding, and can be a fun and whimsical place to express your site's brand. Django comes with customizable built-in batteries, making setting up and customizing these pages a relatively straightforward task.