Hreflang for a multilingual site is the rel=“alternate” attribute on a <link> tag that tells search engines that several URLs are translations of the same page, and hints which version to show a user in their region. In Astro, it’s either generated by @astrojs/sitemap through i18n configuration, or added manually via the astro:i18n module in a page’s head; in Next.js, it’s set through the alternates.languages property in the Metadata API. Without correct configuration, search engines either show the wrong language version or treat the translations as duplicate content.
A hreflang mistake isn’t rare - it’s the norm. Ahrefs analyzed 374,756 domains using hreflang and found that 67% have at least one implementation error: 56.3% are missing x-default, 18% have no self-referencing tag, and 16.9% have hreflang pointing to a broken or redirecting page (Ahrefs, hreflang study).
For a team building sites on Astro and Next.js for clients expanding beyond the CIS, this isn’t an abstract statistic - almost every other multilingual site we audit has at least one of these problems. This article walks through a concrete hreflang implementation for Astro and for Next.js side by side, plus a checklist that covers what each framework’s documentation handles separately but never combines: canonical, hreflang, x-default, localized sitemaps, the lang attribute, and translated meta tags.
For a Russian-speaking team building a site for the US or European market, multilingual support isn’t a bonus layered on top of a finished site - it’s part of the architecture from day one: a RU version for the internal team and investors, an EN or local-language version for the market that traffic and ads target. If hreflang is misconfigured, Google may show the Russian version in US search results instead of the English one, or, conversely, treat both versions as duplicates and demote both in rankings at once.
Hreflang: the rel=“alternate” hreflang=”…” attribute on a <link> tag inside head, or in a sitemap, that tells a search engine that several URLs are language or regional versions of the same page.
Why a multilingual site needs hreflang and what you lose without it
Without hreflang, the search engine decides on its own which language version to show - based on IP, browser language, or link authority, not on whether a translation actually exists. This creates three concrete problems.
First, cannibalization: if the RU and EN versions of a page aren’t linked via hreflang, Google may treat them as duplicate content in different languages and demote both in search results instead of ranking each one in its own region.
Second, the wrong version in search results: a user in Germany searches for a product in English but sees the Russian-language page in results - simply because it has a stronger link profile. Click-through rate drops, and conversion drops with it.
Third, loss of signals between versions: without hreflang, each language version grows as a separate site - there’s no way to consolidate links, authority, and click history into a single domain-wide picture.
For a site targeting the US or European market, this loss is especially costly: ad budget and SEO effort go into the English-language version, but without correct hreflang, part of the organic traffic never reaches the right page.
URL architecture before code: subdomain, folder, or domain
The URL structure you choose determines what every hreflang tag will look like, so it’s worth settling before writing i18n configuration.
Three viable options: example.com/en/, en.example.com, and a separate domain like example.de for a specific country. For most multilingual B2B sites on Astro or Next.js, a folder (/en/, /de/) is optimal - it inherits the main domain’s authority and is simpler to set up than a subdomain or a separate domain in terms of DNS and certificates.
If a site is migrating from an older stack to Astro or Next.js, it makes more sense to design the URL structure for multilingual support from the start rather than adding it later - otherwise you’ll end up configuring redirects and hreflang twice. We covered this in more detail in migrating from WordPress to Next.js and in moving from Webflow to Astro - in both cases, the language structure is decided at the routing-selection stage, not after launch.
Hreflang for a multilingual site in Astro
Astro has two independent hreflang mechanisms: an automatic one, via @astrojs/sitemap, and a manual one, via the astro:i18n module in each page’s head. A complete implementation needs both.
Sitemap hreflang is configured in the i18n block of the integration’s configuration:
@astrojs/sitemap configuration with hreflang
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://example.com',
integrations: [
sitemap({
i18n: {
defaultLocale: 'ru',
locales: {
ru: 'ru-RU',
en: 'en-US',
de: 'de-DE',
},
},
}),
],
});
After the build, every entry in sitemap.xml automatically gets an xhtml:link rel=“alternate” for every language version of the page - there’s no need to manually write out the alternates for the sitemap.
For the link rel=“alternate” hreflang tags in a page’s head, use the getAbsoluteLocaleUrl function from the astro:i18n module - it builds an absolute URL based on your locale configuration:
Hreflang tags in an Astro layout
---
import { getAbsoluteLocaleUrl } from 'astro:i18n';
const locales = ['ru', 'en', 'de'];
const path = Astro.url.pathname.replace(`/${Astro.currentLocale}`, '');
---
{locales.map((locale) => (
<link
rel="alternate"
hreflang={locale}
href={getAbsoluteLocaleUrl(locale, path)}
/>
))}
<link
rel="alternate"
hreflang="x-default"
href={getAbsoluteLocaleUrl('ru', path)}
/>
Google requires an absolute URL with a protocol (https://example.com/en/…), not a relative path; getAbsoluteLocaleUrl already builds it in the right format, so there’s no need to manually concatenate domain and path.
Hreflang in Next.js: the Metadata API and alternates.languages
In Next.js, hreflang isn’t set through individual tags but through the alternates.languages object in the Metadata API - Next.js renders the link rel=“alternate” hreflang tags in the head from it automatically.
Hreflang via the Metadata API in Next.js
import type { Metadata } from 'next';
export const metadata: Metadata = {
metadataBase: new URL('https://example.com'),
alternates: {
canonical: '/en',
languages: {
'ru-RU': '/ru',
'en-US': '/en',
'de-DE': '/de',
'x-default': '/en',
},
},
};
With metadataBase set, relative paths in languages are automatically turned into absolute URLs at build time - the same principle as getAbsoluteLocaleUrl in Astro, just without an explicit function call.
For dynamic routes (a product page, a blog article), you set the same field inside generateMetadata, substituting the locale parameter into the path for each language - the code is identical to the static version, except the languages values are assembled in a loop over the list of supported locales instead of being hardcoded.
X-default and link reciprocity
The x-default tag specifies which version to show a user whose language doesn’t match any of the ones listed - usually the English version or a language-selection page. Without it, according to Ahrefs, 56.3% of domains in the sample can’t determine a default version - the most common hreflang mistake in the study.
The second mandatory condition is reciprocity: if the /ru/ page links to /en/ via hreflang, the /en/ page must link back to /ru/. Google ignores a one-directional hreflang link entirely, not just in one direction.
Both frameworks take this work off the developer’s hands at the configuration level: in Astro the locale list is defined once in i18n.locales, in Next.js in languages, so adding a new language means adding a single entry rather than editing every language version of every page by hand.
Checklist before launching a multilingual site
Six items to close out before launch - each one is covered individually in framework documentation, but they’re rarely gathered in one place.
- Canonical on every language version points to itself, not to the default-language version
- Hreflang for every language pair is reciprocal - A links to B, B links to A
- X-default is present and points to a sensible default version (usually EN)
- Localized sitemaps - either one sitemap with hreflang annotations or separate sitemaps per language, never a mix of formats
- The lang attribute in html lang=”…” matches the page’s actual language, not hardcoded to ru for every locale
- Translated meta tags - title, description, and og:title are translated, not duplicated from the default language
The last item most often fails not because of code but because of a content-process gap: the team translates the page copy but forgets the meta fields in the CMS. If content and translations live in a Headless CMS, you should plan for a localized SEO field block from the start - we covered choosing between Sanity, Contentful, and Strapi for multilingual B2B sites in comparing Headless CMS platforms.
Common hreflang mistakes and how to find them
Ahrefs identifies three mistakes that occur most frequently, and all three can be found without paid tools.
Missing self-referencing tag (18% of domains in the sample) - the /en/ page must include a hreflang link to itself, not just to the other languages.
Broken or redirecting hreflang links (16.9%) - if a language version moved to a new URL but hreflang still points to the old address via a 301 redirect, Google treats this as an error and may ignore the pair entirely.
Incorrect language codes - using en instead of en-US where the distinction between regional versions matters (for example, en-US and en-GB with different pricing), or a country code in place of a language code (hreflang=“US” instead of hreflang=“en-US”).
One practical note: Google removed the “International Targeting” report from Search Console in 2022, and there’s no longer a built-in report for hreflang errors in the interface. Checking pair reciprocity and correctness now requires viewing page source directly or using a crawler like Screaming Frog, which crawls every language version and cross-checks the pairs against each other.
Real case: an RU + EN site for entering the US market
In a typical US market-entry project for a product site of 40-60 pages, with a RU version for the internal team and an EN version for the market, the audit almost always turns up the same thing: the sitemap is built without an i18n block, and link rel=“alternate” hreflang tags are completely absent from page heads - they were simply forgotten during routing setup when the site moved to Astro.
After adding hreflang and correctly configuring the sitemap, the number of indexed EN-version pages in Google typically grows over 3-4 weeks - before that, Google treated them as duplicates of the RU content and didn’t show them in US search results at all. The exact speed depends on the specific domain’s crawl budget, but the direction holds consistently across projects with a similar structure.
Who this is for
Hreflang at this level of detail is needed by companies that publish content in at least two languages and target different countries or regions simultaneously - a product SaaS with RU and EN versions, a European company’s marketing site with DE and EN, or a localized landing page for a specific market. If a site is being built from scratch for multiple languages, the routing architecture and hreflang should be planned at the framework and project-structure selection stage, not bolted on after the site is already live - we covered this in building a site from scratch.
Frequently Asked Questions
What is hreflang for a multilingual site and why is it needed?
Hreflang for a multilingual site is an attribute in link rel=“alternate” or in a sitemap that tells a search engine that several URLs are translations of the same page into different languages. Without it, Google chooses which language version to show on its own, relying on link strength and other indirect signals rather than on whether a translation exists - as a result, a user may see the wrong page version, and the search engine may treat the translations as duplicate content.
Does each language version need its own canonical tag?
Yes. The canonical for each language version must point to itself, not to the default-language page. A self-referencing canonical paired with hreflang is the standard combination: canonical states that this is the original version of a specific URL, and hreflang states that translations exist. A canonical pointing to a different language effectively cancels out that language version for Google’s index.
How do you add hreflang to a sitemap in Astro?
Through the i18n block in the @astrojs/sitemap configuration: specify defaultLocale and a list of locales with language codes. At build time, the integration automatically adds an xhtml:link rel=“alternate” hreflang for every language version of a page in sitemap.xml - no need to manually generate alternates for each URL individually.
How do you implement hreflang in Next.js without mistakes?
Through the alternates.languages property in the Metadata object or in generateMetadata - specify the language code in the format ru-RU or en-US as the key and a relative or absolute path as the value, plus an x-default entry. With metadataBase set, Next.js builds the absolute URLs itself and renders the correct link tags in the page head.
What is x-default and when is it required?
X-default is the hreflang value that specifies which page version to show users whose language doesn’t match any of the ones listed. It’s not formally required, but according to Ahrefs, its absence is the most common hreflang mistake: 56.3% of domains in a sample of 374,756 sites didn’t specify x-default at all. For a site targeting multiple countries, x-default should always be set - usually the English version or a language-selection page.
Bottom line:
- Hreflang isn’t optional for a multilingual site targeting markets beyond the CIS - without it, part of the organic traffic never reaches the right language version
- In Astro, the @astrojs/sitemap and astro:i18n combination covers both the sitemap and the head tags without duplicating logic manually
- In Next.js, the entire configuration is a single alternates.languages object in the Metadata API, including x-default
- The six-point checklist (canonical, hreflang, x-default, sitemap, lang, translated meta) should be run before every new language launch, not after the first traffic drop
If you’re launching a site in multiple languages or adding a new language version to an existing one - describe your current architecture to the Exceltic.dev team. We’ll check hreflang, sitemap, and canonical before launch and point out specific mistakes, if there are any.