Skip to content
Myra EU CAPTCHA Online Help Updated · 25 Aug 2026

HTML and Django

This example protects the contact form of an application that already exists and has no frontend framework. The page is delivered as a Django template. The form sends a usual POST. The widget makes the hidden eu-captcha-response field itself, which goes with the form.

Note

In a single-page application, you accept the token with a callback and send it as JSON instead. See React and PHP.

Requirements

The following requirements must be met:

Requirement Value
Django project in operation and delivers the page
Python Version 3 with pip
Sitekey Public sitekey and secret from the Details view

Warning

The public sitekey belongs in the page, the secret only on the server. Each visitor can read a secret that is in a template.

Project structure

These files change. All other parts of the project stay unchanged:

my-project/                       # existing Django project
├── manage.py
├── templates/
│   └── contact.html              # ← Steps 1 and 2: embed the script and the widget
└── server/
    ├── settings.py               # ← Step 3: enter the app and the credentials
    ├── urls.py                   # ← Step 3: register the /contact/ route
    └── views.py                  # ← Step 3: put the verification at the start of the view

Step 1: Embed the widget

The page is in the templates/contact.html file.

Load the verify.js script from the Myra CDN and put the widget element in the form:

<script src="https://cdn.eu-captcha.eu/verify.js" async defer></script>

<form action="/contact/" method="post">
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>

  <div class="eu-captcha" data-sitekey="EUCAPTCHA_SITE_KEY"></div>

  <button type="submit">Submit</button>
</form>

The script makes the hidden iframe, starts the challenge automatically, and puts the hidden eu-captcha-response field in the form. The field goes with the transmission.

Step 2: Release the transmission

If necessary, disable the button and release it only after the challenge. To do this, read the euCaptchaCompleted window message. Examine the origin of the message, because each script and each extension in the browser can send messages:

<script>
  const CAPTCHA_ORIGIN = "https://cdn.eu-captcha.eu";
  const btn = document.querySelector('button[type="submit"]');
  btn.disabled = true;

  window.addEventListener("message", (msg) => {
    if (msg.origin !== CAPTCHA_ORIGIN) return;
    const data = msg.data ?? {};
    if (data.type === "euCaptchaCompleted") {
      btn.disabled = false;
    }
  });
</script>

Warning

The disabled button in the browser controls only the operation. The verification of the token on the server is always necessary.

Step 3: Verify the token

The verification belongs in the view that accepts the POST of the form, here the /contact/ route in the server/views.py file. It is in the first position, before all processing.

Install the app:

pip install myra-eucaptcha-django

Enter the app and the credentials in the settings.py file:

import os

INSTALLED_APPS = [
    # ...
    "myra_eucaptcha_django",
]

EUCAPTCHA_SITEKEY = os.environ["EUCAPTCHA_SITE_KEY"]
EUCAPTCHA_SECRET = os.environ["EUCAPTCHA_SECRET_KEY"]

Make the tables of the app:

python manage.py migrate

Put the verification at the start of the view:

from django.core.exceptions import ValidationError
from django.http import HttpResponse, HttpResponseBadRequest
from myra_eucaptcha_django import validate_captcha


def contact(request):
    if request.method == "POST":
        # verification before all processing
        try:
            validate_captcha(
                token=request.POST.get("eu-captcha-response", ""),
                remote_addr=request.META.get("REMOTE_ADDR"),
            )
        except ValidationError:
            return HttpResponseBadRequest("captcha verification failed")

        # the existing logic continues from here
        email = request.POST["email"]
        message = request.POST["message"]
        return HttpResponse("Thank you!")

Note

The widget puts the token in a form field. Thus, it is in request.POST. Keep the sitekey and the secret in environment variables. As an alternative, keep the credentials in the administration area of Django at Captcha Configurations. The configuration that is set as the default has precedence over the values from settings.py.

Warning

In training mode, validate_captcha causes no ValidationError. The transmission goes through. Training mode is effective for an unknown sitekey, for a wrong secret, and when the protection is off. Thus, always do the test with the true credentials from the customer portal.

See Django.

Verify the integration

Integration Test view with the Fully Integrated result

Integration Test view with the Fully Integrated result

At the end, use the Integration Test view of the sitekey to examine if the frontend and the backend operate together. Then the sitekey has the Fully Integrated status.

See Testing the integration.