0% прочитано

Laravel JSON-LD BlogPosting: Author, Canonical URL, and Localized Metadata

In ICanUp, BlogPosting JSON-LD connects an article not only to its SEO metadata but also to a real author entity. I show how I represent the author as a Person, keep structured data aligned with the canonical URL and localized metadata, and render it directly in the SSR HTML.

9 вересня 2026 р. 8 хв читанняLaravel

When I started adding structured data to posts in ICanUp, the simplest approach looked obvious: build a BlogPosting object and pass the title, description, URL, and author name.

It quickly became clear that JSON-LD should not become a separate SEO copy of Post state.

Structured data should describe the same Post entity, the same canonical URL, the same locale, and the same author that the application already exposes.

My main rule is simple: JSON-LD does not create a separate Post model for search engines. It serializes the existing public contract using Schema.org vocabulary.
Laravel BlogPosting JSON-LD connecting localized Post metadata canonical URL and Person author
BlogPosting combines Post metadata, the canonical URL, and the author entity into one structured representation of the same public page.

What I Want BlogPosting to Describe

For me, BlogPosting should answer a few simple questions.

What content is this? Which URL is its canonical public URL? Which language version is currently being shown? Who wrote it? Where is the author entity?

If JSON-LD answers those questions differently from the page itself, the SEO infrastructure already has two sources of truth.

JSON - Simplified 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"  }}

The Author Is More Than a String

It is easy to start by putting the author's name into a plain text field.

In ICanUp, however, the author has a public page, a professional identity, and links to external profiles.

That is why my BlogPosting uses a Person object instead of only an author string.

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

This creates an explicit relationship between the Post and the author page.

A search engine receives not only a display name but also the URL of an entity that can be connected across Posts and Person structured data.

Image and sameAs Strengthen the Person Entity

For the author page, I also designed the Person data to include an image and sameAs links to professional profiles.

Those values belong to the author entity. They should not be reinvented separately inside every Post.

I prefer one author contract that can be reused by both Person and BlogPosting.

JSON - Extended 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"  ]}

The JSON-LD URL Should Not Live Separately from Canonical

The next risk is generating the BlogPosting URL manually.

That becomes especially fragile in a multilingual application where the default locale may be unprefixed while EN uses /en.

The structured URL should come from the same public URL contract that produces the canonical URL.

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

This is the same principle I use for canonical and hreflang in multilingual Laravel.

An SEO consumer should not independently decide what a public route looks like.

Headline and Description Come from the Current Locale

A shared entity identity does not mean shared Post metadata.

UK and EN can use the same Post ID and shared slug while having different titles, descriptions, and visible content.

BlogPosting therefore needs to serialize metadata from the current localized page.

Field

Shared or localized

Post identity

Shared

Slug

Shared

Author entity

Shared

Headline

Localized

Description

Localized

Canonical URL

Locale-aware

Visible content

Localized

A Shared Slug Does Not Mean an Identical BlogPosting

In ICanUp, a Post uses the same shared slug across locales.

The English BlogPosting still needs the English headline, English description, and English canonical URL.

This is another reason I keep route identity separate from content localization.

I covered that shared-slug contract in more detail in my Post about shared slugs vs translated slugs.

The Author Remains the Same Entity Across Locales

Unlike a headline or description, the author does not become a different person when the locale changes.

The Person identity remains shared even when the professional description on the author page is localized.

Localized presentation should not create duplicate real-world Person entities.

I separate identity from presentation: Person remains the same entity while surrounding textual fields can be localized.

A Dedicated Author Page Makes the Relationship Explicit

Before the author page existed, the author name inside a post was mostly a display value.

With a public author page, the Post UI, BlogPosting JSON-LD, and Person structured data can point to the same author entity.

It also removes the need to copy professional author information into every post.

Person and BlogPosting Should Agree

The author page describes a Person.

The BlogPosting author property refers to that same person.

If the name, URL, or other identity fields differ between those structured payloads, the entity graph becomes less consistent.

JSON - Person on the 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 Needs to Be in the Initial HTML

Another important part of the implementation is SSR.

I do not want to rely on a crawler executing JavaScript, waiting for hydration, and only then discovering structured data.

BlogPosting JSON-LD should be present directly in the server-rendered HTML.

HTML - BlogPosting in a 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>

How I Verify Structured Data

BASH - Check JSON-LD in raw HTML
curl -s \  https://icanup.com.ua/en/posts/example-slug \  | grep -A 30 'application/ld+json'

Browser developer tools are still useful, but for this test case I care about the actual response HTML.

That confirms that structured data is not being inserted only after client-side rendering.

I Do Not Want a Separate Set of SEO Values for JSON-LD

The worst design for me would be one title for the page, a meta title for SEO, another manually maintained headline for JSON-LD, and yet another hand-built URL.

The more independent copies of public state I have, the easier it becomes for them to drift apart.

Structured data should be assembled from already normalized post, author, and routing data.

What I Would Avoid

  • Do not represent the author only as plain text when a real author entity already exists.

  • Do not hardcode the canonical URL inside JSON-LD.

  • Do not use the UK headline on the EN page.

  • Do not create a separate slug contract only for structured data.

  • Do not duplicate author profile data inside every post.

  • Do not rely only on client-side insertion of JSON-LD.

  • Do not let Person and BlogPosting use different author URLs.

What I Verify in Tests

  • BlogPosting JSON-LD is present in the initial post HTML.

  • The author has type Person.

  • The author contains the expected name and URL.

  • The author page contains its own Person JSON-LD.

  • The localized page uses the localized headline and description.

  • The structured URL matches the public canonical URL for the current locale.

  • UK and EN do not mix localized metadata.

  • The author link from the post points to the same author entity.

For me, good BlogPosting JSON-LD does not invent new facts about a post. It consistently describes the facts the application already treats as true.

What I Kept for Next Time

  • Use a Person object instead of an author string when the author entity already exists.
  • Keep author identity shared across localized pages.
  • Take headline and description from the current locale.
  • Take the public URL from the same routing contract used by canonical.
  • Do not create separate SEO copies of data only for JSON-LD.
  • Render structured data in the initial SSR HTML.
  • Verify Person and BlogPosting as one connected entity graph.

Conclusion

In ICanUp, BlogPosting JSON-LD became less of a standalone SEO feature and more of another consumer of existing application contracts.

Localized post metadata comes from the current language version, the URL stays aligned with canonical routing, and the author is represented as a real Person entity with a dedicated public page.

All of this needs to be available in the server-rendered HTML before hydration.

The less independent logic the JSON-LD builder owns, the lower the risk that structured data starts describing a different page from the one the user actually sees.