Лёгкая headless CMS на PHP, которую можно развернуть одним файлом на обычном shared-хостинге.
Mini Headless CMS — компактная CMS для сайтов, лендингов, каталогов и небольших веб-проектов. Административная панель, схема контента, формы, REST-подобный JSON API и системные инструменты находятся в одном PHP-файле.
Проект ориентирован на разработчиков, которым нужна простая CMS без Composer, Node.js, отдельного backend-приложения и обязательного VPS.
- Основные возможности
- Системные требования
- Установка
- Структура файлов
- Роли и права
- Модель контента
- API
- Публичные формы
- Безопасность
- Резервные копии
- Диагностика
- Ограничения
- Автор
- основная CMS поставляется одним PHP-файлом;
- не требует Composer и сборки frontend;
- работает с SQLite или MySQL;
- автоматически создаёт конфигурацию, таблицы, служебные каталоги и индексы;
- включает мастер первоначальной настройки;
- подходит для обычного PHP-хостинга.
- несколько независимых проектов в одной установке;
- разделы контента, объединяющие несколько коллекций;
- глобальные и вложенные коллекции;
- коллекции типов
SingleиMultiple; - один уровень вложенных коллекций;
- готовые пресеты: страница, блог, товар, FAQ и контакты;
- черновики и опубликованные записи;
- автоматическое сохранение редактора;
- история версий и восстановление предыдущего состояния;
- клонирование коллекций;
- импорт и экспорт схем коллекций;
- избранные коллекции и список недавних записей.
- Text;
- текст без перевода;
- Textarea;
- HTML;
- Email;
- Телефон;
- Number и Integer;
- Date и Datetime;
- Boolean;
- URL;
- Image и File;
- JSON;
- маркированные и нумерованные списки;
- переводимые списки;
- связь с глобальной коллекцией;
- связь с вложенной коллекцией.
Для полей поддерживаются обязательность, значения по умолчанию, минимальная и максимальная длина, числовые границы, регулярные выражения, список разрешённых значений и проверка уникальности.
- интерфейс CMS на русском, казахском и английском языках;
- одноязычный и мультиязычный режим контента;
- поддержка 15 языков контента;
- отдельные переводы для Text, Textarea и переводимых списков;
- переводы названий полей форм и сообщений после успешной отправки;
- получение нужного языка через параметр API
lang.
- единый файловый менеджер проекта;
- изображения и документы;
- контроль расширений и MIME-типа;
- максимальный размер файла по умолчанию — 10 МБ;
- отслеживание используемых и неиспользуемых файлов;
- корзина проекта и глобальная корзина;
- очистка неиспользуемых файлов.
- создание публичных endpoint-форм;
- получение JSON-схемы формы через
GET; - отправка через JSON,
application/x-www-form-urlencodedилиmultipart/form-data; - серверная валидация типов и правил;
- защита honeypot и ограничение частоты запросов;
- статусы заявок: новая, прочитанная, спам;
- массовые операции;
- экспорт заявок в CSV и JSON;
- автоматическое хранение заявок: бессрочно, 30, 90, 180 или 365 дней;
- уведомления на email;
- подписанный webhook;
- уведомления в Telegram.
- четыре роли пользователей;
- права на уровне проекта;
- журнал действий;
- встроенная диагностика установки;
- резервное копирование и восстановление проектов;
- настройка CORS;
- светлая и тёмная темы;
- API Explorer;
- режим отладки для разработки.
- PHP 8.1 или новее;
- PDO;
- JSON;
- OpenSSL;
- Fileinfo;
- Mbstring;
- один из драйверов:
pdo_sqlite;pdo_mysql;
- права на запись в каталоги
storageиuploads.
ZipArchive— резервное копирование и восстановление;- cURL или разрешённый
file_get_contents()для внешних URL — webhooks и Telegram; - настроенная функция
mail()— email-уведомления; - HTTPS — настоятельно рекомендуется для рабочей установки.
Для загрузки файлов размером до 10 МБ значения upload_max_filesize и post_max_size в php.ini должны быть не меньше 10 МБ.
Скопируйте основной PHP-файл проекта в отдельный каталог сайта. При необходимости переименуйте его в index.php.
Пример:
public_html/
└── cms/
└── index.php
PHP должен иметь возможность создавать и изменять:
storage/
uploads/
Обычно достаточно прав 775 для каталогов. Не устанавливайте 777, если хостинг не требует этого явно.
https://example.com/cms/
Мастер установки предложит:
- выбрать язык и тему интерфейса;
- включить или отключить мультиязычный контент;
- выбрать SQLite или MySQL;
- проверить подключение к базе данных;
- создать первого администратора.
SQLite выбран по умолчанию. Файл базы создаётся автоматически:
storage/cms.sqlite
База данных должна быть создана заранее. В мастере установки укажите:
- host;
- database;
- username;
- password.
Таблицы и индексы CMS создаст самостоятельно.
После первого запуска структура будет выглядеть примерно так:
cms/
├── index.php
├── storage/
│ ├── .htaccess
│ ├── index.html
│ ├── config.json
│ ├── cms.sqlite
│ ├── login_attempts.json
│ └── cache/
└── uploads/
При использовании MySQL файл cms.sqlite не нужен.
storage/config.json содержит настройки подключения и установки. Не публикуйте его и не добавляйте в открытый репозиторий вместе с реальными учётными данными.
CMS автоматически создаёт .htaccess для Apache. Nginx не обрабатывает .htaccess, поэтому доступ нужно запретить в конфигурации сервера:
location ^~ /cms/storage/ {
deny all;
return 404;
}| Роль | Возможности |
|---|---|
| Administrator | Полный доступ ко всем проектам, пользователям, журналу, диагностике и системным операциям |
| Developer | Схемы коллекций, разделы, настройки, API, ключи доступа и конфигурация форм |
| Editor | Записи, файлы, публикация контента и обработка заявок форм |
| Viewer | Просмотр контента без права изменения |
Администратор является глобальной ролью. Для остальных пользователей роль назначается отдельно в каждом проекте.
Проект
├── Раздел контента
│ ├── Коллекция
│ └── Коллекция
├── Коллекция без раздела
├── Вложенная коллекция
├── Формы
└── Файлы
Одна коллекция может быть подключена к нескольким разделам. Удаление связи с разделом не удаляет саму коллекцию.
Вложенная коллекция принадлежит родительской коллекции, хранит отдельные записи для каждой родительской записи и поддерживает только один уровень вложенности.
Текущая версия API: 1.4.
API отдаёт только опубликованный контент. Изменение контента выполняется через административную панель.
GET https://example.com/cms/?api=index&project=my-projectПараметр project принимает slug проекта. В установке с одним проектом может использоваться проект по умолчанию, однако явное указание slug делает интеграцию предсказуемее.
| Endpoint | Назначение |
|---|---|
?api=index&project=PROJECT |
Информация об API и доступных маршрутах |
?api=entries&project=PROJECT&c=COLLECTION |
Опубликованные записи коллекции |
?api=entry&project=PROJECT&c=COLLECTION&s=ENTRY |
Одна запись по slug |
?api=group&project=PROJECT&g=GROUP |
Раздел вместе с подключёнными коллекциями |
?api=collections&project=PROJECT |
Список коллекций для авторизованного Developer/Admin |
?api=groups&project=PROJECT |
Список разделов для авторизованного Developer/Admin |
?api=fields&project=PROJECT&c=COLLECTION |
Поля коллекции для авторизованного Developer/Admin |
?api=schema&project=PROJECT&c=COLLECTION |
Схема коллекции для авторизованного Developer/Admin |
?api=files&project=PROJECT |
Файлы проекта для авторизованного Editor/Admin |
?api=files-trash&project=PROJECT |
Корзина файлов для авторизованного Editor/Admin |
Поддерживаются методы GET, HEAD и OPTIONS.
| Параметр | Пример | Описание |
|---|---|---|
lang |
lang=kk |
Язык ответа: код языка, default или all |
page |
page=2 |
Номер страницы |
limit |
limit=25 |
Размер страницы, максимум 100 |
q |
q=automation |
Полнотекстовый поиск по сериализованным данным ответа |
sort |
sort=-created_at |
Сортировка; префикс - означает обратный порядок |
filter |
filter[status]=active |
Фильтрация по системному полю или data.* |
fields |
fields=title,slug,data.price |
Проекция полей ответа |
populate |
populate=author,category |
Раскрытие указанных связей |
populate |
populate=all |
Раскрытие всех связей первого уровня |
schema |
schema=1 |
Добавление схемы; требует авторизации в CMS |
Примеры:
GET /cms/?api=entries&project=site&c=articles&lang=ru&page=1&limit=10&sort=-created_atGET /cms/?api=entries&project=shop&c=products&filter[data.category]=tools&fields=title,slug,data.priceGET /cms/?api=entry&project=site&c=articles&s=first-post&populate=allКоллекцию, раздел или форму можно сделать приватными. Ключ создаётся в административной панели и показывается полностью только один раз.
Передайте его одним из способов:
Authorization: Bearer cms_your_keyили:
X-API-Key: cms_your_keyПример:
curl \
-H "Authorization: Bearer cms_your_key" \
"https://example.com/cms/?api=entries&project=site&c=private-content"Ключи можно именовать, ограничивать по сроку действия и отзывать. В базе хранится хеш ключа, а не его открытое значение.
Публичные ответы используют:
ETag;Last-Modified;- условные запросы с ответом
304 Not Modified; - файловый response cache;
- автоматическую инвалидацию при изменении контента.
Приватные ответы возвращаются с Cache-Control: private, no-store.
Вложенные коллекции не публикуются как самостоятельные endpoint-ы и не добавляются в ответы автоматически. Они доступны через поле типа nested_relation и параметр populate.
GET https://example.com/cms/?form=contact&project=site&lang=ruОтвет содержит:
- данные формы;
- локализованное название;
- сообщение об успешной отправке;
- список полей и правила валидации;
- URL endpoint-а;
- поддерживаемые форматы запроса.
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"name": "Иван",
"email": "ivan@example.com",
"message": "Нужна консультация"
}' \
"https://example.com/cms/?form=contact&project=site&lang=ru"Успешный ответ возвращает HTTP 201 Created:
{
"api_version": "1.4",
"ok": true,
"lang": "ru",
"submission_id": 42,
"message": "Спасибо! Заявка отправлена."
}Формы не принимают файлы. Публичный endpoint сохраняет IP, User-Agent и Referer заявки.
- строгая серверная валидация;
- отклонение неизвестных или некорректных типов данных;
- honeypot для автоматического определения спама;
- не более 20 заявок с одного IP за 10 минут для одной формы;
- ограничение размера и глубины JSON;
- публичный CORS согласно настройкам проекта;
- HMAC-подпись webhook-запросов в заголовке
X-CMS-Signature.
Webhook получает событие form.submitted и заголовок:
X-CMS-Signature: sha256=...В CMS реализованы:
- пароли через
password_hash()иpassword_verify(); - минимальная длина пароля 10 символов;
- CSRF-токены для административных POST-запросов;
- строгий режим PHP-сессий;
- cookies с
HttpOnlyиSameSite=Lax; Secureдля session cookie при работе через HTTPS;- регенерация session ID после входа;
- блокировка входа после 5 неудачных попыток на 10 минут;
- prepared statements через PDO;
- серверная валидация контента и форм;
- очистка пользовательского HTML;
- CORS allowlist на уровне проекта;
- отдельные API-ключи для коллекций, разделов и форм;
- срок действия и отзыв API-ключей;
- журнал действий с пользователем, IP и User-Agent;
- защитные HTTP-заголовки;
- автоматическая защита каталога
storageдля Apache; - HMAC SHA-256 для webhook-уведомлений.
Не включайте режим отладки на рабочем сайте: PHP-ошибки могут раскрыть пути, конфигурацию и внутренние данные приложения.
CMS создаёт ZIP-архив проекта, который включает:
- проект;
- коллекции и вложенные коллекции;
- поля;
- записи;
- разделы и связи;
- формы и поля форм;
- заявки форм;
- файлы проекта.
При восстановлении создаётся новый проект, а идентификаторы коллекций, записей, файлов и связей переназначаются автоматически.
Для функции требуется расширение ZipArchive.
Храните резервные копии вне публичного каталога сайта.
Встроенная страница диагностики проверяет:
- версию PHP;
- необходимые расширения;
- выбранный драйвер базы данных;
- наличие ZipArchive;
- доступность записи в
storageиuploads; - наличие
config.json; - использование HTTPS;
- защиту каталога
storage; - значения
upload_max_filesizeиpost_max_size; - доступность
mail().
- CMS рассчитана прежде всего на небольшие и средние проекты;
- публичный контентный API доступен только для чтения;
- вложенность коллекций ограничена одним уровнем;
- связи раскрываются на один уровень;
- публичные формы не поддерживают загрузку файлов;
- административный интерфейс загружает Bootstrap и Bootstrap Icons через jsDelivr CDN;
- email-уведомления зависят от конфигурации
mail()на сервере; - для Nginx защиту
storageнужно настроить вручную; - single-file архитектура упрощает установку, но требует осторожности при ручном изменении исходного файла.
Перед заменой PHP-файла:
- скачайте резервные копии проектов;
- сохраните каталог
storage; - сохраните каталог
uploads; - замените основной PHP-файл;
- откройте CMS — миграции схемы выполнятся автоматически;
- проверьте страницу диагностики.
Не удаляйте storage/config.json, базу данных и uploads во время обновления.
Проект не требует отдельной сборки:
PHP 8.1+
HTML5
CSS3
Vanilla JavaScript
Bootstrap 5.3.3
Bootstrap Icons 1.11.3
PDO
SQLite / MySQL
Для локального запуска можно использовать встроенный сервер PHP:
php -S localhost:8000После запуска откройте:
http://localhost:8000/
Темирхан Рустемов
DreamerView
GitHub: DreamerView/mini-cms
Mini Headless CMS создаётся как практичный инструмент для разработчиков: минимум требований к серверу, предсказуемая структура данных и быстрый JSON API без лишней инфраструктуры.