Back to News & Insights
SEO September 29, 2026 · 7 min read

Publish Once, Own the Original

Every cross-post of your blog competes with the original unless it carries a canonical link back, and nobody re-checks. castio is the tool I built to publish once on my own site and keep every copy pointing home.

Publish Once, Own the Original

Most developer writing gets read on someone else's website. DEV, Hashnode, Medium, a Bluesky post, a Hacker News thread. That's where the readers are, and your own site is often the last place they land.

I'm fine with that. What I'm not fine with is each of those copies competing with the original in search, because the canonical link was never set, was set slightly wrong, or was set once and never checked again.

So I built castio. I publish on my own site the way I always have, push to git, and castio takes it from there. It waits until the post is actually live and shows me exactly what each platform will get. It posts where it can, hands me a ready-made kit where it can't, and then keeps checking that every copy still points home. The original is yours, and every copy should say so. That should hold every day after you post it, not just on the day itself.

The fix for duplicate content is old and boring: a canonical link. A copy that says "the original lives over there" stops competing with the original. The problem is that every platform handles it differently: DEV has a canonical URL field on each article. Medium and Hashnode each have their own canonical / original-URL setting, in their own corner of their own UI. Bluesky, LinkedIn, Hacker News and Reddit don't copy the post at all. They're just posts that link to you.

None of that is hard. It's tedious, which is worse. Tedious work gets done by hand, a little differently each time, and then forgotten. A missing or slightly-off canonical doesn't throw an error or show a warning. It just sits there.

salahxd.dev is the original. The copies point back to it, and the posts link to it.

Building castio's canonical check meant looking hard at my own site first, and it didn't pass.

My site linked to posts without a trailing slash, but the canonical tag on each post used the URL with one. So every post had two URLs, /blog/some-post and /blog/some-post/. A person would call those the same page. A crawler sees two different URLs.

castio compares the live canonical against the expected URL exactly, so none of my posts would ever have been marked Ready until the two agreed. I fixed the site, and links now end in /. The dashboard now says it right under the URL pattern field: "The trailing slash matters." I wrote that line for myself first.

The other honest one: I have an older DEV article whose canonical I still need to fix by hand. castio only manages what it publishes, so that one's on me.

Push. I write the way I always have: an Astro site, posts as files in src/content/blog, in a GitHub repo. castio is a GitHub App installed on that one repo with read-only Contents access. When I push, a webhook tells castio a post was added or changed. castio verifies the webhook signature before it trusts any of the payload.

Wait. A push isn't a deploy. castio keeps checking the live URL until the post is actually up and its matches the expected URL exactly. Only then is the article Ready. It never distributes a post that isn't live yet, so no cross-post ever points at a 404.

Approve. The preview shows exactly what each platform will receive. Nothing goes out without my approval. I can send it now or schedule it, in my timezone.

Publish. Two platforms are fully automatic: DEV goes through the API with canonicalurl set. I can publish or save as a draft, with an optional "Originally published at" footer. Bluesky gets a post with a link card, built from a template with {title} and {excerpt}. It connects with proper atproto OAuth, with no app passwords.

LinkedIn, Hacker News, Reddit, Medium and Hashnode get copy-paste kits. castio prepares everything, then I post it and mark it done. I never set out to automate every platform. What I wanted was to know where every copy lives.

Verify. After publishing, castio fetches every copy and checks that its canonical points back to the original. Then it checks again every day. If a copy drifts, it gets flagged as needing action and I get a notification, plus an email if I've turned that on.

That last step is the one I actually care about. Everything before it saves me time. Verification is the part I'd never do myself.

From git push to verified copies. The only manual steps are approving, and posting the kits.

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation