
Reaching a bigger audience while your own copy stays primary — rel=canonical and full-text RSS
What backlinks and rel=canonical do, why Zenn and Qiita change the plan, and the pipeline that makes republishing an article a paste rather than a project.
On this page
Introduction
I had done everything a search engine asks for. The site was submitted to Google and Bing, it had a sitemap, structured data, a robots.txt naming every AI crawler, and clean HTML on every page. The only thing missing was the one thing none of that produces: other sites linking to mine.
The obvious answer is to post the same article somewhere with more readers, and it comes with a trap I did not understand well enough to avoid. Two copies of one article compete, and the bigger site usually wins, with your own words. The tag that prevents this is rel=canonical, and asking which platforms support it turned up the finding that shaped everything after: Zenn and Qiita cannot set it at all.
This article covers what a backlink and a canonical actually do, what those platform limits force, and the two things this blog had already shipped that turned republishing from a project into a paste.
What a backlink is, and why it cannot be engineered
A backlink is a link from someone else’s site to yours. That is the definition, and its weight comes from who is doing the linking.
Search engines treat a link as a small vote. A new domain with no votes at all is not penalised so much as unknown, and being unknown is what limits how often a crawler bothers coming back. Every other piece of SEO work makes a page legible once a crawler arrives. A backlink is part of what makes it arrive.
That is why it stayed outstanding after everything else was done. A sitemap is a file I can generate. Structured data is markup I can emit. A link from another site is a decision somebody else makes, and the only honest way to influence it is to put the writing where people who might link to it already are.
Where links come from, and which ones copy anything
A backlink is a link. Nothing is duplicated to make one. Someone writes “see this” and points at your address, and that is the mechanism. It is worth stating plainly, because the next section is about republishing and the two are easy to read as the same move.
They are not. Republishing puts a copy of the article on a platform that already has readers, and it is that copy which creates the problem rel=canonical exists to solve. A shared link makes no copy, so there is nothing to compete with and no canonical to set.
| What you do | Copies the article | Needs a canonical | What it is for |
|---|---|---|---|
| Share the link | no | no | reach, and being discovered |
| Comment with the link where it helps | no | no | reach, on a domain already trusted |
| Republish the full text | yes | yes | that platform’s readers |
| Someone else links to you | no | no | the ranking signal itself |
Links from social platforms are almost all nofollow. Facebook, X and LinkedIn mark outbound links that way by default. Google changed nofollow from an instruction to a hint in September 2019 and extended that to crawling and indexing the following March, so such a link is not worth nothing. It is also not worth planning around.
What a share reliably does is put the writing in front of someone who has a site of their own. Their link is the followed one. That is the only route to the last row of that table, and it is not a route anything described here can automate.
What rel=canonical does
rel=canonical is one line in the <head> of a page:
<link rel="canonical" href="https://oharu121.com/blog/some-article/">It means: this page you are reading is a copy, and that address is the original. A search engine that honours it folds the two together and treats the original as the one to show.
Without it, two pages holding the same text look like two unrelated pages that happen to match. The engine picks one, and it picks on trust, which a large platform has more of than a personal blog does.
Two things are worth being precise about, because both are easy to overstate.
It is a hint, not an instruction. Google’s documentation is explicit that a canonical is one signal among several, and a strong enough contrary signal can override it. In practice it is honoured, and it is the only lever you get.
It does not send readers anywhere. A canonical is invisible to the person reading the copy. They stay on the platform, and that is fine: the link back is what you wanted from the republication, not the pageview.
Zenn and Qiita cannot send one
The agent’s first recommendation was to cross-post to dev.to, Zenn and Qiita with a canonical pointing home, flagged as unverified. Checking it changed the plan.
dev.to supports a canonical URL per post, as a canonical_url field, and can also import from an RSS feed and set it automatically.
Zenn does not. An investigation of exactly this found that Zenn’s published pages carry a canonical pointing at Zenn’s own URL regardless of what the source frontmatter says. Writing one changes nothing that a crawler sees.
Qiita has no user-settable canonical either.
Had that recommendation shipped unchecked, the result would have been the opposite of the goal: the full text of every article handed to two sites with far more authority, with nothing anywhere saying which copy came first.
What the limit forces is a split rather than a retreat. Where a canonical is available, republish in full. Where it is not, post a short summary and link to the original. The summary still reaches those readers and still earns the link; it just declines to enter a competition it would lose.
What this blog had already built for something else
Two earlier changes turned out to matter here, and neither was made for this.
A Markdown twin of every article. Each article is served twice: once as a page, and once as plain Markdown at the same address with .md on the end. That was built so answer engines get the prose without a navigation shell wrapped around it. It also happens to be exactly what you paste into another editor.
Full text in the RSS feeds. Items used to carry a 160-character summary and a link. They now carry the article in full, because a platform importer reading a summary-only feed produces a stub.
The feed change had a constraint the twins did not. compiledContent() is unsupported for MDX and every article here is MDX, so the usual route does not exist. The agent’s approach reuses the transform that already produces the twins and renders its output with Astro’s own markdown processor:
processor ??= createMarkdownProcessor({});const { code } = await (await processor).render(mdxToMarkdown(body));The feed inherits what the twin loses. A diagram arrives as its caption and a photograph as its alt text, because neither has an address that survives leaving the page. Prose, headings, tables and code all pass through. That is a large improvement on two sentences and it is not the same as the page.
Two bugs in the feed, and what they have in common
The feed shipped with two defects the agent wrote, and both were found by review rather than by any check.
Root-relative links leaked. One article links to its own part one as /blog/…. A browser resolves that against the page it is on. A feed reader has no such page, so it resolves against its own origin and 404s. One per feed, in all three languages.
The verification had claimed this was clear. It searched for ./ while the link was /, so the check could not see that shape at all.
A stray control character would have made the feed unparseable. The Markdown transform masks code spans with NUL sentinels before rewriting tags, and restores them at the end. In a .md file a leftover sentinel is cosmetic. In XML it is fatal, and nothing in the build validates the feed:
a NUL inside an element parses: FAIL — not well-formed (invalid token)pnpm check and pnpm build would both have stayed green while every reader failed to parse the feed.
Both now handled: href="/…" and src="/…" are resolved against the site origin, and the XML-forbidden control range is stripped. What the two share is that the thing which would have caught them was written by the same person, at the same time, with the same blind spot. A check and the code it checks agreeing is not evidence.
The runbook
For an article that is ready to republish.
On a platform that supports a canonical, republish in full. The Markdown twin is already the paste:
curl -sS https://oharu121.com/blog/<slug>.md | pbcopyDelete the twin’s header block, including its --- separator, which would otherwise collide with the platform’s own frontmatter delimiters. Then set the canonical:
---title: <the title>published: truecanonical_url: https://oharu121.com/blog/<slug>/tags: seo, astro, webdev---Verify it took. View source on the published post and look for a <link rel="canonical"> pointing at your address. If it is missing, the frontmatter did not parse, which is usually the leftover ---.
On a platform that cannot, post two or three paragraphs and link to the original. On this blog the Japanese and Traditional Chinese translations already exist, so the summary can be written in the reader’s language and point at that locale’s URL.
Where a link is worth more than a repost, comment on the issue where someone hit the problem the article solves. The comment has to stand on its own; the link is the long-form version, not the payload.
What to expect
Worth being blunt, because the honest answer is unsatisfying.
A backlink is not a switch. Nothing about this makes an article rank tomorrow. What it changes is whether a crawler has a reason to come back, and that compounds slowly.
The referral traffic arrives first and the search effect much later, if it arrives at all. The platform readers are immediate. Anything downstream depends on whether one of them writes something and links to you, which is not something a pipeline can cause.
What the tooling actually bought is the cost of trying. Republishing was previously a copy-paste-reformat job per article per platform, which is exactly the sort of chore that quietly stops happening. It is now a curl and a frontmatter block. The pipeline does not make republishing work better. It makes it cheap enough to keep doing.
Summary
- A backlink is a link from someone else’s site, and it is the one part of this nobody can generate. Everything else makes a page legible once a crawler arrives.
- Sharing a link and republishing an article are different moves. Only republishing makes a copy, and only a copy needs a canonical. Social links are almost all
nofollow, which since 2019 is a hint rather than an instruction: worth doing for reach, not worth counting as a ranking signal. rel=canonicaldeclares which copy is the original, so republishing does not put you in competition with yourself. It is a hint rather than an instruction, and it moves no readers.- Two of the three obvious platforms cannot set it. Zenn’s pages point their canonical at Zenn whatever the source says, and Qiita has no setting. Full text goes where a canonical is possible; a summary and a link go everywhere else.
- The Markdown twin and the full-text feed were built for answer engines and turned out to be the syndication pipeline. One is a paste, the other is what an importer reads.
- A check written alongside the code it checks shares its blind spot. The feed’s own verification searched for
./while the broken link was/. - Expect the cost of trying to drop, not the results to arrive. That is still the useful change, because the version that costs an afternoon per platform is the version that does not happen.
References
- Google’s guidance on canonical URLs, including that a canonical is a signal rather than a directive
- Google’s announcement that
nofollowbecame a hint, and the introduction ofrel="ugc"andrel="sponsored" - The DEV Community RSS import settings, which set the canonical URL on every imported post
- An investigation showing Zenn’s published HTML carries its own canonical whatever the frontmatter says
- The RSS 2.0 specification’s content module, which defines
content:encoded