CaptchaBox / Docs / Django
Add a captcha to Django forms
Last updated 6 November 2026 · By Mohammad AlSaraf
You will add a form field, CaptchaBoxField. Put it on any form and form.is_valid() checks the captcha for you.
1. Get your keys
Sign in to the CaptchaBox client area (a free account is enough) and add your site with its domain. You get a site key for the page and a secret for your server. Keep the secret out of your HTML and out of git: put it in an environment variable.
Add them to settings.py (read them from the environment in production):
CAPTCHABOX_SITE_KEY = os.environ["CAPTCHABOX_SITE_KEY"]
CAPTCHABOX_SECRET = os.environ["CAPTCHABOX_SECRET"]
2. The field
Save this as captchabox.py in your app:
"""CaptchaBox for Django: a form field. Copy this file into your app.
settings.py: CAPTCHABOX_SITE_KEY = "..." CAPTCHABOX_SECRET = "..."
forms.py: from .captchabox import CaptchaBoxField
class ContactForm(forms.Form):
email = forms.EmailField()
captcha = CaptchaBoxField()
Test keys that always pass, on any domain: cb_test_pass / cbs_test_pass
"""
import json
import urllib.parse
import urllib.request
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.html import format_html
VERIFY_URL = "https://captcha.natbox.io/v1/siteverify"
def verify_captchabox(token, secret=None):
"""Asks CaptchaBox whether a token is valid. Each token works once, for 5 minutes."""
if not token:
return False
data = urllib.parse.urlencode({"secret": secret or settings.CAPTCHABOX_SECRET, "response": token}).encode()
try:
with urllib.request.urlopen(VERIFY_URL, data, timeout=10) as r:
return json.load(r).get("success") is True
except OSError:
return False
class CaptchaBoxWidget(forms.Widget):
"""Shows the widget. The widget itself adds a hidden captchabox-response field to the form."""
def __init__(self, attrs=None, theme="auto", size="normal"):
super().__init__(attrs)
self.theme, self.size = theme, size
def render(self, name, value, attrs=None, renderer=None):
return format_html(
'<script src="https://captcha.natbox.io/v1/widget.js" async defer></script>'
'<div class="captchabox" data-sitekey="{}" data-theme="{}" data-size="{}"></div>',
settings.CAPTCHABOX_SITE_KEY, self.theme, self.size)
def value_from_datadict(self, data, files, name):
return data.get("captchabox-response", "")
class CaptchaBoxField(forms.Field):
widget = CaptchaBoxWidget
default_error_messages = {"captcha": "The captcha check failed. Please try again."}
def __init__(self, **kwargs):
kwargs.setdefault("label", "")
super().__init__(**kwargs)
def validate(self, value):
if not verify_captchabox(value):
raise ValidationError(self.error_messages["captcha"], code="captcha")
3. Use it in a form
from django import forms
from .captchabox import CaptchaBoxField
class ContactForm(forms.Form):
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
captcha = CaptchaBoxField()
Render the form as usual ({{ form.as_p }} and {% csrf_token %}). The field shows the widget, and is_valid() fails with "The captcha check failed" if the token is missing, forged, used or expired.
It works on ModelForms too, and on the admin login if you subclass AuthenticationForm and add the field.
A complete one-file example
Run it with python example_project.py runserver next to captchabox.py:
"""A one-file Django site that uses captchabox.py, to try it out.
Run: pip install django && python example_project.py runserver 0.0.0.0:8000
"""
import os
import sys
from django.conf import settings
settings.configure(
DEBUG=True,
SECRET_KEY="change-me",
ROOT_URLCONF=__name__,
ALLOWED_HOSTS=["*"],
MIDDLEWARE=["django.middleware.csrf.CsrfViewMiddleware"],
TEMPLATES=[{"BACKEND": "django.template.backends.django.DjangoTemplates"}],
CAPTCHABOX_SITE_KEY=os.environ.get("CAPTCHABOX_SITE_KEY", "cb_test_pass"),
CAPTCHABOX_SECRET=os.environ.get("CAPTCHABOX_SECRET", "cbs_test_pass"),
)
from django import forms # noqa: E402
from django.http import HttpResponse # noqa: E402
from django.middleware.csrf import get_token # noqa: E402
from django.urls import path # noqa: E402
from django.utils.html import format_html # noqa: E402
from captchabox import CaptchaBoxField # noqa: E402
class ContactForm(forms.Form):
email = forms.EmailField()
captcha = CaptchaBoxField()
def contact(request):
form = ContactForm(request.POST or None)
if request.method == "POST" and form.is_valid():
# The visitor passed: send the email, save the message, etc.
return HttpResponse('<p id="result">Thanks, your message was sent.</p>')
return HttpResponse(format_html(
'<!doctype html><meta charset="utf-8"><title>Contact</title>'
'<form method="post"><input type="hidden" name="csrfmiddlewaretoken" value="{}">{}<button>Send</button></form>',
get_token(request), form.as_p()))
urlpatterns = [path("", contact)]
if __name__ == "__main__":
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
Trying it out? The test keys cb_test_pass (site key) and cbs_test_pass (secret) always pass, on any domain including localhost. Swap in your own keys from the client area before going live.
Other platforms: all CaptchaBox docs. Questions? Open a ticket, we reply within 24 hours.