Как включить CPT в REST API WordPress: /wp/v2 отдаёт 404
CPT не отображается в REST API и даёт 404? Пошагово включаем show_in_rest и rest_base, проверяем маршруты и устраняем типичные ошибки. WordPress 6.x, PHP 8.1+.
Требования
- WordPress 6.x
- PHP 8.1+
- Доступ к коду темы или плагина
Как включить CPT в REST API WordPress: /wp/v2 отдаёт 404
Если у вас wordpress custom post type rest api not working, почти всегда это выглядит одинаково: CPT зарегистрирован, в админке есть, а запрос GET /wp-json/wp/v2/{post_type} возвращает 404. В конце получится нормальная регистрация CPT с REST-маршрутом в wp/v2, рабочим rest_base и поддержкой редактора блоков (Gutenberg). Опираемся только на параметры register_post_type() из официальной документации. (WordPress Developer Resources)
Сниппеты по статье: CPT с REST в теме (functions.php) · CPT с REST в mu-plugin · Диагностика CPT и REST (get_post_type_object) · curl: проверка types и endpoint
В чём проблема
Симптом
- CPT виден в админке (меню, список записей есть).
- В REST API endpoint отсутствует:
curl -i https://example.com/wp-json/wp/v2/items
Ожидаемо при проблеме:
HTTP/2 404
{"code":"rest_no_route","message":"No route was found matching the URL and request method.","data":{"status":404}}
Почему так происходит
WordPress создаёт REST-маршруты для типа записи только если при регистрации CPT указано:
show_in_rest => true
Это прямо описано в официальной документации REST API (раздел про добавление поддержки REST для кастомных типов). (WordPress Developer Resources)
Параметры rest_base и rest_controller_class — тоже часть register_post_type() и управляют URL и контроллером маршрутов. (WordPress Developer Resources)
По умолчанию show_in_rest в WordPress установлен в false: так не все типы записей автоматически попадают в API, что сохраняет обратную совместимость и снижает риск случайного раскрытия данных. Для CPT, который должен быть доступен через REST (и в редакторе блоков), параметр нужно явно включить.
Полезные первоисточники:
register_post_type()— полный список аргументов: https://developer.wordpress.org/reference/functions/register_post_type/ (WordPress Developer Resources)- REST API: поддержка кастомных типов: https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-rest-api-support-for-custom-content-types/ (WordPress Developer Resources)
- Справочник с примерами (допустимый вторичный источник): wp-kama.ru: register_post_type (WordPress at Your Fingertips)
Рабочее решение
Ниже — минимальный «боевой» вариант, который:
- добавит маршрут в
wp/v2, - задаст человеко-понятный endpoint (
rest_base), - использует стандартный контроллер WordPress,
- включит Gutenberg (через
show_in_rest+supports => editor).
Код ниже рассчитан на PHP 8.1+ и использует declare(strict_types=1) в соответствии с современной практикой (PSR, строгая типизация). Контроллер WP_REST_Posts_Controller::class — стандартный класс ядра WordPress, менять его не требуется. Оба варианта используют только API ядра и не требуют сторонних плагинов.
Вариант 1: в теме (быстро, но не идеально для поддержки)
Готовый вариант с пояснениями: сниппет «CPT с REST API в теме».
Путь: wp-content/themes/your-theme/functions.php
<?php
declare(strict_types=1);
add_action('init', static function (): void {
$labels = [
'name' => __('Items', 'your-textdomain'),
'singular_name' => __('Item', 'your-textdomain'),
];
register_post_type('item', [
'labels' => $labels,
'public' => true,
'has_archive' => true,
// Gutenberg и REST
'show_in_rest' => true,
'rest_base' => 'items',
'rest_controller_class' => WP_REST_Posts_Controller::class,
// Чтобы редактор блоков был доступен
'supports' => ['title', 'editor', 'excerpt', 'thumbnail'],
]);
});
Аргументы show_in_rest, rest_base, rest_controller_class официально поддерживаются register_post_type(). (WordPress Developer Resources)
Вариант 2: mu-plugin (лучше для prod)
Тема может меняться, а CPT — обычно часть модели данных. Поэтому для продакшена чаще кладут в mu-plugin. Готовый код: сниппет «CPT с REST API в mu-plugin».
Путь: wp-content/mu-plugins/cpt-item.php
(если папки mu-plugins нет — создайте)
<?php
/**
* Plugin Name: MU: Item CPT (REST enabled)
*/
declare(strict_types=1);
add_action('init', static function (): void {
register_post_type('item', [
'label' => 'Items',
'public' => true,
'show_in_rest' => true,
'rest_base' => 'items',
'rest_controller_class' => WP_REST_Posts_Controller::class,
'supports' => ['title', 'editor'],
]);
}, 10);
Диагностика: убедиться, что WordPress реально зарегистрировал REST
- Посмотреть список типов в REST (сниппет «curl: проверка types и endpoint»):
curl -s https://example.com/wp-json/wp/v2/types | head
- Найти свой тип и проверить
rest_base/show_in_rest. Удобно через небольшой debug-код (временно). Готовый mu-plugin: сниппет «Диагностика CPT и REST».
Путь: wp-content/mu-plugins/_debug-cpt.php
<?php
declare(strict_types=1);
add_action('init', static function (): void {
$obj = get_post_type_object('item');
if (!$obj) {
error_log('CPT item: NOT registered');
return;
}
error_log('CPT item: show_in_rest=' . (int) $obj->show_in_rest);
error_log('CPT item: rest_base=' . (string) $obj->rest_base);
error_log('CPT item: rest_controller_class=' . (string) $obj->rest_controller_class);
}, 999);
Если show_in_rest=0, маршрута в wp/v2 не будет — это норма по логике WordPress REST API. (WordPress Developer Resources)
Проверка результата
Проверка endpoint
После деплоя кода выполните запрос к вашему домену (подставьте свой URL вместо example.com), например:
curl -i https://example.com/wp-json/wp/v2/items
Ожидаем:
HTTP 200- JSON-массив записей (или пустой массив
[], если пока нет элементов)
В ответе должны быть поля в формате REST API WordPress: id, date, slug, title, content, excerpt и т.д. — в зависимости от того, что поддерживает ваш CPT. Если приходит 404, снова проверьте rest_base и наличие типа в /wp-json/wp/v2/types.
Проверка, что маршрут существует
Если вы не уверены, что именно создаётся, откройте в браузере:
https://example.com/wp-json/— индекс APIhttps://example.com/wp-json/wp/v2/types— типы, которые «видны» REST API (WordPress Developer Resources)
Проверка Gutenberg
Если CPT поддерживает editor и включён show_in_rest, то редактор блоков для него доступен: это стандартное требование редактора, так как он получает и сохраняет данные через REST API. Связь параметра show_in_rest и работы Block Editor описана в официальной документации. (Block Editor Handbook)
Типичные ошибки
1) ❌ show_in_rest => true добавили, но всё равно 404
Причина: код регистрации не выполняется (не тот файл, условие, порядок хуков, фатал до init).
Как исправить:
- перенесите регистрацию в
mu-plugins(самый надёжный вариант), - убедитесь, что CPT регистрируется на
init, - временно залогируйте
get_post_type_object()как в диагностике выше.
Официально REST-маршрут появляется только при show_in_rest=true. (WordPress Developer Resources)
2) ❌ Endpoint другой, чем вы ожидаете
Причина: вы обращаетесь к /wp/v2/item, а rest_base не задан или задан иначе.
Как исправить:
- либо используйте дефолт (обычно равен имени пост-типа),
- либо явно задайте
rest_base => 'items'и обращайтесь к/wp-json/wp/v2/items. (WordPress Developer Resources)
3) ❌ REST работает, но Gutenberg не появляется (или падает)
Причина: CPT не поддерживает нужные фичи (supports) или отключён UI/редактор.
Как исправить:
- минимум:
supports => ['title', 'editor'] - проверьте, что
show_uiне отключён, и CPT публичный/управляемый.
supports — часть аргументов register_post_type() и напрямую влияет на доступность редактора. (WordPress Developer Resources)
4) ❌ REST отдаёт 401/403 вместо 404
Причина: это уже не про маршрут, а про права/авторизацию или фильтры безопасности (rest_authentication_errors, плагины, WAF).
Как исправить (быстрый чек):
- проверьте запрос без авторизации на публичное чтение,
- временно отключите плагины безопасности,
- проверьте логи PHP/nginx и наличие кастомных фильтров на REST.
5) ❌ После смены кода маршрут по-прежнему не появляется
Причина: кэш объекта типов записей или необходимость сброса постоянных ссылок. В некоторых конфигурациях WordPress кэширует список типов при первом обращении к REST.
Как исправить: откройте в админке «Настройки → Постоянные ссылки» и нажмите «Сохранить» без изменений — это пересоздаёт правила. Убедитесь, что регистрация CPT выполняется на хуке init и ничто не отключает REST API для типа записи позже (фильтры rest_prepare_*, register_rest_route и т.п.).
Где применять
- prod / dev: решение одинаково применимо в обеих средах. В продакшене обязательно выносите CPT в
mu-pluginsили отдельный плагин, чтобы смена темы не ломала REST API. В разработке удобно держать регистрацию в теме для быстрых правок. - nginx / Apache: поведение REST API от веб-сервера не зависит — важно лишь корректно проксировать запросы к PHP (например,
try_filesв nginx не должен перехватывать/wp-json/*). - docker / CI/CD: удобно проверять наличие маршрута
curl-ом после деплоя (smoke-тест):curl -f https://site/wp-json/wp/v2/items. - BitrixVM тут ни при чём, но подход тот же: конфиг должен быть повторяемым и не зависеть от «ручных правок в теме».
Сниппеты
Практичные фрагменты по теме статьи (официальные источники: developer.wordpress.org, wp-kama.ru):
- CPT с REST API в теме (functions.php) — минимальная регистрация с
show_in_rest,rest_base,WP_REST_Posts_Controllerдля вставки в тему. - CPT с REST API в mu-plugin — тот же вариант для
wp-content/mu-plugins; не зависит от темы, подходит для prod. - Диагностика CPT и REST (get_post_type_object) — временный mu-plugin: логирование
show_in_rest,rest_base,rest_controller_classв debug.log для поиска причины 404. - curl: проверка types и endpoint — команды для проверки
/wp-json/wp/v2/typesи запроса к endpoint кастомного типа (HTTP-код и тело ответа).



Комментарии