Сценарий знакомый: сайт стал быстрее после включения кэширования, но часть интерфейса внезапно ломается. Кнопки не работают, фильтры в каталоге WooCommerce молчат, форма отправки крутится бесконечно, а в консоли браузера видно 404 или 403 на запросах к /wp-admin/admin-ajax.php. Обычно проблема не в самом AJAX, а в том, как кэш, сервер или правила безопасности обрабатывают этот путь.
Что именно ломается и где искать причину
Первый шаг — не править код наугад, а понять, кто возвращает ошибку. Если запрос уходит на admin-ajax.php, но в ответе 404 Not Found, чаще всего виноваты:
- страничный кэш, который пытается кэшировать или переписывать AJAX-запрос;
- правила Nginx/Apache, которые режут доступ к
wp-adminили конкретно кadmin-ajax.php; - плагин безопасности, блокирующий запросы по
action; - неверный URL в JavaScript после переноса сайта или смены домена;
- конфликт с оптимизацией JS, когда скрипт отправляет запрос не туда или слишком рано.
Как быстро диагностировать проблему
Откройте DevTools в браузере, вкладку Network, повторите действие и посмотрите сам запрос. Важно не только код ответа, но и адрес:
- если URL ведет на правильный домен, но ответ
404— смотрим сервер и кэш; - если URL неправильный или относительный — проблема в JavaScript;
- если ответ
403— вероятнее всего, блокировка на уровне безопасности или WAF; - если ответ
200, но действие не срабатывает — тогда уже проверяем PHP-обработчик.
Полезно также открыть сам URL в браузере: /wp-admin/admin-ajax.php. В норме без параметров WordPress обычно отвечает строкой 0, а не 404. Если сразу 404 — путь не доходит до WordPress или перехватывается сервером.
Пошаговое решение: убрать 404 на admin-ajax.php
1. Исключите admin-ajax.php из кэширования
Для большинства сайтов это обязательный шаг. Страничный кэш не должен хранить ответы AJAX-запросов. В настройках кэширующего плагина добавьте исключение для /wp-admin/admin-ajax.php. Если используется серверный кэш или reverse proxy, проверьте аналогичные правила там.
Если у вас есть доступ к конфигурации Nginx, убедитесь, что запросы к admin-ajax.php не попадают под агрессивные location-правила. Пример безопасного исключения:
location = /wp-admin/admin-ajax.php {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php-fpm;
fastcgi_no_cache 1;
fastcgi_cache_bypass 1;
}Для Apache обычно достаточно не добавлять этот путь в правила кэширования и не закрывать wp-admin целиком без исключений.
2. Проверьте, не ломает ли оптимизация JavaScript адрес AJAX
Если вы объединяете, откладываете или минифицируете JS, убедитесь, что скрипт получает корректный URL. В WordPress правильнее передавать его через wp_localize_script() или wp_add_inline_script(), а не хардкодить относительный путь.
<?php
add_action('wp_enqueue_scripts', function () {
wp_enqueue_script(
'theme-ajax',
get_stylesheet_directory_uri() . '/assets/js/theme-ajax.js',
array('jquery'),
'1.0.0',
true
);
wp_localize_script('theme-ajax', 'themeAjax', array(
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('theme_ajax_nonce'),
));
});В JS используйте именно переданный URL:
jQuery(function ($) {
$('#load-more').on('click', function () {
$.post(themeAjax.ajaxUrl, {
action: 'load_more_posts',
nonce: themeAjax.nonce,
page: 2
}).done(function (response) {
console.log(response);
});
});
});3. Добавьте обработчик AJAX и nonce-проверку
Если запрос доходит до WordPress, но ответ пустой или 0, проверьте, зарегистрирован ли хук. Для авторизованных и неавторизованных пользователей нужны разные действия, если сценарий доступен всем.
<?php
add_action('wp_ajax_load_more_posts', 'theme_load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'theme_load_more_posts');
function theme_load_more_posts() {
check_ajax_referer('theme_ajax_nonce', 'nonce');
$page = isset($_POST['page']) ? absint($_POST['page']) : 1;
$query = new WP_Query(array(
'post_type' => 'post',
'posts_per_page' => 6,
'paged' => $page,
));
ob_start();
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
get_template_part('template-parts/content', get_post_type());
}
wp_reset_postdata();
}
wp_send_json_success(array(
'html' => ob_get_clean(),
'maxPages' => (int) $query->max_num_pages,
));
}Если nonce не нужен для публичного действия, его можно не проверять, но для форм и пользовательских операций это лучше не пропускать.
4. Проверьте правила безопасности и WAF
Плагины безопасности иногда блокируют admin-ajax.php, если видят подозрительный action или слишком частые запросы. В логах это обычно видно как 403 или как запись о блокировке IP. Если защита включена, добавьте исключение только для нужного действия, а не для всего AJAX.
Если на сервере стоит ModSecurity или аналогичный WAF, посмотрите его логи. Блокировка может быть вызвана не WordPress, а правилом на уровне хостинга.
Как понять, что исправление сработало
Проверка должна быть не визуальной, а технической. После изменений сделайте три вещи:
- Очистите кэш страницы, объектный кэш и CDN, если он есть.
- Повторите проблемное действие в браузере с открытой вкладкой Network.
- Убедитесь, что запрос к
admin-ajax.phpвозвращает200, а не404или403.
Дополнительно проверьте тело ответа. Для успешного AJAX-обработчика WordPress обычно возвращает JSON через wp_send_json_success() или wp_send_json_error(). Если вы видите HTML страницы ошибки, значит запрос уходит не туда или перехватывается до WordPress.
Сравнение подходов: плагин, сервер, код
| Подход | Когда подходит | Плюсы | Минусы |
|---|---|---|---|
| Настройка кэша в плагине | Если проблема появилась после включения page cache | Быстро, без правки кода | Не решает серверные блокировки |
| Правка конфигурации Nginx/Apache | Если 404 идет с сервера | Точный контроль над маршрутизацией | Нужен доступ к конфигу и понимание правил |
| Исправление JS и PHP-обработчика | Если URL неверный или хук не зарегистрирован | Устраняет первопричину | Требует проверки темы или плагина |
Частые ошибки и как их исправить
Хардкод /wp-admin/admin-ajax.php в JS
После переноса сайта, установки в подкаталог или изменения домена такой путь может вести не туда. Используйте admin_url('admin-ajax.php') и передавайте его в скрипт.
Кэшируется HTML-страница, а не сам AJAX
Иногда кажется, что виноват AJAX, но на деле кэшируется страница с устаревшим JS, который отправляет старый URL или старый nonce. Очистите кэш страницы и проверьте, что на фронтенде загружается актуальная версия скрипта.
Зарегистрирован только wp_ajax_, но нет wp_ajax_nopriv_
Если действие должно работать для гостей, без второй регистрации WordPress вернет ошибку или пустой ответ. Для публичных сценариев нужны оба хука.
Плагин безопасности режет запрос по nonce или action
Это типично для форм, фильтров и поиска. Не отключайте защиту целиком. Лучше добавить исключение для конкретного действия и проверить, не слишком ли агрессивны лимиты запросов.
Практические советы по безопасности и производительности
AJAX в WordPress часто используют для фильтров, подгрузки товаров и отправки форм. Чтобы не создать новую проблему после исправления старой, держите в голове несколько правил:
- не отдавайте через AJAX тяжелые запросы без необходимости;
- для публичных действий ограничивайте объем данных и проверяйте входные параметры через
absint(),sanitize_text_field()и аналогичные функции; - для чувствительных операций используйте
check_ajax_referer(); - не кэшируйте ответы, которые зависят от пользователя, корзины или сессии WooCommerce;
- если запросов много, посмотрите, нельзя ли заменить часть логики на REST API или серверную пагинацию.
Если на сайте много технических дублей, лишних скриптов и мусорных настроек, имеет смысл отдельно пройтись по чистке фронтенда и SEO-обвязки. В таких задачах полезны инструменты вроде Clearfy Pro, но только как средство упорядочить настройки, а не как замена диагностике.
Мини-чек-лист перед публикацией правки
- URL AJAX берется через
admin_url('admin-ajax.php'). - На сервере нет правила, которое отдает
404дляadmin-ajax.php. - Плагин кэша исключает этот путь из page cache.
- Есть нужные хуки
wp_ajax_...и, если нужно,wp_ajax_nopriv_.... - Nonce проверяется для защищенных действий.
- В DevTools запрос возвращает
200и ожидаемый JSON.
Если после всех правок 404 остается, смотрите не WordPress, а маршрут на уровне веб-сервера и логи ошибок PHP. В таких случаях проблема обычно находится не в AJAX-обработчике, а в том, как запрос до него вообще доходит.