Bilingual SEO Migrations: Testing 301, Canonical, and hreflang
Development
Renaming guides to articles and opening the new page completes only part of a migration. Old links must reach the same article, metadata must agree on its new address, and sharing images must still resolve. This describes the implementation and reusable acceptance checks on this site. Local output checks cannot establish production crawling or indexing.
Define URL mappings before replacing strings
Article slugs remain unchanged, while /zh/guides and /en/guides move to /zh/articles and /en/articles. An old detail page must reach its matching article, not the archive homepage. Query parameters survive so previously shared source information is retained.
The rules distinguish pages from files. Bare /articles/codex-subscription redirects to default English; /articles/image.jpg is a public asset and must not acquire an /en prefix. Article and OG image directories moved too, with permanent redirects retained for legacy resources.
The www legacy route changes host and directory in one hop. Matching is limited to the actual guides path, so /guides-old does not accidentally redirect. These are acceptance examples rather than navigation links.
| Request example | Result | Acceptance condition |
|---|---|---|
| /zh/guides/codex-subscription | 301 → /zh/articles/codex-subscription | Same locale and article; destination 200 |
| /zh/guides?page=2 | 301 → /zh/articles?page=2 | Query retained; 200 only if page two exists |
| /articles/codex-subscription | 301 → /en/articles/codex-subscription | Default-language entry |
| /og/guides/old-image.jpg | 301 → /og/articles/old-image.jpg | Test an asset that actually exists |
| /guides-old | 404 | No accidental prefix match |
Self-canonical translations, reciprocal language annotations
Complete Chinese and English articles have independently useful content. Each canonical points to its own new URL, while hreflang identifies the equivalent translation. Canonicalizing every Chinese article to English sends a different consolidation signal; it is not a replacement for language annotations.
The code below shows the Chinese article’s target markup. Its English counterpart includes the same reciprocal alternate set, with an English self-canonical. This site chooses English for x-default because that is the default entry. It does not represent a third translation or a choice every bilingual site must copy.
Use consistent absolute production URLs. Exclude localhost, www, legacy directories, and mismatched languages. Review the pair of articles, not merely whether the word hreflang appears on one of them.
<link rel="canonical"
href="https://evanlong.me/zh/articles/codex-subscription">
<link rel="alternate" hreflang="zh-CN"
href="https://evanlong.me/zh/articles/codex-subscription">
<link rel="alternate" hreflang="en"
href="https://evanlong.me/en/articles/codex-subscription">
<link rel="alternate" hreflang="x-default"
href="https://evanlong.me/en/articles/codex-subscription">Generate the article and its metadata from the same record
Article records store translated titles, descriptions, dates, topics, and images. Lists, detail pages, generateMetadata, and sitemap consume those records. Editing a title therefore updates the H1, search title, OG / Twitter title, and Article headline without maintaining separate copies. A metadata helper adds the brand suffix without adding it to the visible title.
This is a consistency strategy, not a ranking guarantee. Inspect returned HTML: canonical, og:url, language URLs, and JSON-LD url / mainEntityOfPage should agree. Authors, dates, and images should match visible content. Do not invent reviews, sales, or HowTo steps to seek additional search presentation.
Sharing and reading images have different roles. OG cards here use 1200×630 images; article screenshots retain their real dimensions with descriptive alt text, captions, dates, and source links. Pricing screenshots preserve the reviewed state; text explains the possibility of change. Alt text describes the image rather than listing purchase keywords.
Pagination represents different content
The homepage displays three articles and the archive uses ten per page with ordinary previous / next links. If page two exists, /articles?page=2 canonicalizes to itself. ?page=1 redirects to the clean first-page URL. Canonicalizing every page to page one obscures the different article sets.
There are currently five articles, so page two returns 404. Preserving a query in a migration does not make every page number valid. With eleven articles, verify page two, language switching with page=2, and paginated sitemap entries. Future filters and sorting need their own indexing decisions rather than automatically inheriting pagination rules.
Test tooling must distinguish historical examples from active legacy URLs. This article explains /guides migration, so rejecting HTML merely for containing that string would reject useful case-study prose. Check canonical, sitemap, image src, and internal navigation for stale paths instead.
Check responses before release, crawling after release
Locally, check legacy status and Location, destination 200, reciprocal languages, working images, and 404 for missing slugs. The command below is a production check to run after deployment; replace the origin with localhost:3000 for local work. A browser displaying the final page after following redirects does not reveal every intermediate response.
After release, inspect representative new URLs in Search Console for crawling, selected canonical, and sitemap state, then observe replacement of legacy paths. Redirects and sitemap support the migration; passing locally cannot prove Google has crawled the pages or that rankings improved. Avoid another rename simply because results have not changed yet.
curl -sS -o /dev/null -D - \
'https://evanlong.me/zh/guides/codex-subscription?utm_source=test'
# Run after this migration is deployed.
# Expected: 301, then a Location ending in
# /zh/articles/codex-subscription?utm_source=test
# Check the destination separately: it should return 200.A shop click is not a paid order
A subscription article can answer a commercial search question while design and development articles remain useful in their own right. Place a purchase action after the relevant decision, such as finding that a membership is needed, instead of advertising in every section. First complete the task that brought the reader to the article.
This site’s shop_click event records public page paths, placement, and product identifiers without query strings or order materials. It shows where a visitor clicked, not whether they paid. A personal site and shop subdomain do not automatically share a complete purchasing attribution path.
For one landing page and reporting period, separate search impressions and clicks, article visits, product-link clicks, and attributable paid orders. In a hypothetical 100 visits with eight product clicks, the measured link-click rate is 8%; without attributed orders it is not an 8% purchase conversion rate. Small samples also cannot establish a winning title from one or two clicks. Change one identifiable problem and observe the metric it should affect.