Django Page Not Found (404): Complete Guide to Understanding and Handling 404 Errors
Last updated 1 month, 2 weeks ago | 132 views 75 5

In web development, a 404 error means the server couldn’t find what was requested. Django, like most web frameworks, handles this scenario with a "Page Not Found" response. Understanding how Django manages 404 errors — and how you can customize them — is key to creating a polished and user-friendly application.
What is a 404 Error?
A 404 (Not Found) HTTP response means:
"The page you’re trying to access does not exist on the server."
This could happen because:
-
A user visits a non-existent URL
-
A link is broken or mistyped
-
A resource (like a blog post or user) was deleted or never existed
⚙️ How Django Handles 404 by Default
By default, Django returns a plain and simple HTML page when a 404 error occurs. The view function doesn't need to explicitly raise it unless you're manually checking conditions.
Example: Auto-generated 404
# No matching URL pattern for this path
http://localhost:8000/does-not-exist/
If the URL doesn’t match any pattern in urls.py
, Django automatically raises a Http404
and shows a default page.
Manually Raising 404 in Views
Sometimes you want to raise a 404 manually if a specific object doesn’t exist. Django makes this easy:
Using get_object_or_404
(Recommended)
from django.shortcuts import get_object_or_404
from .models import Article
def article_detail(request, slug):
article = get_object_or_404(Article, slug=slug)
return render(request, 'article_detail.html', {'article': article})
Manually Raising Http404
from django.http import Http404
def article_detail(request, slug):
try:
article = Article.objects.get(slug=slug)
except Article.DoesNotExist:
raise Http404("Article not found")
return render(request, 'article_detail.html', {'article': article})
Customizing the 404 Page
You can create a custom 404.html to show a styled error page instead of Django’s default.
Step-by-Step:
1. Create 404.html
in your main template directory:
templates/404.html
:
<!DOCTYPE html>
<html>
<head>
<title>Page Not Found - 404</title>
</head>
<body>
<h1>Oops! Page not found (404)</h1>
<p>The page you were looking for doesn’t exist or has been moved.</p>
<a href="/">Go to Homepage</a>
</body>
</html>
2. Ensure DEBUG = False
and ALLOWED_HOSTS
is set properly in settings.py
:
DEBUG = False
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'yourdomain.com']
⚠️ Custom error pages only appear when
DEBUG = False
. Otherwise, Django will show a detailed traceback (for developers).
3. Run with production settings
To test:
python manage.py runserver --insecure
Then visit a non-existent URL to see your custom 404 page.
Best Practices for 404 Pages
-
✅ Add navigation links back to home or help pages
-
✅ Use branding consistent with your site
-
✅ Explain possible reasons (broken link, moved page)
-
✅ Log errors for analysis (especially if 404s are frequent)
Debugging and Fixing Common 404 Errors
Scenario | Cause | Fix |
---|---|---|
URL not found | No matching pattern in urls.py |
Check and update URL patterns |
Mistyped slug or ID | Invalid path or query | Use get_object_or_404() |
Static files 404 | Static files not found in production | Ensure collectstatic is run and paths are correct |
DEBUG = True shows Django error page | That’s expected | Set DEBUG = False to test custom 404 page |
Wrong ALLOWED_HOSTS |
404 or 400 errors | Set ALLOWED_HOSTS in production |
Example: Complete Custom 404 Setup
settings.py
DEBUG = False
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
templates/404.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404 Not Found</title>
<link rel="stylesheet" href="{% static 'css/error.css' %}">
</head>
<body>
<div class="error-page">
<h1>404</h1>
<p>Sorry, we couldn’t find that page.</p>
<a href="/">Return to homepage</a>
</div>
</body>
</html>
urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('myapp.urls')),
]
Extra: Logging 404 Errors
To track 404 errors in production, you can enable logging:
# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'WARNING',
'class': 'logging.FileHandler',
'filename': 'django.log',
},
},
'loggers': {
'django.request': {
'handlers': ['file'],
'level': 'WARNING',
'propagate': True,
},
},
}
Summary
Feature | Description |
---|---|
404 error | Raised when a page or object isn’t found |
Default behavior | Django returns a generic 404 page |
Custom page | Add 404.html in your template directory |
Manual 404 | Use get_object_or_404() or raise Http404() |
Production display | Set DEBUG = False and configure ALLOWED_HOSTS |
❓ Bonus: Want Custom 403 or 500 Pages?
You can also create:
-
403.html
for forbidden access -
500.html
for server errors
Same rules apply: DEBUG = False
and place them in the root templates folder.