Laravel

Статьи на сайт Laravel без ручной работы

Добавьте маршрут в routes/api.php и контроллер, который проверяет подпись и сохраняет статью через updateOrCreate по идентификатору из нашего сервиса. Так повторная доставка обновит запись, а не создаст вторую.

Публикация на произвольный сайт входит в тарифы от Starter.

31
шаг в лонгрид-пайплайне
~5 мин
на стандартную статью
5–8 тыс.
слов в лонгрид-режиме
4
соцсети автоматически
Схема: SEO Writer отправляет готовую статью POST-запросом на обработчик вашего сайта

Как это работает

1

Добавьте контроллер и маршрут

app/Http/Controllers/SeoWriterController.php и строка в routes/api.php. Маршруты api-группы не требуют csrf-токена, это нам и нужно.

2

Пропишите ключ

SEOWRITER_SECRET в .env, затем php artisan config:clear, если кэшируете конфиг.

3

Свяжите с моделью

Добавьте в таблицу публикаций колонку seowriter_id с уникальным индексом — по ней и ищется запись.

Обработчик для Laravel

Тело берётся через $request->getContent() — это сырые байты запроса. Подписывается строка «время.тело запроса», алгоритм HMAC-SHA256. Время внутри подписи не даёт переиграть перехваченный запрос: отвергайте всё старше пяти минут. Подписей в заголовке может быть несколько через запятую — это сутки после смены ключа, проверять нужно каждую.

app/Http/Controllers/SeoWriterController.php
// routes/api.php
Route::post('/seowriter', [SeoWriterController::class, 'handle']);

// app/Http/Controllers/SeoWriterController.php
public function handle(Request $request): JsonResponse
{
    $raw = $request->getContent();
    $timestamp = $request->header('X-SEOWriter-Timestamp', '');
    $header = $request->header('X-SEOWriter-Signature', '');
    $event = $request->header('X-SEOWriter-Event', '');

    if (abs(time() - (int) $timestamp) > 300) {
        return response()->json(['ok' => false, 'error' => 'stale'], 401);
    }

    $expected = hash_hmac('sha256', $timestamp . '.' . $raw, env('SEOWRITER_SECRET'));
    $valid = false;
    foreach (explode(',', $header) as $part) {
        $part = trim($part);
        if (str_starts_with($part, 'v1=') && hash_equals($expected, substr($part, 3))) {
            $valid = true;
        }
    }
    if (!$valid) {
        return response()->json(['ok' => false], 401);
    }

    if ($event === 'ping') {
        return response()->json(['success' => true, 'cms' => 'Laravel ' . app()->version()]);
    }

    $article = json_decode($raw, true)['article'];

    // Ключ — seowriter_id: повторная доставка обновит запись, а не создаст вторую
    $post = Post::updateOrCreate(
        ['seowriter_id' => $article['id']],
        [
            'title'      => $article['h1'],
            'slug'       => $article['slug'],
            'body'       => $article['content_html'],
            'excerpt'    => $article['excerpt'],
            'meta_title' => $article['meta']['title'],
            'meta_desc'  => $article['meta']['description'],
            'cover_url'  => $article['cover']['url'] ?? null,
            'published'  => $article['status'] === 'publish',
        ]
    );

    // Верните url — от него зависят внутренняя перелинковка и анонсы в соцсетях
    return response()->json(['ok' => true, 'id' => $post->id, 'url' => route('posts.show', $post->slug)]);
}
Скачать файл целиком

Что приходит в каждой статье

Не только HTML: всё, что нужно странице, приходит отдельными полями — разбирать текст регулярками не придётся.

article.idПостоянный идентификатор статьи. Обновляйте запись по нему — тогда повторная доставка не создаст дубль
article.statusdraft или publish — публиковать сразу или оставить черновиком
article.title, h1, slugЗаголовок, H1 и ЧПУ статьи
article.content_htmlГотовый HTML статьи вместе с микроразметкой внутри
article.metaМета-заголовок и описание для тега title и meta description
article.coverОбложка: адрес, alt и признак того, постоянная ли ссылка
article.imagesКартинки из текста. У каждой помечено, лежит она у нас или на стороннем сервисе
article.faqВопросы и ответы — если рисуете свой блок FAQ, не разбирая HTML
article.json_ldБлоки микроразметки Schema.org отдельными объектами
article.categoryКатегория, если она задана в настройках сайта

Кому подходит доставка вебхуком

🧑‍💻

Разработчикам своих проектов

Сайт написан руками или собран на фреймворке, готовой интеграции для него не существует и не появится. Обработчик занимает тридцать строк и живёт в вашем репозитории вместе с остальным кодом.

🏢

Агентствам с разношёрстными клиентами

У одного клиента WordPress, у другого самописная админка, у третьего Next.js. Вебхук снимает вопрос «а поддерживаете ли вы такую CMS»: подходит любая, и все клиентские сайты ведутся из одного кабинета.

⚡

Headless-проектам и статике

Контент лежит в своей базе или в headless-CMS, а страницы собираются на деплое. Обработчик сохраняет статью и запускает пересборку — публикация появляется без ручного коммита.

Вручную и через вебхук

Статью всегда можно перенести руками — вопрос в том, сколько это стоит времени на дистанции.

ВручнуюЧерез вебхук
Перенос статьи на сайтКопирование текста, картинок и мета-тегов рукамиПриходит готовым за секунды после генерации
Время на одну статью20–30 минут на перенос и вёрсткуНоль: человек не участвует
Картинки и обложкаСкачать, загрузить, проставить altПриходят ссылками, обложка отдельным полем
Микроразметка и FAQСобирать вручную или забытьГотовые блоки Schema.org отдельными полями
Публикация по расписаниюНужен человек в нужный часИдёт сама, хоть ночью
Если сайт лежалСтатья ждёт, пока про неё вспомнятШесть повторов за восемь часов и журнал доставок

Безопасность

  • Каждый запрос подписан ключом, который знаете только вы и мы.
  • Время подписи не даёт переиграть перехваченный запрос: всё старше пяти минут отвергается.
  • Отправляем только по https и не идём по переадресациям.
  • Ключ можно перевыпустить: сутки после этого запросы подписаны старым и новым сразу, чтобы обработчик успел обновиться.

Поручите подключение своему ИИ

Скопируйте задание в Claude Code, Cursor или Codex прямо в своём проекте: в нём описан контракт без секретов, и ассистент встроит обработчик по правилам вашей кодовой базы.

Задание для ассистента
В моём проекте нужен серверный обработчик вебхука от сервиса SEO Writer.

Добавь POST-эндпоинт по адресу /api/seowriter, используя текущий стек проекта, его роутинг и ORM.
Не создавай отдельный микросервис, если в проекте уже есть серверная часть.

Запрос приходит с заголовками:
  X-SEOWriter-Event: article.published | article.updated | ping
  X-SEOWriter-Timestamp: unix-время в секундах
  X-SEOWriter-Signature: v1=<hex>[,v1=<hex>]
  X-SEOWriter-Delivery: идентификатор доставки, одинаковый на всех повторах

Тело запроса — JSON вида:
{ "event": "article.published",
  "article": { "id": "uuid", "status": "draft|publish", "title": "...", "h1": "...", "slug": "...",
               "content_html": "...", "excerpt": "...", "meta": {"title": "...", "description": "..."},
               "cover": {"url": "...", "alt": "..."}, "images": [{"url": "...", "alt": "..."}],
               "faq": [{"question": "...", "answer": "..."}], "json_ld": [...] } }

Реализуй по шагам:
1. Прочитай тело как сырые байты или текст ДО разбора JSON.
2. Посчитай HMAC-SHA256 от строки "{timestamp}.{сырое тело}" с ключом из переменной окружения SEOWRITER_SECRET.
3. Заголовок подписи может содержать несколько значений v1= через запятую — проверь КАЖДОЕ, любое совпадение считается верным. Сравнивай безопасным сравнением, не обычным равенством.
4. Отклони запрос кодом 401, если подпись не сошлась или если время отличается от текущего больше чем на 300 секунд.
5. На событие ping ответь 200 и JSON {"success": true}.
6. На article.published и article.updated сохрани статью в существующую модель публикаций проекта, обновляя запись по article.id — повторная доставка не должна создавать дубль. Для тела страницы используй content_html как готовый HTML.
7. Ответь 200 и JSON {"ok": true, "url": "<адрес опубликованной статьи>"} — адрес нужен сервису для внутренней перелинковки.
8. Отвечай быстро: тяжёлую обработку выноси в фоновую задачу.

Ограничения:
- не хардкодь секрет, читай его из переменной окружения;
- не отключай проверку подписи даже временно;
- не отвечай переадресацией;
- принимай только POST.

В конце скажи, какой полный адрес эндпоинта получится после деплоя и куда добавить SEOWRITER_SECRET.

Частые вопросы

Маршрут в web-группе — что нужно изменить?

Исключите его из проверки csrf: добавьте путь в $except у VerifyCsrfToken. Запрос приходит снаружи, токена у него нет и быть не может.

Обработка занимает много времени — как быть?

Верните ответ сразу, а работу отправьте в очередь: dispatch(new ProcessArticle($article)). Мы ждём ответа тридцать секунд, и это тот случай, когда лучше ответить быстро и доработать в фоне.

Как отличить новую статью от обновления?

По заголовку X-SEOWriter-Event: article.published приходит при первой публикации, article.updated при изменении — например, когда мы досылаем обложку. Обрабатывайте оба, иначе обложка не доедет.

Статья не проходит, сервер отвечает ошибкой 413

Это ограничение на размер тела запроса. Длинная статья со всей обвязкой весит несколько сотен килобайт, а в nginx по умолчанию стоит мегабайт. Поднимите client_max_body_size до 5m.

Что говорят пользователи

Реальные результаты от реальных людей

«Я веду футбольный блог уже более 5 лет и всегда сам писал или нанимал авторов по 2 000–3 000 рублей за статью. Попробовал SEO Writer скептически. Через месяц в индексе было 23 новых статьи, через три — трафик вырос на 60%. Сейчас плачу за тариф Starter и экономлю около 40 000 ₽ в месяц на текстах. Качество — реально не хуже хорошего копирайтера, структура правильная и логичная.»

Богдан
Богдан
Вебмастер
footbolno.ru

«Контент всегда был узким местом - клиентов берём быстро, а авторов под каждый проект искать долго. SEO Writer закрыл эту проблему полностью. Настроили 8 сайтов, каждый пишет по 3–4 статьи в неделю. Особенно ценим сервис за то, что Битрикс и InSales поддерживаются нативно, так как у половины клиентов именно они. Ни один западный инструмент этого не умеет.»

SEO-Ready
SEO-Ready
Агентство поискового продвижения сайтов
seo-ready.ru

«У нас магазин на Insales и товаров много, а блог стоял пустой три года. Подключили SEO Writer, за два месяца вышло 27 статей под информационные запросы. Сейчас из органики приходит около 30% новых клиентов, раньше использовали только контекст. Нравится, что статьи сами ставят внутренние ссылки на наши товары и категории, не нужно ничего делать руками.»

Александр И.К.
Александр И.К.
Владелец интернет-магазина квадрокоптеров

«Ваш сервис супер, поддержка отвал башки, спасибо!»

К
Клиент на тарифе Pro
Отзыв после обращения в поддержку

Тарифы

Начните бесплатно, масштабируйтесь по мере роста

Free

0₽/мес

  • 3 статей в месяц
  • 1 сайт
  • Системные API ключи
  • Подбор тем: 1 в месяц
  • Редактор статьи с историей версий
Начать бесплатно

Starter

1 290₽/мес

  • 15 статей в месяц
  • 2 сайта
  • Системные API ключи
  • Подбор тем: 4 в месяц
  • Редактор статьи с историей версий
  • Свои API ключи
  • Планировщик
  • Яндекс.Вебмастер в кабинете
  • «Дописать раздел»: 30 в месяц
  • «Улучшить статью» по запросам Вебмастера
  • Рерайт для сторонних площадок
  • Генерация обложек
Подключить
Популярный

Pro

2 990₽/мес

  • 50 статей в месяц
  • 5 сайтов
  • Системные API ключи
  • Подбор тем: 10 в месяц
  • Редактор статьи с историей версий
  • Свои API ключи
  • Планировщик
  • Яндекс.Вебмастер в кабинете
  • «Дописать раздел»: 100 в месяц
  • «Улучшить статью» по запросам Вебмастера
  • Рерайт для сторонних площадок
  • Генерация обложек
Подключить

Agency

9 990₽/мес

  • 150 статей в месяц
  • 20 сайтов
  • Системные API ключи
  • Подбор тем: 30 в месяц
  • Редактор статьи с историей версий
  • Свои API ключи
  • Планировщик
  • Яндекс.Вебмастер в кабинете
  • «Дописать раздел»: 300 в месяц
  • «Улучшить статью» по запросам Вебмастера
  • Рерайт для сторонних площадок
  • Генерация обложек
  • API доступ (скоро)
  • Приоритетная поддержка
Подключить

Оплата через ЮKassa · Карты, СБП, ЮMoney · Без автопродления · 30 дней доступа

Смотрите также

А если у вас популярная CMS

Для этих платформ обработчик писать не нужно — подключение занимает пару минут по логину и паролю.

Ваш сайт начнёт наполняться сам

Подключение занимает полчаса вместе с обработчиком. Дальше статьи приходят по расписанию, а вы видите каждую доставку в кабинете.

Создать аккаунт бесплатно →