Handling 404 and Custom Error Pages in Django

Introduction

When building web applications, handling errors gracefully is crucial for providing a good user experience. Users often encounter errors like 404 Not Found, 500 Internal Server Error, or 403 Forbidden, and Django provides mechanisms to handle these errors elegantly.

By default, Django shows plain or technical error pages in production or development. However, it’s best practice to create custom error pages that match the design of your website, provide helpful messages, and route users correctly when they encounter invalid URLs.

This guide explains how to handle errors, create custom error pages, and configure Django to display them in a professional manner.

1. Understanding Common HTTP Error Codes

Web servers return HTTP status codes to indicate the result of a request:

  • 404 Not Found: Requested URL does not exist.
  • 500 Internal Server Error: Unhandled server-side exception.
  • 403 Forbidden: User does not have permission to access a resource.
  • 400 Bad Request: Request could not be understood by the server.

Handling these errors allows developers to provide clear guidance and maintain the site’s usability.


2. Default Django Error Pages

Django provides default error pages for development and production:

  • Development: Displays detailed technical information for debugging (DEBUG = True).
  • Production: Shows simple, generic pages (DEBUG = False).

Default error templates:

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

These templates are rendered when the corresponding error occurs.


3. Creating Custom Error Templates

You can customize error pages by creating templates in your project’s templates directory. For example:

myproject/
templates/
    404.html
    500.html
    403.html
    400.html

Example: Custom 404 Page

<!DOCTYPE html>
<html>
<head>
&lt;title&gt;Page Not Found&lt;/title&gt;
</head> <body>
&lt;h1&gt;404 - Page Not Found&lt;/h1&gt;
&lt;p&gt;Oops! The page you are looking for does not exist.&lt;/p&gt;
&lt;a href="{% url 'home' %}"&gt;Return to Home&lt;/a&gt;
</body> </html>

4. Configuring Django to Use Custom Error Pages

In settings.py, make sure:

DEBUG = False
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'yourdomain.com']

Django only shows custom error pages when DEBUG = False.

You also need to create handlers in urls.py:

# project/urls.py
from django.contrib import admin
from django.urls import path
from myapp import views

handler404 = 'myapp.views.custom_404'
handler500 = 'myapp.views.custom_500'
handler403 = 'myapp.views.custom_403'
handler400 = 'myapp.views.custom_400'

urlpatterns = [
path('admin/', admin.site.urls),
path('', views.home, name='home'),
]

5. Creating Custom Error Views

Define functions in views.py to render your custom templates:

from django.shortcuts import render

def custom_404(request, exception):
return render(request, '404.html', status=404)
def custom_500(request):
return render(request, '500.html', status=500)
def custom_403(request, exception):
return render(request, '403.html', status=403)
def custom_400(request, exception):
return render(request, '400.html', status=400)

6. Handling 404 Errors

404 errors occur when the requested URL does not match any defined URL pattern.

Example 404 Template:

<!DOCTYPE html>
<html>
<head>
&lt;title&gt;404 Not Found&lt;/title&gt;
</head> <body>
&lt;h1&gt;Page Not Found&lt;/h1&gt;
&lt;p&gt;The page you are trying to access does not exist.&lt;/p&gt;
&lt;a href="{% url 'home' %}"&gt;Go Back Home&lt;/a&gt;
</body> </html>

This template can include site navigation, search bars, or helpful links.


7. Handling 500 Errors

500 errors indicate server-side issues, such as unhandled exceptions.

Example 500 Template:

<!DOCTYPE html>
<html>
<head>
&lt;title&gt;Server Error&lt;/title&gt;
</head> <body>
&lt;h1&gt;Oops! Something went wrong.&lt;/h1&gt;
&lt;p&gt;We are experiencing technical difficulties. Please try again later.&lt;/p&gt;
&lt;a href="{% url 'home' %}"&gt;Return to Home&lt;/a&gt;
</body> </html>

8. Handling 403 Errors

403 Forbidden occurs when a user tries to access a restricted resource.

Example 403 Template:

<!DOCTYPE html>
<html>
<head>
&lt;title&gt;Access Denied&lt;/title&gt;
</head> <body>
&lt;h1&gt;403 - Forbidden&lt;/h1&gt;
&lt;p&gt;You do not have permission to view this page.&lt;/p&gt;
&lt;a href="{% url 'home' %}"&gt;Go Home&lt;/a&gt;
</body> </html>

9. Handling 400 Errors

400 Bad Request occurs when the server cannot understand the request.

Example 400 Template:

<!DOCTYPE html>
<html>
<head>
&lt;title&gt;Bad Request&lt;/title&gt;
</head> <body>
&lt;h1&gt;400 - Bad Request&lt;/h1&gt;
&lt;p&gt;The server could not understand your request.&lt;/p&gt;
&lt;a href="{% url 'home' %}"&gt;Return Home&lt;/a&gt;
</body> </html>

10. Testing Custom Error Pages

  1. Set DEBUG = False in settings.py.
  2. Ensure ALLOWED_HOSTS includes your testing domain.
  3. Trigger a 404 error by visiting a non-existent URL.
  4. Trigger a 500 error by raising an exception in a view:
def trigger_error(request):
division_by_zero = 1 / 0

Visit /trigger-error/ to test the 500 page.


11. Adding Search or Navigation on 404 Pages

Custom 404 pages can include:

  • Site navigation menu
  • Search bar to help users find content
  • Links to popular pages

This improves user experience and reduces frustration.


12. Using Templates Across the Site

For consistency, use base templates for error pages:

{% extends 'base.html' %}
{% block content %}
<h1>404 - Page Not Found</h1>
<p>Oops! The page you requested does not exist.</p>
{% endblock %}

This ensures all error pages match the site’s design.


13. Logging Errors

It is important to log errors for debugging and monitoring. Configure logging in settings.py:

LOGGING = {
'version': 1,
'handlers': {
    'file': {
        'level': 'ERROR',
        'class': 'logging.FileHandler',
        'filename': 'errors.log',
    },
},
'loggers': {
    'django': {
        'handlers': &#91;'file'],
        'level': 'ERROR',
        'propagate': True,
    },
},
}

Now, 500 errors and other server errors are logged to errors.log.


14. Redirecting Users on Errors

You can redirect users to specific pages instead of showing an error template:

from django.shortcuts import redirect

def custom_404_redirect(request, exception):
return redirect('home')

This approach is helpful for minor errors or outdated URLs.


15. Using Middleware for Error Handling

Middleware can be used to handle errors globally:

# myapp/middleware.py
from django.shortcuts import render

class HandleExceptionMiddleware:
def __init__(self, get_response):
    self.get_response = get_response
def __call__(self, request):
    response = self.get_response(request)
    return response
def process_exception(self, request, exception):
    return render(request, '500.html', status=500)

Add it to MIDDLEWARE in settings.py.


16. Handling AJAX and API Requests

For AJAX requests, return JSON errors instead of HTML pages:

from django.http import JsonResponse

def custom_404_ajax(request, exception):
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
    return JsonResponse({'error': 'Page not found'}, status=404)
else:
    return render(request, '404.html', status=404)

17. Best Practices for Custom Error Pages

  1. Match site design for consistency.
  2. Provide clear, helpful messages.
  3. Include navigation links or search options.
  4. Log errors for monitoring.
  5. Test custom pages with DEBUG = False.
  6. Handle API or AJAX requests separately.
  7. Avoid revealing sensitive information.
  8. Use base templates to maintain layout consistency.
  9. Handle all common errors: 404, 500, 403, 400.

18. Example: Blog Application

Views (views.py):

from django.shortcuts import render

def blog_home(request):
return render(request, 'blog_home.html')
def custom_404(request, exception):
return render(request, '404.html', status=404)

Project URL Configuration (urls.py):

from django.contrib import admin
from django.urls import path, include
from blog import views

handler404 = 'blog.views.custom_404'

urlpatterns = [
path('admin/', admin.site.urls),
path('', views.blog_home, name='blog-home'),
]

404 Template (404.html):

{% extends 'base.html' %}
{% block content %}
<h1>404 - Page Not Found</h1>
<p>Sorry, we couldn’t find the page you were looking for.</p>
<a href="{% url 'blog-home' %}">Back to Home</a>
{% endblock %}

19. Testing in Production Environment

  • Set DEBUG = False.
  • Add domain names to ALLOWED_HOSTS.
  • Access non-existent URLs to see custom 404.
  • Raise exceptions in views to test 500 pages.
  • Test 403 by restricting access using @login_required or permissions.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *