Customizing Django 404 and 500 Error Pages

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 Setup

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~=4.2.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~=4.2.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.

Django Welcome Page

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.

Django Debug Page

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.

Django 404 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!

Django Custom 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.

Django 500 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.

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.

Join My Newsletter

Subscribe to get the latest tutorials/writings by email.

    No spam. Unsubscribe at any time.