The hard part of a multilingual blog is not the first translation. The hard part is keeping URLs, canonical tags, language alternates, feeds, and internal links correct after the third or fourth post.
The short answer: decide the URL model first, then generate canonical and hreflang links from the same content relationship.
Astro is a good fit for this because it can generate static HTML and validate Markdown metadata through content collections. But Astro does not decide your multilingual SEO model for you. That part should be explicit.
Use visible locale URLs
For a technical blog, visible locale prefixes are easier to operate than browser-language redirects.
/ko/posts/astro-multilingual-seo-url-structure/
/en/posts/astro-multilingual-seo-url-structure/
This keeps the document address stable. A search crawler, a feed reader, or a person sharing the URL can see which language the page is in.
| Model | Good for | Watch out for |
|---|---|---|
/ko and /en prefixes |
Clear multilingual sites | Default locale handling |
/ as default, /en as alternate |
Local-first blogs | Root canonical and x-default |
| Automatic language redirect | Apps with user preference | Crawlers and slow networks |
For a public blog, I would avoid hiding the first page behind a JavaScript redirect. Render useful HTML at the root, then provide language alternates.
Keep a translation key
Do not rely on titles or filenames to connect translations. Titles change. Slugs can change. A stable translationKey gives the site one internal relationship to work with.
---
title: "Astro Multilingual SEO: Start with URL Structure"
lang: "en"
translationKey: "astro-multilingual-seo-url-structure"
---
The Korean version can use the same key:
---
title: "Astro 다국어 블로그 SEO: URL 구조를 먼저 정해야 하는 이유"
lang: "ko"
translationKey: "astro-multilingual-seo-url-structure"
---
That key can drive language switching, alternate links, sitemap logic, related posts, and editorial checks.
Canonical and hreflang are not the same thing
Canonical tells search engines which URL is the preferred version of a page. Hreflang tells them which language or regional alternatives exist.
An English post should canonicalize to itself:
<link rel="canonical" href="https://example.com/en/posts/foo/" />
Then it can list alternatives:
<link rel="alternate" hreflang="ko" href="https://example.com/ko/posts/foo/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/posts/foo/" />
<link
rel="alternate"
hreflang="x-default"
href="https://example.com/ko/posts/foo/"
/>
The Korean post should also canonicalize to itself. Do not point every translation to one language as canonical. Different language pages are not duplicate pages; they are alternate versions.
Sitemap entries should match real pages
A multilingual sitemap should include the pages that actually exist.
https://example.com/ko/posts/foo/
https://example.com/en/posts/foo/
If the English version does not exist, do not invent an English hreflang URL. Missing alternates are better than broken alternates.
The same rule applies to internal language switchers. A language switch should point to the translated article when it exists, and to the localized posts index only when it does not.
A practical Astro boundary
A clean Astro setup usually separates the work like this:
| Area | Example file | Responsibility |
|---|---|---|
| Routing | src/pages/[lang]/posts/[slug].astro |
Build localized URLs |
| Content | src/content/posts/{lang} |
Markdown and frontmatter |
| SEO | src/lib/seo.ts |
Title, description, canonical |
| Alternates | src/lib/alternates.ts |
translationKey matching |
| Layout | BaseLayout.astro |
Render head tags |
The point is not abstraction for its own sake. The point is that adding a Markdown file should update the article page, list page, RSS feed, sitemap, and structured data without manual edits in five places.
Common failure modes
[ ] Every language page uses the same canonical URL
[ ] hreflang points to pages that do not exist
[ ] the root page is only a visible redirect shell
[ ] English metadata is copied from Korean metadata
[ ] URLs are localized but the body language is not
[ ] sitemap includes only the default language
The first failure is the most damaging. If all language pages canonicalize to the Korean page, the English page may look like a duplicate instead of a valid alternate.
When to translate
Not every post needs an English version. Translate posts when the search intent is global.
| Topic | English value |
|---|---|
| Astro, GitHub Pages, Cloudflare, Vercel | High |
| Naver Search Advisor | Low |
| GA4, AdSense, consent, privacy | High |
| Personal product decisions | Medium |
A small blog can translate fewer posts if the translated posts are the ones with global search demand.