How to unify authorship across two sites by sharing one Person @id in JSON-LD
I run two sites: a portfolio at developerehsan.com and this blog at
blog.developerehsan.com. To a search engine, those are two different domains
that both happen to mention someone named Ehsan Shahid. Are they the same person?
It has no way to know — unless I tell it. This post is about the one line of
structured data that does exactly that, and the linked-data idea that makes it
work.
The problem: duplicated identities
Both sites describe an author. If each site invents its own author node, a
crawler sees two unrelated Person entities that share a name. That is a missed
opportunity: the reputation, the projects, and the articles never accumulate onto
one identity. Worse, ambiguous authorship can weaken the trust signals that feed
rich results.
The fix is not to copy author details into the blog. It is to reference the author that already exists on the portfolio.
The key idea: @id is a stable identity, not a location
In JSON-LD, @id is a globally unique name for a thing. It usually looks like a
URL, but it is not a link you fetch — it is an identifier. When two JSON-LD
nodes anywhere on the web use the same @id, they are asserting they describe the
same entity. This is the core of linked data: identity by shared name, not by
copied data.
My portfolio publishes a Person node (along with ProfilePage, WebSite, and FAQPage) with a fixed id: https://developerehsan.com/#person.
The #person fragment makes it a distinct node on the homepage. I store that id
once, in the blog’s constants, right next to a comment reminding me to keep it in
sync:
// blog/src/consts.ts
// Keep authorship values in sync with the root portfolio's structured data
// (index.html Person @id).
export const SITE = {
title: "Ehsan Shahid — Blog",
url: "https://blog.developerehsan.com",
portfolioUrl: "https://developerehsan.com",
author: "Ehsan Shahid",
authorId: "https://developerehsan.com/#person",
ogImage: "/og-image.png",
} as constThe important field is authorId. Its value is the portfolio’s Person id — a
URL on a different domain than the blog. That cross-domain pointer is the whole
trick.
Referencing it from the blog’s posts
In PostLayout.astro, when I build the BlogPosting JSON-LD, the author and
publisher do not spell out a person. They reference one by @id:
---
// blog/src/layouts/PostLayout.astro
import { SITE } from "@/consts"
// ...
author: { "@type": "Person", "@id": SITE.authorId, name: SITE.author },
publisher: { "@type": "Person", "@id": SITE.authorId, name: SITE.author },
---Because SITE.authorId is https://developerehsan.com/#person, every blog post
now says: “the author and publisher of this article is the exact same Person the
portfolio already describes.” I include name as a friendly label, but the @id
is what does the unifying. The rich profile — job title, social links, skills —
lives once on the portfolio; the blog just points at it.
flowchart LR
subgraph Portfolio
P["Person\n@id: developerehsan.com/#person"]
end
subgraph Blog
A1["Post A author"] --> P
A2["Post B author"] --> P
A3["Post C publisher"] --> P
endEvery arrow is the same @id. To a crawler consuming both sites, there is one
Person, referenced from many places, rather than a fresh stranger per page.
Why reference instead of copy?
You could paste the full author object into every post. Referencing by @id is
better for three reasons:
- Single source of truth. Update your title or add a social profile once on the portfolio; the blog never goes stale because it holds no author details to drift.
- Explicit sameness. A shared
@idis an unambiguous claim that these are one entity. Matching names across domains is only a guess. - Consolidated signals. Articles, projects, and profile all attach to one identity, so authority accrues in one place.
The one discipline it requires: the portfolio must actually emit a Person node
whose @id is https://developerehsan.com/#person. If that id ever changes, the
blog’s authorId must change with it — which is why consts.ts carries that
sync comment. Keep them equal and the two sites read as one author.
Verifying it
Build the blog, open a post’s source, and find the application/ld+json script.
Confirm both author and publisher show "@id": "https://developerehsan.com/#person".
Then run the portfolio and confirm its Person node uses the identical id.
Google’s Rich Results Test will parse each page independently; the shared id is
what ties them together across the crawl.
What to remember
- In JSON-LD,
@idis a stable identifier, not a URL you fetch — same@idmeans same entity. - Store the portfolio’s Person id once as
SITE.authorIdand reference it from every post’sauthorandpublisher. - Reference, don’t copy: one source of truth, an explicit sameness claim, and consolidated authority.
- The portfolio must publish a
Personnode with that exact@id; keep the two values in sync.
Related reading: this builds on the per-post BlogPosting JSON-LD and complements the RSS feed, sitemap, and SEO meta setup.