DEV Community

Андрей Викулов (VProger)
Андрей Викулов (VProger)

Posted on • Originally published at viku-lov.ru on

Как включить CPT в REST API WordPress: /wp/v2 отдаёт 404

Как включить CPT в REST API WordPress: /wp/v2 отдаёт 404

Как включить 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


В чём проблема

Симптом

  1. CPT виден в админке (меню, список записей есть).
  2. В REST API endpoint отсутствует:

curl -i https://example.com/wp-json/wp/v2/items

Enter fullscreen mode Exit fullscreen mode

Ожидаемо при проблеме:


HTTP/2 404

{"code":"rest\_no\_route","message":"No route was found matching the URL and request method.","data":{"status":404}}

Enter fullscreen mode Exit fullscreen mode

Почему так происходит

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 (и в редакторе блоков), параметр нужно явно включить.

Полезные первоисточники:


Рабочее решение

Ниже — минимальный «боевой» вариант, который:

  • добавит маршрут в 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'],

]);

});

Enter fullscreen mode Exit fullscreen mode

Аргументы 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);

Enter fullscreen mode Exit fullscreen mode

Диагностика: убедиться, что WordPress реально зарегистрировал REST

  1. Посмотреть список типов в REST (сниппет «curl: проверка types и endpoint»):

curl -s https://example.com/wp-json/wp/v2/types | head

Enter fullscreen mode Exit fullscreen mode
  1. Найти свой тип и проверить 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);

Enter fullscreen mode Exit fullscreen mode

Если 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

Enter fullscreen mode Exit fullscreen mode

Ожидаем:

  • HTTP 200
  • JSON-массив записей (или пустой массив [], если пока нет элементов)

В ответе должны быть поля в формате REST API WordPress: id, date, slug, title, content, excerpt и т.д. — в зависимости от того, что поддерживает ваш CPT. Если приходит 404, снова проверьте rest_base и наличие типа в /wp-json/wp/v2/types.

Проверка, что маршрут существует

Если вы не уверены, что именно создаётся, откройте в браузере:

Проверка 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-код и тело ответа).

Read more on viku-lov.ru

Top comments (0)