Что такое Captcha?
Captcha (капча) – это некий тест, который человек решает очень легко, а робот – нет (научить компьютер решать его крайне сложно и затруднительно).
Другими словами, основная цель капчи – это определить кем является пользователь: человеком или роботом.
Используется Captcha на сайте для защиты от спама и повышенной нагрузки, которые создают роботы. Капчу можно очень часто встретить в формах регистрации, входа, отправки сообщений, при скачивании файлов и многих других местах.
В большинстве случаев капча отображается как некоторый искаженный или наложенный на фон текст, который посетителю сайта необходимо разобрать и ввести его в некоторое поле. Кроме текста на фоне используется и другие алгоритмы: найти среди множества картинок правильные, собрать пазл, переместить слайдер, нарисовать связь между несколькими картинками и т.д.
Исходные коды капчи
Исходные коды капчи расположены на GitHub: itchief/captcha.
Скриншот формы с капчей:

Процесс разработки капчи представлен в виде следующих этапов:
- верстка формы;
- создания файла «captcha.php» для генерация кода капчи и изображения;
- написание обработчика для формы (файл «process-form.php»);
- написание JavaScript для отправки формы на сервер через AJAX и обработки ответа.
Верстка формы
Разработку Captcha начнём с создания формы. Для простоты форма будет состоять из капчи и кнопки отправить:
<!-- Сообщение которое будем показывать при успешной отправки формы --> <div class="form-result d-none">Форма успешно отправлена!</div> <!-- Форма --> <form id="form" action="/assets/php/process-form.php" method="post"> <!-- Капча --> <div class="captcha"> <div class="captcha__image-reload"> <img class="captcha__image" src="/assets/php/captcha.php" width="132" alt="captcha"> <button type="button" class="captcha__refresh"></button> </div> <div class="captcha__group"> <label for="captcha">Код, изображенный на картинке</label> <input type="text" name="captcha" id="captcha"> <div class="invalid-feedback"></div> </div> </div> <!-- Кнопка "Отправить" --> <button type="submit">Отправить</button> </form>
Генерация кода капчи и изображения
Формирование кода капчи и изображения выполним в файле «captcha.php», который поместим в папку «/assets/php»:
<?php define('USE_SESSION', true); // 1. Генерируем код капчи // 1.1. Устанавливаем символы, из которых будет составляться код капчи $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz'; // 1.2. Количество символов в капче $length = 6; // 1.3. Генерируем код $code = substr(str_shuffle($chars), 0, $length); if (USE_SESSION) { // 2a. Используем сессию session_start(); $_SESSION['captcha'] = crypt($code, '$1$itchief$7'); session_write_close(); } else { // 2a. Используем куки (время действия 600 секунд) $value = crypt($code, '$1$itchief$7'); $expires = time() + 600; setcookie('captcha', $value, $expires, '/', 'test.ru', false, true); } // 3. Генерируем изображение // 3.1. Создаем новое изображение из файла $image = imagecreatefrompng(__DIR__ . '/files/bg.png'); // 3.2 Устанавливаем размер шрифта в пунктах $size = 36; // 3.3. Создаём цвет, который будет использоваться в изображении $color = imagecolorallocate($image, 66, 182, 66); // 3.4. Устанавливаем путь к шрифту $font = __DIR__ . '/files//oswald.ttf'; // 3.5 Задаём угол в градусах $angle = rand(-10, 10); // 3.6. Устанавливаем координаты точки для первого символа текста $x = 56; $y = 64; // 3.7. Наносим текст на изображение imagefttext($image, $size, $angle, $x, $y, $color, $font, $code); // 3.8 Устанавливаем заголовки header('Cache-Control: no-store, must-revalidate'); header('Expires: 0'); header('Content-Type: image/png'); // 3.9. Выводим изображение imagepng($image); // 3.10. Удаляем изображение imagedestroy($image);
Генерирование текста капчи выполняется очень просто. Для этого в переменную $chars
помещаются символы, из которых она может состоять. Далее с помощью функции str_shuffle()
эти символы случайным образом перемешиваются и посредством substr
выбирается первые шесть из них.
Сохранении полученной капчи по умолчанию осуществляется в сессионную переменную. Но если хотите в куки, то установите переменной $use_session
значение false
:
define('USE_SESSION', false);
Если используете протокол HTTPS, то установите шестому аргументу значение true:
setcookie('captcha', $value, $expires, '/', 'test.ru', true, true);
Для отправки капчи клиенту создается изображение, имеющее фон «bg.png», на котором с помощью функции imagefttext()
пишется текст капчи.
Скрипт для обновления капчи на форме
Код для обновления капчи при нажатию на кнопку .captcha__refresh
:
// функция для обновления капчи const refreshCaptcha = (target) => { const captchaImage = target.closest('.captcha__image-reload').querySelector('.captcha__image'); captchaImage.src = '/assets/php/captcha.php?r=' + new Date().getUTCMilliseconds(); } // получение кнопки для обновления капчи const captchaBtn = document.querySelector('.captcha__refresh'); // запуск функции refreshCaptcha при нажатии на кнопку captchaBtn.addEventListener('click', (e) => refreshCaptcha(e.target));
Добавление обработчика к кнопке выполняется через addEventListener
.
Написание обработчика формы
Для обработки формы создадим файл «process-form.php» в папке «/assets/php/»
В этом файле будем сравнивать текст капчи, который пользователь ввел в форме с тем, который у нас хранится в сессии или куки.
<?php // 1a session_start(); $captcha = $_SESSION['captcha']; unset($_SESSION['captcha']); session_write_close(); // 1b //$captcha = $_COOKIE['captcha']; //unset($_COOKIE['captcha']); //setcookie('captcha', '', time() - 3600, '/', 'test.ru', false, true); $result = ['success' => false]; $code = $_POST['captcha']; if (empty($code)) { $result['errors'][] = ['captcha', 'Пожалуйста введите код!']; } else { $code = crypt(trim($code), '$1$itchief$7'); $result['success'] = $captcha === $code; if (!$result['success']) { $result['errors'][] = ['captcha', 'Введенный код не соответствует изображению!']; } } echo json_encode($result);
В качестве результата будем возвращать JSON. В случае успеха:
{success: true}
В противном случае, success
присвоим значение false
, а в errors
поместим ошибки:
{ success: false, errors: [ ['captcha', 'Пожалуйста введите код!'] ] }
По умолчанию этот файл сравнивает капчу со значением, находящимся в сессии. Если в «captcha.php» сохраняете капчу в куки, то здесь необходимо закомментировать секцию 1a и раскомментировать 1b:
// 1a //session_start(); //$captcha = $_SESSION['captcha']; //unset($_SESSION['captcha']); //session_write_close(); // 1b $captcha = $_COOKIE['captcha']; unset($_COOKIE['captcha']); setcookie('captcha', '', time() - 3600, '/', 'test.ru', false, true);
Если используете протокол HTTPS, то замените шестой аргумент на значение true
:
//setcookie('captcha', '', time() - 3600, '/', 'test.ru', true, true);
JavaScript для отправки формы на сервер через AJAX
Код для отправки формы на сервер через AJAX и обработки полученного результата:
const form = document.querySelector('#form'); form.addEventListener('submit', (e) => { e.preventDefault(); try { fetch(form.action, { method: form.method, credentials: 'same-origin', body: new FormData(form) }) .then((response) => { return response.json(); }) .then((data) => { document.querySelectorAll('input.is-invalid').forEach((input) => { input.classList.remove('is-invalid'); input.nextElementSibling.textContent = ''; }); if (!data.success) { refreshCaptcha(form.querySelector('.captcha__refresh')); data.errors.forEach(error => { console.log(error); const input = form.querySelector(`[name="${error[0]}"]`); if (input) { input.classList.add('is-invalid'); input.nextElementSibling.textContent = error[1]; } }) } else { form.reset(); form.querySelector('.captcha__refresh').disabled = true; form.querySelector('[type=submit]').disabled = true; document.querySelector('.form-result').classList.remove('d-none'); } }); } catch (error) { console.error('Ошибка:', error); } });
В этом коде отправка данных через AJAX выполняется посредством fetch()
. Получение данных формы с использованием FormData
.
Для отправки и получения cookie посредством fetch()
установим:
credentials: 'same-origin',
Если в success
находится значение false
, то будем помечать поля, которые не прошли валидацию и выводить подсказки:
if (!data.success) { refreshCaptcha(form.querySelector('.captcha__refresh')); data.errors.forEach(error => { console.log(error); const input = form.querySelector(`[name="${error[0]}"]`); if (input) { input.classList.add('is-invalid'); input.nextElementSibling.textContent = error[1]; } }) }

Если в success
содержится значение true
, то будем очищать поля и выводить сообщение об успешной отправки формы:
form.reset(); form.querySelector('.captcha__refresh').disabled = true; form.querySelector('[type=submit]').disabled = true; document.querySelector('.form-result').classList.remove('d-none');
