Плагины для форм — это удобно, но они добавляют нагрузку, обновления и потенциальные уязвимости. В этой статье покажем, как создать полноценную контактную форму на WordPress с нуля: HTML-разметка, AJAX-отправка и доставка на почту через wp_mail(). Никаких Contact Form 7, WPForms и Gravity Forms.
Зачем делать формы без плагина?
- Скорость — нет лишних CSS/JS файлов, которые грузит плагин
- Контроль — вы точно знаете что происходит с данными
- Безопасность — меньше кода = меньше точек атаки
- Гибкость — форма точно под ваш дизайн, без костылей
Шаг 1: HTML-разметка формы
Создаём форму с полями имени, email, телефона и сообщения. Добавляем скрытое поле для nonce (защита от CSRF) и honeypot-поле (антиспам).
<?php
/**
* Шорткод [contact_form] — выводит форму обратной связи
*/
function wenesis_contact_form_shortcode() {
// Генерируем nonce для защиты
$nonce = wp_create_nonce('contact_form_nonce');
ob_start();
?>
<form id="contact-form" class="contact-form" method="post">
<!-- Nonce для безопасности -->
<input type="hidden" name="contact_nonce" value="<?php echo $nonce; ?>">
<!-- Honeypot: скрытое поле, боты его заполнят -->
<div style="position:absolute;left:-9999px;" aria-hidden="true">
<input type="text" name="website_url" tabindex="-1" autocomplete="off">
</div>
<div class="form-group">
<label for="cf-name">Имя *</label>
<input type="text" id="cf-name" name="name" required placeholder="Ваше имя">
</div>
<div class="form-group">
<label for="cf-email">Email *</label>
<input type="email" id="cf-email" name="email" required placeholder="example@mail.ru">
</div>
<div class="form-group">
<label for="cf-phone">Телефон</label>
<input type="tel" id="cf-phone" name="phone" placeholder="+7 (999) 123-45-67">
</div>
<div class="form-group">
<label for="cf-message">Сообщение *</label>
<textarea id="cf-message" name="message" rows="5" required placeholder="Расскажите о вашем проекте"></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Отправить</button>
</div>
<div id="form-status" class="form-status" style="display:none;"></div>
</form>
<?php
return ob_get_clean();
}
add_shortcode('contact_form', 'wenesis_contact_form_shortcode');
Теперь можно вывести форму на любой странице шорткодом [contact_form].
Шаг 2: AJAX-обработчик на PHP
Принимаем данные формы через AJAX, валидируем и отправляем письмо через wp_mail().
<?php
/**
* AJAX-обработчик отправки формы
*/
function wenesis_handle_contact_form() {
// 1. Проверяем nonce
if (!isset($_POST['contact_nonce']) || !wp_verify_nonce($_POST['contact_nonce'], 'contact_form_nonce')) {
wp_send_json_error('Ошибка безопасности. Обновите страницу.');
}
// 2. Антиспам: honeypot
if (!empty($_POST['website_url'])) {
wp_send_json_error('Spam detected');
}
// 3. Антиспам: таймер (менее 3 секунд = бот)
$load_time = isset($_POST['page_load_time']) ? intval($_POST['page_load_time']) : 0;
if ($load_time > 0 && $load_time < 3) {
wp_send_json_error('Слишком быстро! Подождите немного.');
}
// 4. Получаем и очищаем данные
$name = sanitize_text_field($_POST['name'] ?? '');
$email = sanitize_email($_POST['email'] ?? '');
$phone = sanitize_text_field($_POST['phone'] ?? '');
$message = sanitize_textarea_field($_POST['message'] ?? '');
// 5. Валидация
if (empty($name)) {
wp_send_json_error('Укажите имя');
}
if (empty($email) || !is_email($email)) {
wp_send_json_error('Укажите корректный email');
}
if (empty($message)) {
wp_send_json_error('Напишите сообщение');
}
// 6. Антиспам: проверка на ссылки в имени
if (preg_match('/https?:\/\/|www\./i', $name)) {
wp_send_json_error('Недопустимое имя');
}
// 7. Антиспам: проверка на спам-слова
$spam_words = ['casino', 'viagra', 'porn', 'crypto', 'bitcoin', 'loan', 'click here', 'заработок', 'казино'];
$all_text = strtolower($name . ' ' . $message);
foreach ($spam_words as $word) {
if (strpos($all_text, $word) !== false) {
wp_send_json_error('Spam detected');
}
}
// 8. Антиспам: лимит отправок с одного IP (5 в час)
$ip = $_SERVER['REMOTE_ADDR'];
$transient_key = 'contact_submits_' . md5($ip);
$submits = get_transient($transient_key) ?: 0;
if ($submits >= 5) {
wp_send_json_error('Слишком много заявок. Попробуйте позже.');
}
set_transient($transient_key, $submits + 1, HOUR_IN_SECONDS);
// 9. Формируем письмо
$to = get_option('admin_email');
$subject = 'Новая заявка с сайта ' . get_bloginfo('name');
$body = "Имя: {$name}\n";
$body .= "Email: {$email}\n";
if ($phone) {
$body .= "Телефон: {$phone}\n";
}
$body .= "\nСообщение:\n{$message}\n";
$body .= "\n---\n";
$body .= "IP: {$ip}\n";
$body .= "Дата: " . current_time('d.m.Y H:i') . "\n";
$headers = [
'Content-Type: text/plain; charset=UTF-8',
"Reply-To: {$name} <{$email}>",
];
// 10. Отправляем письмо
$sent = wp_mail($to, $subject, $body, $headers);
if ($sent) {
// Сохраняем заявку в базу (опционально)
$post_id = wp_insert_post([
'post_type' => 'contact-submissions',
'post_status' => 'private',
'post_title' => "Заявка от {$name} — {$email}",
'post_content' => $body,
]);
wp_send_json_success('Сообщение отправлено! Мы свяжемся с вами в ближайшее время.');
} else {
wp_send_json_error('Ошибка отправки. Попробуйте позже или свяжитесь с нами по телефону.');
}
}
// Регистрируем AJAX-хуки (для авторизованных и гостей)
add_action('wp_ajax_send_contact_form', 'wenesis_handle_contact_form');
add_action('wp_ajax_nopriv_send_contact_form', 'wenesis_handle_contact_form');
Шаг 3: JavaScript для AJAX-отправки
Отправляем форму без перезагрузки страницы. Добавляем таймер загрузки для антиспама.
<?php
/**
* Подключаем JS для AJAX-формы
*/
function wenesis_contact_form_scripts() {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Запоминаем время загрузки страницы
var pageLoadTime = Math.floor(Date.now() / 1000);
var loadTimeInput = document.querySelector('input[name="page_load_time"]');
if (loadTimeInput) {
loadTimeInput.value = pageLoadTime;
}
var form = document.getElementById('contact-form');
if (!form) return;
form.addEventListener('submit', function(e) {
e.preventDefault();
var submitBtn = form.querySelector('button[type="submit"]');
var status = document.getElementById('form-status');
// Блокируем кнопку
submitBtn.disabled = true;
submitBtn.textContent = 'Отправка...';
status.style.display = 'none';
// Собираем данные формы
var formData = new FormData(form);
formData.append('action', 'send_contact_form');
// AJAX-запрос
fetch('/wp-admin/admin-ajax.php', {
method: 'POST',
body: formData
})
.then(function(response) { return response.json(); })
.then(function(data) {
status.style.display = 'block';
if (data.success) {
status.className = 'form-status success';
status.textContent = data.data;
form.reset();
} else {
status.className = 'form-status error';
status.textContent = data.data;
}
})
.catch(function(error) {
status.style.display = 'block';
status.className = 'form-status error';
status.textContent = 'Ошибка сети. Попробуйте позже.';
})
.finally(function() {
submitBtn.disabled = false;
submitBtn.textContent = 'Отправить';
});
});
});
</script>
<?php
}
add_action('wp_footer', 'wenesis_contact_form_scripts');
Шаг 4: CSS-стили для формы
Добавляем базовые стили, чтобы форма выглядела аккуратно.
<?php
/**
* CSS стили для контактной формы
*/
function wenesis_contact_form_styles() {
?>
<style>
.contact-form {
max-width: 600px;
margin: 0 auto;
}
.contact-form .form-group {
margin-bottom: 20px;
}
.contact-form label {
display: block;
margin-bottom: 6px;
font-weight: 600;
color: #333;
}
.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form input[type="tel"],
.contact-form textarea {
width: 100%;
padding: 12px 16px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 16px;
transition: border-color 0.3s;
}
.contact-form input:focus,
.contact-form textarea:focus {
outline: none;
border-color: #3232ff;
}
.contact-form .btn-primary {
background: #3232ff;
color: #fff;
border: none;
padding: 14px 32px;
font-size: 16px;
border-radius: 8px;
cursor: pointer;
transition: background 0.3s;
}
.contact-form .btn-primary:hover {
background: #1a1acc;
}
.contact-form .btn-primary:disabled {
background: #999;
cursor: not-allowed;
}
.form-status {
margin-top: 16px;
padding: 12px 16px;
border-radius: 8px;
font-size: 15px;
}
.form-status.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.form-status.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
</style>
<?php
}
add_action('wp_head', 'wenesis_contact_form_styles');
Шаг 5: Регистрация типа записей для заявок (опционально)
Если хотите сохранять заявки в админке WordPress — регистрируем кастомный тип записей.
<?php
/**
* Регистрируем тип записей "Заявки" для хранения отправленных форм
*/
function wenesis_register_submissions_post_type() {
register_post_type('contact-submissions', [
'labels' => [
'name' => 'Заявки',
'singular_name' => 'Заявка',
'add_new' => 'Новая заявка',
'add_new_item' => 'Добавить заявку',
'all_items' => 'Все заявки',
],
'public' => false,
'show_ui' => true,
'show_in_menu' => true,
'menu_icon' => 'dashicons-email-alt',
'supports' => ['title', 'editor'],
'has_archive' => false,
]);
}
add_action('init', 'wenesis_register_submissions_post_type');
Итоговый код: всё в одном файле
Создайте файл contact-form.php в папке вашей темы и подключите через functions.php:
<?php
// В functions.php добавьте:
require_once get_template_directory() . '/inc/contact-form.php';
Или вставьте весь код прямо в functions.php — для простых случаев это допустимо.
Что мы получили
- ✅ Форма с полями имя, email, телефон, сообщение
- ✅ AJAX-отправка без перезагрузки страницы
- ✅ Валидация на сервере
- ✅ Защита nonce (CSRF)
- ✅ Honeypot антиспам
- ✅ Таймер загрузки (антибот)
- ✅ Проверка на спам-слова
- ✅ Лимит отправок с одного IP
- ✅ Сохранение заявок в админке
- ✅ Отправка письма через wp_mail()
- ✅ Стили для формы
- ✅ Никаких плагинов!
Расширение: добавить отправку в Telegram
Если хотите получать уведомления о заявках в Telegram — добавьте этот код после wp_mail():
<?php
/**
* Отправка уведомления в Telegram
*
* @param string $name Имя отправителя
* @param string $email Email отправителя
* @param string $phone Телефон
* @param string $message Текст сообщения
*/
function wenesis_send_telegram($name, $email, $phone, $message) {
$bot_token = 'ВАШ_ТОКЕН_БОТА';
$chat_id = 'ВАШ_CHAT_ID';
$text = "🆕 Новая заявка!\n\n";
$text .= "👤 Имя: {$name}\n";
$text .= "📧 Email: {$email}\n";
if ($phone) {
$text .= "📱 Телефон: {$phone}\n";
}
$text .= "\n💬 Сообщение:\n{$message}";
$url = "https://api.telegram.org/bot{$bot_token}/sendMessage";
wp_remote_post($url, [
'body' => [
'chat_id' => $chat_id,
'text' => $text,
'parse_mode' => 'HTML',
],
]);
}
// Вызываем после успешной отправки письма:
// wenesis_send_telegram($name, $email, $phone, $message);
Заключение
Нативная форма на WordPress — это просто, быстро и надёжно. Вы получаете полный контроль над кодом, минимальную нагрузку на сервер и никаких зависимостей от сторонних плагинов.
Код из этой статьи можно скопировать и использовать в любом проекте. Если нужны дополнительные поля — просто добавьте их в HTML и обработку в PHP.
Читайте также:
Добавить комментарий