Капча и защита формы в Django: что подготовить до начала
Разбор того, что нужно иметь под рукой до подключения reCAPTCHA к форме на Django: ключи, версии, настройки CSRF и обработка ошибок валидации.

К концу вы подключите Google reCAPTCHA v3 к обычной форме на Django 5.x, добавите проверку токена на сервере и разберётесь, где встроенная защита фреймворка заканчивается и почему без внешней капчи спам-боты всё равно проходят. На чистом проекте это занимает 30–40 минут вместе с получением ключей. Код проверялся на Django 5.0 и Python 3.11.
Что нужно иметь до начала
Прежде чем писать хоть строчку, соберите вот это. Половина проблем новичков — не в коде, а в том, что чего-то не хватило на середине.
- Django 5.x и работающая форма (обычная
forms.Formилиforms.ModelForm), которую вы уже выводите в шаблоне и обрабатываете в представлении. - Пара ключей reCAPTCHA — site key и secret key. Регистрируются бесплатно в админке Google reCAPTCHA. Для v3 при регистрации выбираете тип reCAPTCHA v3 и указываете домены, включая
localhostдля отладки. - Возможность делать исходящие HTTPS-запросы с сервера — проверка токена идёт запросом к серверу Google. На закрытых контурах без выхода в интернет капча работать не будет.
- Библиотека
requestsдля запроса к API проверки:pip install requests. Можно обойтись стандартнымurllib, но сrequestsкод короче. - Понимание, что CSRF-защита Django и капча решают разные задачи. Одно другое не заменяет.
Site key вставляется в HTML и виден всем — это нормально. Secret key живёт только на сервере, его нельзя коммитить в репозиторий. Держите его в переменной окружения.
Что Django уже защищает, а что нет
Django из коробки закрывает CSRF: если в форме есть {% csrf_token %} и включён CsrfViewMiddleware, чужой сайт не отправит запрос от имени вашего пользователя. Это защита от подделки запроса, а не от автоматики.
А вот от чего Django не защищает без вашего участия:
- Массовая отправка формы ботом, который сам получает CSRF-токен и шлёт валидный POST. CSRF ему не мешает.
- Спам в комментариях, регистрациях и формах обратной связи.
- Перебор паролей на форме входа — тут нужен ещё и rate limit, капча одна не спасёт.
Капча отсекает грубую автоматику, но не заменяет ограничение частоты запросов. Форма логина без rate limit остаётся уязвимой к перебору, даже с reCAPTCHA v3.
Почему v3, а не v2
reCAPTCHA v2 — это чекбокс «я не робот» и картинки со светофорами. v3 работает в фоне: возвращает оценку от 0.0 до 1.0, где ближе к 1.0 — вероятнее человек. Пользователь ничего не нажимает.
Цена выбора: v3 не блокирует запрос сама, она отдаёт число. Порог отсечения (score) вы задаёте руками, и его придётся подбирать под свой трафик. Слишком высокий порог начнёт резать живых людей, слишком низкий пропустит ботов.
| Параметр | reCAPTCHA v2 | reCAPTCHA v3 |
|---|---|---|
| Действие пользователя | Клик по чекбоксу | Ничего, работает в фоне |
| Результат проверки | Прошёл / не прошёл | Оценка 0.0–1.0 |
| Кто принимает решение | Вы, по порогу score | |
| Риск ложных срабатываний | Ниже | Зависит от порога |
Подключение по шагам
-
Сохраните ключи в переменные окружения и прочитайте их в
settings.py. Не хардкодьте secret key.import os RECAPTCHA_SITE_KEY = os.environ.get("RECAPTCHA_SITE_KEY") RECAPTCHA_SECRET_KEY = os.environ.get("RECAPTCHA_SECRET_KEY") RECAPTCHA_MIN_SCORE = 0.5 # порог отсечения, подберёте позжеПроверка: в
python manage.py shellкомандаfrom django.conf import settings; settings.RECAPTCHA_SITE_KEYдолжна вернуть ваш ключ, а неNone. -
Подключите скрипт reCAPTCHA в шаблоне и добавьте скрытое поле для токена. Site key подставляется из контекста.
<script src="https://www.google.com/recaptcha/api.js?render={{ recaptcha_site_key }}"></script> <form method="post" id="contact-form"> {% csrf_token %} {{ form.as_p }} <input type="hidden" name="recaptcha_token" id="recaptcha_token"> <button type="submit">Отправить</button> </form> <script> document.getElementById("contact-form").addEventListener("submit", function (event) { event.preventDefault(); const form = event.target; grecaptcha.ready(function () { grecaptcha.execute("{{ recaptcha_site_key }}", { action: "submit" }) .then(function (token) { document.getElementById("recaptcha_token").value = token; form.submit(); }); }); }); </script>Проверка: откройте страницу, в правом нижнем углу должен появиться значок reCAPTCHA. В консоли браузера не должно быть ошибки про неверный site key.
-
В представлении передайте site key в шаблон.
from django.conf import settings from django.shortcuts import render def contact_view(request): if request.method == "POST": # обработка ниже pass return render(request, "contact.html", { "form": ContactForm(), "recaptcha_site_key": settings.RECAPTCHA_SITE_KEY, }) -
Проверьте токен на сервере. Токен из поля
recaptcha_tokenотправляется на API Google вместе с secret key.import requests from django.conf import settings def verify_recaptcha(token, remote_ip=None): """Возвращает True, если проверка пройдена и score не ниже порога.""" if not token: return False payload = { "secret": settings.RECAPTCHA_SECRET_KEY, "response": token, } if remote_ip: payload["remoteip"] = remote_ip try: response = requests.post( "https://www.google.com/recaptcha/api/siteverify", data=payload, timeout=5, ) result = response.json() except requests.RequestException: # сеть недоступна или таймаут — решаете сами: пропускать или нет return False if not result.get("success"): return False # для v3 проверяем оценку return result.get("score", 0) >= settings.RECAPTCHA_MIN_SCORE -
Свяжите проверку с обработкой формы. Если капча не прошла, добавьте ошибку в форму, а не роняйте запрос.
def contact_view(request): if request.method == "POST": form = ContactForm(request.POST) token = request.POST.get("recaptcha_token") remote_ip = request.META.get("REMOTE_ADDR") if not verify_recaptcha(token, remote_ip): form.add_error(None, "Проверка на робота не пройдена. Обновите страницу.") elif form.is_valid(): # сохраняем, отправляем письмо и так далее return redirect("contact_success") else: form = ContactForm() return render(request, "contact.html", { "form": form, "recaptcha_site_key": settings.RECAPTCHA_SITE_KEY, })Проверка: отправьте форму вручную — она проходит. Затем откройте DevTools, удалите скрытое поле
recaptcha_tokenперед отправкой — форма должна вернуться с сообщением об ошибке капчи.
Если не получилось
- В консоли
Invalid site keyили значок не появляется. Site key в шаблоне не совпадает с зарегистрированным, либо домен страницы не добавлен в список доменов в админке reCAPTCHA. Для локальной отладки добавьтеlocalhostи127.0.0.1. result["success"]всегдаFalse, а в ответеerror-codes: ["invalid-input-secret"]. Перепутаны местами site key и secret key, либо secret key не подтянулся из окружения. Проверьтеsettings.RECAPTCHA_SECRET_KEYв shell.error-codes: ["timeout-or-duplicate"]. Токен reCAPTCHA v3 живёт около двух минут и одноразовый. Если между генерацией токена и отправкой прошло много времени или токен переиспользован — проверка провалится. Генерируйте токен непосредственно перед submit, как в примере.- Форма молча не отправляется. JavaScript перехватил
submitи ждёт токен, ноgrecaptchaне загрузился. Проверьте, что тег<script src="...api.js...">подключён и не заблокирован блокировщиком рекламы. - Живые пользователи получают ошибку капчи. Порог
RECAPTCHA_MIN_SCOREслишком высокий. Начните с 0.5, посмотрите распределениеscoreв логах и корректируйте.
Что настроить дальше
Работающая капча — только часть защиты. После подключения:
- Логируйте
scoreиз ответа Google хотя бы неделю, прежде чем фиксировать порог. Так вы увидите реальное распределение своих пользователей, а не подберёте число наугад. - Добавьте ограничение частоты запросов на чувствительные формы — вход, регистрацию, восстановление пароля. Капча ловит ботов, rate limit ловит перебор.
- Решите, что делать при недоступности API Google: в примере
verify_recaptchaпри таймауте возвращаетFalseи блокирует отправку. Для формы обратной связи это может быть слишком строго — тогда логируйте инцидент и пропускайте.
Интеграция капчи в Django admin и в API на Django REST Framework — отдельные темы, здесь разобрана только обычная HTML-форма.
AI-стилист и фотограф: Замена фона, ретушь, аватарки, фото для резюме.
Спросить за 20 ₽Источники: reCAPTCHA v3 — Google Developers, Cross Site Request Forgery protection — Django documentation
Частые вопросы
А если у меня Django 4.2, код подойдёт?
Обязательно ставить requests, или можно стандартной библиотекой?
Капча заменяет CSRF-токен Django?
Что будет, если сервер Google недоступен?
Не сработало — форма отправляется без токена. Почему?
Какой порог score выставить для v3?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.