Как включить 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/ — индекс API
- https://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-код и тело ответа).

Top comments (0)