← Назад в блог

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

CPT не отображается в REST API и даёт 404? Пошагово включаем show_in_rest и rest_base, проверяем маршруты и устраняем типичные ошибки. WordPress 6.x, PHP 8.1+.

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

Требования

  • 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


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

Симптом

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

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


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

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

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

  1. Посмотреть список типов в REST (сниппет «curl: проверка types и endpoint»):
curl -s https://example.com/wp-json/wp/v2/types | head
  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);

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

0 просмотров

Комментарии

Загрузка комментариев...
Пока нет комментариев. Будьте первым!