DEV Community

guzmanojero
guzmanojero

Posted on

From Default to Custom: Handling Errors in Django Like a Pro

Nobody likes the generic browser's error page — the kind that instantly breaks immersion and reminds users they’re looking at an unfinished feature. The good news? Django makes it surprisingly easy to replace those defaults with pages that feel polished and intentional.

This post walks through two practical methods:

  1. Template-only overrides (drop in a file and you’re done)

  2. Fully custom error handlers (your own views, logic, and folder structure)


1. Template‑Only Overrides (the quickest improvement)

Django has built‑in views for common HTTP errors:

  • 404 — Page Not Found
  • 500 — Server Error
  • 403 — Permission Denied
  • 400 — Bad Request

These views are located at: django.views.defaults

When these views run, they automatically try to load templates named exactly:

  • 404.html
  • 500.html
  • 403.html
  • 400.html

So if you create one of these files inside any folder Django can find, Django will render it instead of its plain default.

Example structure:

project_root/
└── templates/
└── 404.html
└── 500.html
Enter fullscreen mode Exit fullscreen mode

No custom views. No URL changes. No special configuration.

You can repeat this for the other codes as well.


2. Fully Custom Error Handlers

If you want:

  • your own views,
  • your own folder like templates/errors/,
  • custom context,
  • or error‑specific logic,

…then you define custom error handlers.

Step 1: Create custom views

Let's create a folder called errors at project level. But you could put this anywhere.

# errors/views.py
from django.shortcuts import render


def error_404(request, exception):
return render(request, "errors/404.html", status=404)


def error_500(request):
return render(request, "errors/500.html", status=500)


def error_403(request, exception):
return render(request, "errors/403.html", status=403)


def error_400(request, exception):
return render(request, "errors/400.html", status=400)
Enter fullscreen mode Exit fullscreen mode

These views will handle each custom error defined.

Step 2: Register the handlers in the project-level urls.py

# project/urls.py
from django.urls import path, include


urlpatterns = [
path("admin/", admin.site.urls),
path("", include("core.urls")),
]


handler404 = "errors.views.error_404"
handler500 = "errors.views.error_500"
handler403 = "errors.views.error_403"
handler400 = "errors.views.error_400"
Enter fullscreen mode Exit fullscreen mode

These variable names (handler404, etc.) are mandatory. Django looks for them.

Step 3: Create your templates

I have set "DIRS": [BASE_DIR / "templates"], so Django will look for a templates folder at the project level.

templates/
└── errors/
├── 400.html
├── 403.html
├── 404.html
└── 500.html
Enter fullscreen mode Exit fullscreen mode

Each template can extend your site layout and include consistent styling.

For example:

{% extends "base.html" %}

{% block content %}

<h1 class="mb-3">Upps... 404 error 🫨</h1>
<div>
    <h3>Sorry, what you're looking is not here</h3>
    <h4>Go <a href="{% url 'home' %}">home</a></h4>
</div>

{% endblock content %}
Enter fullscreen mode Exit fullscreen mode

So this is it.
Now you're ready to handle storms.

Top comments (0)