0% прочитано

Laravel JSON-LD BlogPosting: author, canonical і localized metadata

У ICanUp JSON-LD BlogPosting пов’язує статтю не тільки з SEO metadata, а й з реальною author entity. Показую, як я представила автора як Person, узгодила structured data з canonical URL і localized metadata та чому JSON-LD має рендеритися одразу в SSR HTML.

9 вересня 2026 р. 8 хв читанняЛаравель

Коли я почала додавати structured data до статей у ICanUp, найпростіший варіант виглядав очевидно: зібрати BlogPosting, передати title, description, URL та ім'я автора.

Але дуже швидко стало зрозуміло, що JSON-LD не повинен бути окремою SEO-копією Post state.

Structured data мають описувати ту саму Post entity, той самий canonical URL, ту саму locale і того самого автора, яких уже бачить application.

Моє головне правило: JSON-LD не створює окрему модель статті для search engines. Він серіалізує вже існуючий public contract у Schema.org vocabulary.
Laravel BlogPosting JSON-LD connecting localized Post metadata canonical URL and Person author
BlogPosting збирає Post metadata, canonical URL і author entity в одну structured representation тієї самої public сторінки.

Що я хочу отримати від BlogPosting

Для мене BlogPosting має відповідати на кілька простих питань.

Що це за матеріал? Який URL є його canonical public URL? Якою мовою показана поточна версія? Хто її написав? Де знаходиться author entity?

Якщо JSON-LD відповідає на ці питання інакше, ніж сама сторінка, SEO infrastructure вже має два джерела правди.

JSON - Спрощений BlogPosting contract
{  "@context": "https://schema.org",  "@type": "BlogPosting",  "headline": "Localized post title",  "description": "Localized post description",  "url": "CANONICAL_URL",  "author": {    "@type": "Person",    "name": "Alyona Yakymiv",    "url": "AUTHOR_URL"  }}

Author для мене - не просто string

Спочатку author легко передати як просте ім'я.

Але в ICanUp у автора вже є окрема public сторінка, професійна identity і зв'язок із зовнішніми профілями.

Тому в BlogPosting я використовую об'єкт Person, а не просто текстове поле.

JSON - Author як Person
{  "author": {    "@type": "Person",    "name": "Alyona Yakymiv",    "url": "https://icanup.com.ua/author/alyona-yakymiv"  }}

Це створює явний зв'язок між Post і author page.

Search engine отримує не тільки ім'я, а URL сутності, яку можна пов'язати з іншими статтями та structured data типу Person.

Image і sameAs посилюють Person entity

Для author page я також заклала image і sameAs із професійними зовнішніми профілями.

Ці поля належать саме author entity. Їх не потрібно заново придумувати окремо для кожної статті.

Краще мати один author contract і повторно використовувати його в Person та BlogPosting.

JSON - Розширена Person entity
{  "@type": "Person",  "name": "Alyona Yakymiv",  "url": "https://icanup.com.ua/author/alyona-yakymiv",  "image": "AUTHOR_IMAGE_URL",  "jobTitle": "Full-stack developer",  "sameAs": [    "PROFESSIONAL_PROFILE_URL"  ]}

URL у JSON-LD не повинен жити окремо від canonical

Наступний ризик - сформувати BlogPosting URL вручну.

У multilingual application це особливо небезпечно: default locale може бути без prefix, EN мати /en, а routing rules з часом змінюватися.

URL structured data має походити з того самого public URL contract, що і canonical.

Canonical URL contract
UK:https://icanup.com.ua/posts/example-slug EN:https://icanup.com.ua/en/posts/example-slug

Це той самий принцип, який я використовую для canonical і hreflang у multilingual Laravel.

SEO consumer не повинен самостійно вирішувати, як виглядає public route.

Headline і description беруться з поточної locale

Shared entity identity не означає shared Post metadata.

UK і EN можуть мати той самий Post ID та shared slug, але title, description і visible content у них різні.

Тому BlogPosting має серіалізувати metadata саме поточної localized page.

Field

Shared чи localized

Post identity

Shared

Slug

Shared

Author entity

Shared

Headline

Localized

Description

Localized

Canonical URL

Locale-aware

Visible content

Localized

Shared slug не означає однаковий BlogPosting

У ICanUp Post використовує shared slug для всіх locales.

Але EN BlogPosting все одно має EN headline, EN description і EN canonical URL.

Це ще один приклад того, чому я розділяю route identity та content localization.

Сам shared-slug contract я детальніше розбирала у статті про shared slug vs translated slugs.

Author залишається тією самою entity між locales

На відміну від headline або description, автор статті не стає іншою людиною після перемикання locale.

Тому Person identity залишається shared, навіть якщо професійний description на author page локалізований.

Localized presentation не повинна створювати дублікати реальної Person entity.

Я розділяю identity і presentation: Person залишається тією самою сутністю, а текстові поля навколо неї можуть локалізуватися.

Окрема author page робить зв'язок явним

До окремої author page ім'я в статті було лише display value.

Після її появи Post UI, BlogPosting JSON-LD і Person structured data можуть посилатися на одну author entity.

Це також прибирає необхідність копіювати професійну інформацію автора у кожну статтю.

Person і BlogPosting повинні погоджуватися між собою

На author page structured data описують Person.

У BlogPosting поле author посилається на цю саму людину.

Якщо ім'я, URL або інші identity fields відрізняються між цими двома JSON-LD payloads, entity graph стає менш послідовним.

JSON - Person на author page
{  "@context": "https://schema.org",  "@type": "Person",  "name": "Alyona Yakymiv",  "url": "https://icanup.com.ua/author/alyona-yakymiv",  "image": "AUTHOR_IMAGE_URL",  "sameAs": [    "PROFESSIONAL_PROFILE_URL"  ]}

JSON-LD має бути в initial HTML

Ще одна важлива частина implementation - SSR.

Я не хочу покладатися на те, що crawler виконає JavaScript, дочекається hydration і лише після цього побачить structured data.

BlogPosting JSON-LD має бути присутнім у server-rendered HTML одразу.

HTML - BlogPosting у server-rendered page
<script type="application/ld+json">{  "@context": "https://schema.org",  "@type": "BlogPosting",  "headline": "Localized title",  "description": "Localized description",  "url": "https://icanup.com.ua/en/posts/example-slug",  "author": {    "@type": "Person",    "name": "Alyona Yakymiv",    "url": "https://icanup.com.ua/author/alyona-yakymiv"  }}</script>

Це продовжує проблему, яку я вже зустрічала раніше: meta tags були в browser, але не в початковому HTML Inertia SSR.

Як я перевіряю structured data

BASH - Перевірити JSON-LD у raw HTML
curl -s \  https://icanup.com.ua/en/posts/example-slug \  | grep -A 30 'application/ld+json'

Browser inspector корисний, але для цього test case мене цікавить саме response HTML.

Так я перевіряю, що structured data не з'являються лише після client-side rendering.

Я не хочу окремого набору SEO values для JSON-LD

Найгірший варіант для мене - мати title для page, meta title для SEO, ще один headline для JSON-LD і окремий вручну сформований URL.

Чим більше незалежних copies одного public state, тим легше вони розходяться.

Structured data повинні збиратися з уже нормалізованих Post, author і routing data.

Що я б не робила

  • Не передавала б автора тільки як plain string, якщо вже існує author entity.
  • Не hardcode canonical URL у JSON-LD.
  • Не використовувала б UK headline для EN page.
  • Не створювала б окремий slug contract тільки для structured data.
  • Не дублювала б author profile data в кожній статті.
  • Не покладалася б тільки на client-side insertion JSON-LD.
  • Не дозволяла б Person і BlogPosting використовувати різні author URLs.

Що я перевіряю тестами

  • BlogPosting JSON-LD присутній у initial HTML статті.
  • Author має type Person.
  • Author містить правильні name та URL.
  • Author page містить власний Person JSON-LD.
  • Localized page використовує localized headline і description.
  • Structured URL відповідає public canonical URL поточної locale.
  • UK і EN не змішують localized metadata.
  • Author link зі статті веде на ту саму author entity.

Для мене хороший BlogPosting JSON-LD не додає нові факти про статтю. Він послідовно описує факти, які application уже вважає правдою.

Що я залишила собі на майбутнє

  • Використовувати Person object замість author string, коли author entity уже існує.
  • Тримати author identity shared між localized pages.
  • Брати headline і description з поточної locale.
  • Брати public URL з того самого routing contract, що і canonical.
  • Не створювати окремі SEO copies даних тільки для JSON-LD.
  • Рендерити structured data в initial SSR HTML.
  • Перевіряти Person і BlogPosting як один пов'язаний entity graph.

Висновок

У ICanUp BlogPosting JSON-LD став не окремою SEO feature, а ще одним consumer уже існуючих application contracts.

Localized Post metadata приходять із поточної мовної версії, URL узгоджується з canonical routing, а author представлений як реальна Person entity з окремою public page.

І все це має бути присутнім у server-rendered HTML ще до hydration.

Чим менше окремої logic знає JSON-LD builder, тим менший ризик, що structured data почнуть описувати іншу сторінку, ніж та, яку реально бачить користувач.