Creating 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
- Create a directory at the root of your project called “middleware”
- Add a file named “__init__.py” to this directory
- Create a file named “http.py” in this directory with the following contents:
- You should now have this:
- /myproject/middleware/
- __init__.py
- http.py
- /myproject/middleware/
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))
Modify Your Project’s “settings.py”
- Add ” ‘myproject.middleware.http.Http403Middleware’, ” to your MIDDLEWARE_CLASSES
Create a Custom “403.html” page
- Put it at the root of your template directory
- 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
- 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)
- Raise the 403
from myproject.middleware.http import Http403
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
- A middleware solution is here (HT Felipe ‘chronos’ Prenholato)
- A middleware solution is here (HT Glen Zangirolami)
- A potential decorator solution is here (HT Magus)
Related posts:
- Specify a custom manager for the Django admin interface
- Getting Started with South (Django Database Migrations)
- Getting Started with virtualenv (Isolated Python Environments)
- Errata: Practical Django Projects 2nd Edition (PDF)
- Aptana Studio and “Undefined variable from import: DoesNotExist”

8 Responses to “Show a Custom 403 Forbidden Error Page in Django”
Hmm, your code (at least middleware) is very similar to one that I wrote a half year ago and can be found at http://chronosbox.org/blog/manipulando-erros-http-403-permissao-negada-no-django?lang=en
AFAIK have some discussion on django-dev about add this error in http core
Thanks Felipe, you post was definitely one of the key resource I found to help me solve this problem. Here’s hoping that simpler 403 handling gets added to core.
I like how you added TemplateDoesNotExist. Thanks for the reference!
Thanks for this I found it very useful.
I had to change:
‘message’: exception.message
to:
‘message’: ‘%s’ % exception
otherwise I got an exception “message is not an attribute…”
Cheers,
Alan
Thanks for the correction, Alan.
Thanks Mitch! Good stuff
Hey Mark! Thanks for stopping by, hope all is well.
That’s great, thanks.
If you want to define the 403 handler as a string in urls.py (like it’s done with 404), replace the following line:
callback = getattr(import_module(settings.ROOT_URLCONF),’handler403′)
by the followin one:
name = getattr(import_module(settings.ROOT_URLCONF),’handler403′)
hierarchy = name.split(‘.’)
callback = __import__(hierarchy[0])
for name in hierarchy[1:]:
callback = getattr(callback, name)