SEO
SEO в OnPress держит Yoast SEO. Через API v2 оно правится на двух уровнях: страница — полями frontmatter при публикации, сайт — ручкой /site/seo. Третья ручка обходит опубликованные страницы и говорит, чего на них не хватает.
SEO страницы
| Поле frontmatter | Что задаёт |
|---|---|
seo_title | <title> и заголовок для соцсетей. Пусто — работает шаблон сайта (%%title%% %%sep%% %%sitename%%) |
seo_description | мета-описание. Пусто — работает шаблон сайта, если он задан |
keyword | ключевая фраза Yoast |
excerpt | выдержка — описание страницы в списках и бинд page.description |
Пустые seo_title и seo_description во frontmatter не стирают прежние значения — они просто не пишутся. Прочитать, что стоит: GET /pages/{ref} → page.seo. Canonical, Open Graph, Twitter, JSON-LD, hreflang и карта сайта строятся Yoast автоматически, в том числе на статических копиях и на фреймах-документах.
Закрыть отдельную страницу от индексации через API v2 нельзя; закрывается сайт целиком (blog_public: 0 в настройках) или тип записей (noindex ниже).
SEO сайта
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/seo" | jq '.seo | {separator, separator_char, titles, noindex, sitemap: .sitemap | {enabled, index_url}, robots_url}'
Ответ seo — всё, что Yoast хранит для сайта целиком:
| Раздел | Что в нём |
|---|---|
separator, separator_char | ключ разделителя (sc-dash, sc-pipe, sc-middot…) и символ, который он выводит |
titles | шаблоны заголовков: home, page, post, 404, author, archive, search — с переменными %%title%%, %%sep%%, %%sitename%%, %%sitedesc%%, %%page%% |
metadesc | шаблоны описаний: home, page, post, author, archive |
noindex | archive, author, author_noposts и карты post_types, taxonomies: true — закрыть от индексации |
attachments | redirect_to_parent — уводить страницы вложений на родителя |
breadcrumbs | enabled, separator, home, prefix, bold_last — хлебные крошки Yoast |
organization | type (company или person), name, alternate_name, logo_id, logo_url, данные персоны |
social | open_graph, twitter, twitter_card, картинка по умолчанию, profiles (facebook, twitter, instagram, linkedin, youtube, wikipedia, mastodon, pinterest, other) |
sitemap | enabled, entries_per_page, author, состав (post_types, taxonomies — вычисляется из noindex), index_url |
index_now | enabled, key, supported — умеет ли эта сборка Yoast отправлять IndexNow |
robots_url, yoast_version | адрес robots.txt и версия плагина |
Изменить
Шлите только разделы, которые меняете:
curl -s -X PATCH "$API/site/$SITE/seo" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{
"separator": "sc-pipe",
"titles": { "page": "%%title%% %%sep%% %%sitename%%" },
"noindex": { "author": true, "post_types": { "post": true } },
"organization": { "type": "company", "name": "OnPress", "logo": 4827 },
"social": { "twitter_card": "summary_large_image", "profiles": { "twitter": "onpress", "other": ["https://t.me/onpress"] } },
"sitemap": { "entries_per_page": 500 }
}' | jq '{changed, cache}'
Каждое значение пишется через собственный валидатор Yoast, поэтому ответ честный:
changed— список реально изменившихся ключей в видераздел.ключ(одно поле может дать несколько записей: логотип пишет id, адрес и сброс кэша логотипа);- значение, которое валидатор Yoast не принял, приходит в
changedс пометкой(отклонено валидатором Yoast)— оно не сохранено; - ничего не изменилось —
changed: [], кэши не трогаются; что-то изменилось — сбрасываются кэш настроек и карты сайта Yoast, кэш страниц и объектный кэш.
Ключи записи отличаются от ключей чтения там, где это логично: пишете id картинки, читаете id и адрес. organization.logo → logo_id и logo_url; organization.person_logo → person_logo_id; social.default_image → default_image_id и default_image. Id должен быть картинкой медиатеки этого сайта (400 invalid_logo, 400 invalid_default_image), 0 — снять. social.profiles.twitter — имя аккаунта без @, остальные профили — адреса.
Состав карты сайта задаёт noindex, а не sitemap. Тип записей или таксономия попадает в карту, когда они индексируются. sitemap.post_types или sitemap.taxonomies в запросе — 400 sitemap_composition_readonly; меняйте noindex.post_types / noindex.taxonomies, а GET покажет получившийся состав.
IndexNow. Бесплатная сборка Yoast IndexNow не отправляет, поэтому включение там — 409 index_now_unsupported. Включить без ключа — 400 index_now_key_missing; ключ — 8–128 шестнадцатеричных символов, "" очищает, "auto" генерирует.
Другие отказы: неизвестный раздел или ключ — 400 unknown_field со списком допустимых (allowed); раздел не объект — 400 invalid_<раздел>; неизвестный разделитель — 400 invalid_separator; пустое тело — 400 nothing_to_change; Yoast не активен — 409 yoast_missing.
Проверка страниц
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/seo/pages?lang=ru" | jq '{totals, meta: .meta.pages, headings: [.headings.pages[] | {url, problems}], unreachable: [.unreachable[] | {url, status, reason}]}'
Ручка обходит все опубликованные страницы (в языке lang или всех — lang=all, по умолчанию) по их настоящим адресам — так же, как их видит поисковик, — и разбирает отданный HTML. Это долгий запрос: страница на страницу, до восьми одновременно, до 2000 страниц; время обхода — в took_ms.
| Раздел ответа | Что в нём |
|---|---|
totals | сколько страниц, обойдено, проверено, пропущено как noindex, недоступно |
meta.pages | страницы без <title> или без мета-описания: missing: ["description"] и что сейчас стоит |
headings.pages | страницы с проблемами структуры заголовков: no_h1 (нет H1), many_h1 (больше одного), jump (скачок уровня, например H2 → H4) и сама структура outline |
unreachable | страницы, которые ответили не 200, не HTML или не ответили: reason (http_error, timeout, unreachable, not_html), status |
Страницы с noindex не проверяются и считаются отдельно. Обход читает отданную страницу, а не исходник, поэтому видит H1 из фрейма, описание из шаблона Yoast и то, что вывели вставки.
Частая находка many_h1: фрейм выводит заголовок страницы биндом page.title в <h1>, а в теле страницы тоже стоит # Заголовок. Уберите один из двух.
Раздел SEO дашборда показывает то же самое списком проверок.