Show a Custom 403 Forbidden Error Page in Django

error_403Creating a custom 404 Page Not Found error page is so easy in Django (all you do is put your own template named “404.html” at the root of your templates directory) that I naturally assumed doing the same for a 403 Forbidden error page would be just as easy. Unfortunately it is not.

After searching around for quite a while last night, I found bits and pieces that I have modified slightly and republished below in a unambiguous step-by-step tutorial (see the “Source and Other Resources” section at the end of the post for a few of the source posts).

The method posted below leverages some custom Django middleware code. Please, if you have a better, more elegant solution I’d love to hear about it in the comments.

Create the Middleware

    1. Create a directory at the root of your project called “middleware”
    2. Add a file named “__init__.py” to this directory
    3. Create a file named “http.py” in this directory with the following contents:
from django.conf import settings
from django.http import HttpResponseForbidden
from django.template import RequestContext,Template,loader,TemplateDoesNotExist
from django.utils.importlib import import_module

"""
# Middleware to allow the display of a 403.html template when a
# 403 error is raised.
"""

class Http403(Exception):
    pass

class Http403Middleware(object):
    def process_exception(self, request, exception):
        from http import Http403

        if not isinstance(exception, Http403):
            # Return None so django doesn't re-raise the exception
            return None

        try:
            # Handle import error but allow any type error from view
            callback = getattr(import_module(settings.ROOT_URLCONF),'handler403')
            return callback(request,exception)
        except (ImportError,AttributeError):
            # Try to get a 403 template
            try:
                # First look for a user-defined template named "403.html"
                t = loader.get_template('403.html')
            except TemplateDoesNotExist:
                # If a template doesn't exist in the projct, use the following hardcoded template
                t = Template("""{% load i18n %}
                 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
                        "http://www.w3.org/TR/html4/strict.dtd">
                 <html>
                 <head>
                     <title>{% trans "403 ERROR: Access denied" %}</title>
                 </head>
                 <body>
                     <h1>{% trans "Access Denied (403)" %}</h1>
                     {% trans "We're sorry, but you are not authorized to view this page." %}
                 </body>
                 </html>""")

            # Now use context and render template
            c = RequestContext(request, {
                  'message': exception.message
             })

            return HttpResponseForbidden(t.render(c))
  1. You should now have this:
    • /myproject/middleware/
      • __init__.py
      • http.py

Modify Your Project’s “settings.py”

  1. Add ” ‘myproject.middleware.http.Http403Middleware’, ” to your MIDDLEWARE_CLASSES

Create a Custom “403.html” page

  1. Put it at the root of your template directory
  2. Sample content: (note: assumes you’ve already defined a “base.html” template)
{% extends "base.html" %}
{% block title %} | Access Denied{% endblock %}

{% block content %}
<h1>Access Denied</h1>
<span>We're sorry, but you are not authorized to view this page (Error: 403)</span>
{% endblock content %}

Raise a 403 Error

    1. In the file where you want to raise the 403 add this at the top: (I used it in my project’s “view.py” file)
from myproject.middleware.http import Http403
    1. Raise the 403
if request.user.id != object.user.id:
    raise Http403

That’s it, you now have a Django template to handle 403 Forbidden errors. I’m sure there’s a way for your front-end, production web server to do the same, but I haven’t explored that yet.

Source and Other Resources

  1. A middleware solution is here (HT Felipe ‘chronos’ Prenholato)
  2. A middleware solution is here (HT Glen Zangirolami)
  3. A potential decorator solution is here (HT Magus)