Как добавить Google reCAPTCHA в HTML-форму

Как добавить Google reCAPTCHA в HTML-форму

2025-05-20

В этой статье мы рассмотрим, как интегрировать Google reCAPTCHA (v2) в HTML-форму обратной связи с клиентской валидацией без использования библиотек jQuery.

Также покажем, как проверить результат на сервере с помощью PHP и curl. Отправка формы будет осуществляться на 1C-Bitrix, однако данный подход можно легко адаптировать и для других систем.

Это позволит эффективно защитить вашу форму от спама и автоматических отправок.

Шаг 1. Регистрация и получение ключей reCAPTCHA

  1. Перейдите на Google reCAPTCHA Admin Console.
  2. Зарегистрируйте новый сайт, выбрав тип reCAPTCHA v2 — «Я не робот» (Checkbox).
  3. Введите домен вашего сайта.
  4. Получите два ключа:
    • Site key — для вставки в HTML.
    • Secret key — для проверки на сервере.

Шаг 2. Пример HTML-формы с reCAPTCHA

1
2
<!-- Подключаем скрипт reCAPTCHA на страницу -->
<script src="https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoadCallback&render=explicit" async defer></script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<form id="myform" class="myform" novalidate>
<div class="myform__row">
<div class="myform__field">
<label for="name" class="myform__label">Имя:</label>
<div class="myform__input-wrap">
<input type="text" id="name" class="myform__input" name="name" minlength="4" required />
</div>
</div>
<div class="myform__error">
<div class="myform__error-message myform__error-message--name"></div>
</div>
</div>

<div class="myform__row">
<div class="myform__field">
<label for="phone" class="myform__label">Телефон:</label>
<div class="myform__input-wrap">
<input type="text" id="phone" class="myform__input" name="phone" placeholder="+7 (___) ___ __-__" minlength="18" maxlength="18" required />
</div>
</div>
<div class="myform__error">
<div class="myform__error-message myform__error-message--phone"></div>
</div>
</div>

<div class="myform__row">
<div class="myform__field">
<label for="email" class="myform__label">Email:</label>
<div class="myform__input-wrap">
<input type="email" id="email" class="myform__input" name="email" required />
</div>
</div>
<div class="myform__error">
<div class="myform__error-message myform__error-message--email"></div>
</div>
</div>

<div class="myform__row">
<div class="myform__field">
<label for="message" class="myform__label">Сообщение:</label>
<div class="myform__input-wrap">
<textarea name="message" id="message" rows="5" class="myform__input" required></textarea>
</div>
</div>
<div class="myform__error">
<div class="myform__error-message myform__error-message--message"></div>
</div>
</div>

<div class="g-recaptcha" id="myform-g-recaptcha"></div>

<button type="submit" class="myform__submit">Отправить</button>
</form>

Шаг 3. Реализуем валидацию и маску для поля телефона с помощью чистого JavaScript (без jQuery и сторонних библиотек).

Это позволяет ускорить загрузку формы, избежать лишних зависимостей и снизить вес подключаемого кода.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
const formValidationModule = (() => {

const success = 200;
const error = 400;

function onPhoneInput(e) {
let input = e.target;
let value = input.value.replace(/\D/g, ''); // Оставляем только цифры

if (value.startsWith('8')) {
value = '7' + value.slice(1); // Преобразуем 8 в 7
}

if (!value.startsWith('7')) {
value = '7' + value; // Всегда начинаем с 7
}

let formatted = '+7 ';

if (value.length > 1) { // (999)
formatted += '(' + value.substring(1, 4);
}
if (value.length >= 4) {
formatted += ') ' + value.substring(4, 7);
}
if (value.length >= 7) {
formatted += ' ' + value.substring(7, 9);
}
if (value.length >= 9) {
formatted += '-' + value.substring(9, 11);
}

input.value = formatted;
}

function onPhoneFocus(e) {
if (!e.target.value) {
e.target.value = '+7 ';
}
}

function onPhoneBlur(e) {
const value = e.target.value;
if (value === '+7 ' || value.length < 18) {
e.target.value = '';
}
}

function onPhoneKeyDown(e) {
// Разрешаем только цифры, Backspace, Delete, стрелки
if (
e.key.length === 1 &&
!/[0-9]/.test(e.key)
) {
e.preventDefault();
}
}

return {

checkValid: function (form) {
if (!form.checkValidity()) {
const invalidFields = Array.from(form.elements).filter(el => {
if (!el.validity.valid) {
return !el.validity.valid;
}
});

this.addErrorMessages(invalidFields, form.id);
return false;
}

// Если валидно возвращаем данные формы
const formData = new FormData(form);
return formData;
},

submitHandler: function (...args) {
const formEl = document.getElementById(args[0]);

// Сохранить контекст this через стрелочную функцию
formEl.addEventListener('submit', async (e) => {
e.preventDefault(); // отменяем стандартную отправку формы

const formTarget = e.target;
const dataFields = this.checkValid(formTarget);
if (dataFields) {
const response = await this.fetch(args[0], args[1], dataFields);
}
});
},

fetch: async function (...args) {

try {
const response = await fetch(args[1], {
method: 'POST',
body: args[2], // dataFields
// Если сервер ожидает JSON, нужно собрать JSON из formData и указать заголовок:
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(Object.fromEntries(formData.entries())),
});
if (!response.ok) {
throw new Error(`Ошибка сервера: ${response.status}`);
}

// или response.text() в зависимости от ответа
const result = await response.json();

if (result.code == success) {

// Чистим поля формы
const formEl = document.getElementById(args[0]);
formEl.reset();
}

console.log(`result ${result.code}: ${result.message}`);
alert(`${result.message}`);

} catch (error) {
console.log(`Ошибка отправки: ${error.message}`);
}
},

phoneMask: function (phone) {
const phoneInput = document.getElementById(phone);
if (phoneInput) {
phoneInput.addEventListener('input', onPhoneInput);
phoneInput.addEventListener('focus', onPhoneFocus);
phoneInput.addEventListener('blur', onPhoneBlur);
phoneInput.addEventListener('keydown', onPhoneKeyDown);
}
},

addErrorMessages: function (invalidFields, formId) {
const messages = invalidFields.map(field => {

if (field.validity.valueMissing) {
let errorStr = document.querySelector(`.${formId}__error-message.${formId}__error-message--${field.name}`);
if (errorStr) {
errorStr.innerHTML = '<span>Необходимо заполнить поле</span>';
}
}

if (field.validity.typeMismatch) {
let errorStr = document.querySelector(`.${formId}__error-message.${formId}__error-message--${field.name}`);
if (errorStr) {
errorStr.innerHTML = '<span>Неверный формат</span>';
}
}

if (field.validity.tooShort) {
let errorStr = document.querySelector(`.${formId}__error-message.${formId}__error-message--${field.name}`);
if (errorStr) {
errorStr.innerHTML = '<span>Слишком короткое значение</span>';
}
}

});
},

cleanErrorMessages: function (formId) {
const formEl = document.getElementById(formId);
formEl.addEventListener('input', (event) => {
const field = event.target;
let errorStr = document.querySelector(`.${formId}__error-message.${formId}__error-message--${field.name}`);
if (errorStr) {
errorStr.innerHTML = '';
}
});
},

init: function (...args) {
this.submitHandler(args[0], args[2]);
this.cleanErrorMessages(args[0]);
this.phoneMask(args[1]);
}
}

})();

function onRecaptchaLoadCallback() {

const form = formValidationModule;

if (document.getElementById('myform-g-recaptcha')) {
const recaptchaWidgetId = grecaptcha.render('myform-g-recaptcha', {
'sitekey': 'ВАШ_SITE_KEY'
});
/*
1. id формы - myform
2. Маска для телефона (передаём название name input-a). Отключить маску: false
3. Валидация клиента recaptchaWidgetId.
4. Путь к отправщику: /ajax/form.php
*/
form.init('myform', 'phone', '/ajax/form.php');
}

}

В данном коде реализован модуль formValidationModule, который отвечает за:

1. Маска поля телефона

  • При вводе в поле телефона автоматически форматируется номер в российский формат:
    • Удаляются все символы, кроме цифр.
    • Если номер начинается с 8, он преобразуется в 7.
    • Всегда добавляется префикс +7 .
    • Номер форматируется по шаблону: +7 (999) 999 99-99.
  • Обработчики событий:
    • input — форматирует номер в процессе ввода.
    • focus — при фокусе, если поле пустое, вставляет +7 .
    • blur — если поле пустое или введён неполный номер, очищает поле.
    • keydown — разрешает ввод только цифр, клавиш удаления и навигации.

2. Валидация формы на клиенте

  • Метод checkValid(form) проверяет валидность всех полей формы с помощью встроенного HTML API (checkValidity()).
  • Если есть ошибки, вызывает метод addErrorMessages(invalidFields), который отображает сообщения об ошибках рядом с каждым невалидным полем:
    • Обязательное поле (valueMissing) — сообщение “поле обязательно”.
    • Неверный формат (typeMismatch) — сообщение “неверный формат”.
    • Слишком короткое значение (tooShort) — сообщение “слишком короткое значение”.
  • Если форма валидна, возвращает объект FormData с данными формы.

3. Отправка формы

  • Метод submitHandler(formId, url) добавляет обработчик события submit на форму с указанным id.
  • При отправке:
    • Отменяет стандартное поведение браузера.
    • Проверяет форму через checkValid.
    • Если данные валидны, отправляет их на сервер с помощью метода fetch(formId, url, formData).
  • Метод fetch использует fetch API для POST-запроса к серверу:
    • Отправляет данные формы.
    • Обрабатывает ответ в формате JSON.
    • При успешном ответе (код 200) сбрасывает форму.
    • Выводит сообщение из ответа в консоль и через alert.

4. Очистка сообщений об ошибках

  • Метод cleanErrorMessages(formId) добавляет обработчик события input на форму.
  • При изменении любого поля очищает соответствующее сообщение об ошибке.

5. Инициализация модуля

  • Метод init(formId, phoneInputId, url) запускает:
    • Обработчик отправки формы.
    • Очистку ошибок при вводе.
    • Маску для телефона (если передан phoneInputId).

Интеграция с Google reCAPTCHA

  • Функция onRecaptchaLoadCallback() вызывается после загрузки API reCAPTCHA.
  • Если на странице есть элемент с id myform-g-recaptcha, инициализируется виджет reCAPTCHA с указанным sitekey.
  • После этого вызывается инициализация модуля валидации с параметрами:
    • myform — id формы.
    • phone — id поля телефона для маски (если маска не нужна — передать false).
    • /ajax/form.php — адрес обработчика на сервере.

Замените ВАШ_SITE_KEY на ваш реальный site key из Google.

Шаг 4. Обработка формы на PHP с проверкой полей и reCAPTCHA

Создайте файл по пути /ajax/form.php с примером кода:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Разместим обработку формы в файле /ajax/form.php

require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';

$secretKey = 'ВАШ_SECRET_KEY';

// Если требуется, то можно указать дополнительные поля
$name = trim($_POST['name'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$email = trim($_POST['email'] ?? '');
$message = trim($_POST['message'] ?? '');
$recaptchaResponse = $_POST['g-recaptcha-response'] ?? '';

if (!$name || !$phone || !$email || !$message) {
echo json_encode([
'status' => 'error',
'code' => 400,
'message' => 'Пожалуйста, заполните все поля'
], JSON_UNESCAPED_UNICODE);
exit;
}

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo json_encode([
'status' => 'error',
'code' => 400,
'message' => 'Некорректный email'
], JSON_UNESCAPED_UNICODE);
exit;
}

function validatePhone($phone) {
// Убираем все нецифровые символы
$digits = preg_replace('/\D+/', '', $phone);

// Проверяем, что осталось 11 цифр и первая — 7 или 8
if (preg_match('/^[78]\d{10}$/', $digits)) {
return true;
}
return false;
}
if (!validatePhone($phone)) {

echo json_encode([
'status' => 'error',
'code' => 400,
'message' => 'Некорректный номер телефона'
], JSON_UNESCAPED_UNICODE);
exit;
}

function verifyRecaptcha($recaptchaResponse, $secretKey) {
if (empty($recaptchaResponse)) {
return false;
}

$url = 'https://www.google.com/recaptcha/api/siteverify';
$data = [
'secret' => $secretKey,
'response' => $recaptchaResponse,
'remoteip' => $_SERVER['REMOTE_ADDR'] ?? ''
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
if ($response === false) {
curl_close($ch);
return false;
}
curl_close($ch);

$responseData = json_decode($response, true);
return $responseData['success'] ?? false;
}

if (!verifyRecaptcha($recaptchaResponse, $secretKey)) {
echo json_encode([
'status' => 'error',
'code' => 400,
'message' => 'Пожалуйста, подтвердите, что вы не робот.'
], JSON_UNESCAPED_UNICODE);
exit;
}

$arFields = [
'NAME' => $name,
'PHONE' => $phone,
'EMAIL' => $email,
'MESSAGE' => $message,
];

CEvent::Send("NAZVANIE_EVENTA", ['s1'], $arFields);

echo json_encode([
'status' => 'ok',
'code' => 200,
'message' => 'Спасибо! Данные успешно отправлены.'
], JSON_UNESCAPED_UNICODE);
exit;

Не забудьте заменить ВАШ_SECRET_KEY на ваш секретный ключ.

Краткое пояснение к коду

HTML: подключаем скрипт reCAPTCHA, добавляем виджет с вашим sitekey.

PHP:

  • Получаем данные из $_POST.
  • Получаем ответ в JSON и проверяем поле success.
  • Если проверка не пройдена — выводим ошибку.
  • Если всё хорошо — обрабатываем форму (например, отправляем письмо).

Итог

  • reCAPTCHA помогает защитить формы от спама.
  • Интеграция простая: добавляем виджет в HTML и проверяем ответ на сервере.
  • Для проверки используем curl и API Google.
  • Обязательно валидируем данные формы и результат reCAPTCHA.