# General Translation Knowledge Base: What is hreflang, and how do I set it up?
URL: https://generaltranslation.com/en-GB/kb/what-is-hreflang.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Hreflang tells Google which URLs are language or regional versions of the same page, so it can show the right one in search results. Add it with HTML link tags, HTTP Link headers, or a sitemap, make every link reciprocal, and use valid codes such as en-GB, not en-UK.
Category: Multilingual SEO
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

Hreflang only works once each language already has its own URL, because it maps one URL to another. If you haven&#39;t picked that structure yet, start with [subdirectories, subdomains, or country domains](/kb/multilingual-url-structure), then come back to annotate it.

## What hreflang does and doesn&#39;t do

Hreflang groups the URLs of one page into a cluster and labels each with a language, and optionally a region. When a German speaker&#39;s search matches your English pricing page, Google can show the German URL from the same cluster instead. Google&#39;s [localized versions guide](https://developers.google.com/search/docs/specialty/international/localized-versions) says it may find alternate versions without annotations, but recommends declaring them explicitly.

It&#39;s easy to expect more from it than it delivers:

* **It isn&#39;t a redirect.** Hreflang helps Google choose which URL appears in search results; it does not guarantee a particular result. A visitor who clicks lands on that URL, and anything after that is your server&#39;s decision (covered in [how a site should pick the language it serves](/kb/locale-routing-seo)).
* **It doesn&#39;t detect language.** Google says it uses neither hreflang nor the HTML `lang` attribute to detect a page&#39;s language; it determines the language from the content with its own algorithms.
* **It doesn&#39;t fix untranslated pages.** Google treats localised versions as duplicates only when the main content remains untranslated. Translating just the navigation and footer leaves a page that hreflang can label but can&#39;t make distinct.

## How do you add hreflang?

Google treats HTML, HTTP headers, and sitemaps as equivalent and says using more than one has no benefit in Search. Pick the one that fits how your site is built.

### HTML link elements

Put one `<link rel="alternate">` per version, including the current page, inside the `<head>`. The set is identical on every version. URLs must be fully qualified (`https://example.com/de/preise`, not `/de/preise`).

```html
<!-- In the <head> of /en/pricing, /de/preise, and /fr/tarifs alike -->
<link rel="alternate" hreflang="en" href="https://example.com/en/pricing" />
<link rel="alternate" hreflang="de" href="https://example.com/de/preise" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/tarifs" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/pricing" />
```

Don&#39;t combine hreflang with other attributes such as `media` in a single `<link>`, and check the rendered HTML: a link that ends up outside a well-formed `<head>` doesn&#39;t count.

### HTTP Link headers

For files without a `<head>`, such as PDFs, return the same cluster in a `Link` response header. Each URL sits inside angle brackets, and entries are comma-separated.

```http
Link: <https://example.com/en/guide.pdf>; rel="alternate"; hreflang="en",
      <https://example.com/de/leitfaden.pdf>; rel="alternate"; hreflang="de",
      <https://example.com/fr/guide.pdf>; rel="alternate"; hreflang="fr"
```

### XML sitemap

A sitemap keeps the annotations out of your pages. Declare the `xhtml` namespace, give each URL its own `<url>` entry, and list every version, including itself, under each one. Google says these child entries don&#39;t count towards the sitemap URL limit.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <url>
    <loc>https://example.com/en/pricing</loc>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/pricing"/>
    <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/preise"/>
    <xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/tarifs"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/pricing"/>
  </url>
  <url>
    <loc>https://example.com/de/preise</loc>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/pricing"/>
    <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/preise"/>
    <xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/tarifs"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/pricing"/>
  </url>
  <url>
    <loc>https://example.com/fr/tarifs</loc>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/pricing"/>
    <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/preise"/>
    <xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/tarifs"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/pricing"/>
  </url>
</urlset>
```

Place the sitemap at the site root when its page entries span several locale folders. Google&#39;s [sitemap guidance](https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap) describes directory scope and cross-site submission options; hreflang alternate URLs can point to other domains.

## The rules every method shares

* **Use the same complete set where practical, including each page itself.** Three language versions means three entries on each page. If maintaining a complete set is difficult, Google can process smaller reciprocal groups; prioritise return links between new versions and the original or dominant version.
* **Links must be reciprocal.** If page X lists page Y but Y doesn&#39;t list X back, Google ignores the pair. This stops another site from claiming to be your German version.
* **Optionally add `x-default`.** It identifies a fallback for unmatched languages, such as a language selector or a default-language version. It is not required for hreflang to work. Keep the fallback relevant to the page rather than sending every deep page to the homepage.
* **Codes are a language, then an optional region.** The language is ISO 639-1 (`de`), the region is ISO 3166-1 alpha-2 (`de-CH`), and case doesn&#39;t matter. A region alone is invalid, and a country code in the language slot means something else: `be` is Belarusian, not Belgium.
* **Scripts are allowed.** Google accepts ISO 15924 script subtags such as `zh-Hans` and `zh-Hant`, optionally with a region (`zh-Hans-US`). With `zh-TW`, it derives Traditional Chinese from the region.
* **Give regional variants a language-only fallback.** If you have `en-IE`, `en-CA` and `en-AU` pages, add an `en` page (it can be one of the three) for English speakers everywhere else.

Some valid locale codes still fail here. `es-419` (Spanish for Latin America) is a correct BCP 47 tag, but Google&#39;s hreflang supports only ISO 3166-1 regions, so annotate that page as `es` or with specific countries such as `es-MX` and `es-AR`.

## Should hreflang pages use self-referencing canonicals?

For distinct translated pages, the preferred canonical usually points to the page itself. Google&#39;s [duplicate URL guidance](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls) recommends a canonical in the same language when using hreflang, or the best substitute if no same-language canonical exists. Avoid pointing all translated pages at English.

Same-language regional pages need a separate decision. If `en-US` and `en-GB` are duplicates, you can choose a preferred English canonical and use hreflang to describe regional targeting. Keep canonical and hreflang signals consistent, and inspect Google&#39;s selected canonical in Search Console. A declared canonical is a strong signal, not a guarantee.

```html
<!-- In the <head> of https://example.com/de/preise -->
<link rel="canonical" href="https://example.com/de/preise" />
<link rel="alternate" hreflang="en" href="https://example.com/en/pricing" />
<link rel="alternate" hreflang="de" href="https://example.com/de/preise" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/tarifs" />
```

Two related details from the same page: Google ignores `rel="canonical"` links that carry an `hreflang` attribute, so keep the two link types separate, and for canonicalisation it prefers URLs that belong to an hreflang cluster. Use the canonical URL of each version in the cluster, never a variant with tracking parameters.

## What are common hreflang errors?

* **Missing return links.** First on Google&#39;s list of common mistakes. Usually one locale&#39;s template omits the block, or a new language was added to some pages but not others.
* **Invalid codes.** Google ignores reserved region codes such as `UK` and `EU`, so the region in `en-UK` has no effect; use `en-GB`. Unsupported regions such as `419` in `es-419` aren&#39;t recognised either.
* **Relative or mixed-protocol URLs.** Alternates must be fully qualified, and Google warns against listing `http://` URLs in hreflang when the site is on HTTPS.
* **Alternates that don&#39;t lead directly to the intended indexable page.** Use the final canonical URL, not a redirect or an error page. A `noindex` page is excluded from Search, so it cannot serve as the result you want Google to show. Check the destination content and its return links as well as its status code.
* **Pages where only the template is translated.** Hreflang can&#39;t make them distinct, and Google says the repeated content makes a poor search experience.

## How do you check that hreflang works?

Search Console&#39;s International Targeting report is gone: Google [deprecated it](https://support.google.com/webmasters/answer/12474899) along with its country targeting setting, and says it continues to support and use hreflang. Use these checks instead:

* **Spot-check one page.** Fetch the HTML and confirm the page lists itself and every sibling:

  ```bash
  curl -s https://example.com/de/preise | grep -o '<link[^>]*hreflang[^>]*>'
  ```

* **Test a live page.** Google&#39;s guide points to two third-party tools it doesn&#39;t maintain: [Merkle&#39;s hreflang tag tester](https://technicalseo.com/seo-tools/hreflang/) for a single URL and [Aleyda Solis&#39;s hreflang generator](https://www.aleydasolis.com/english/international-seo-tools/hreflang-tags-generator/) for writing tags.

* **Crawl the whole site.** A crawler checks reciprocity across every page. [Screaming Frog&#39;s hreflang audit](https://www.screamingfrog.co.uk/seo-spider/tutorials/how-to-audit-hreflang/) reports missing return links, non-200 and `noindex` alternates, invalid codes, and alternates that aren&#39;t the canonical URL.

* **Validate the sitemap.** Run `xmllint --noout sitemap.xml` to catch malformed XML before Google does.

## Setting up hreflang with General Translation

General Translation&#39;s `gt-next` gives every language its own URL through [`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware), the prerequisite for hreflang. With localised slugs from its `pathConfig` option, such as `/fr/a-propos`, each localised path is the `href` you list in the cluster.

The annotations themselves stay in your app. Next.js writes the `<link rel="alternate" hreflang>` tags from `metadata.alternates.languages`, and [How do I set up SEO for a multilingual Next.js app?](/kb/multilingual-nextjs-seo) has a helper that returns the canonical and every alternate in one call. Use the configured locales as candidates, then list only the versions of each page you have actually published.

If you route a locale under an alias, such as `/cn` for Chinese, the [locale alias SEO guide](/docs/react/nextjs/locale-alias-seo) uses [`resolveCanonicalLocale`](/docs/react/nextjs/reference/functions/resolve-canonical-locale) so hreflang carries the BCP 47 code `zh` while the URL keeps `/cn`.

## Key takeaways

* Hreflang maps URLs to languages and regions for search results. It doesn&#39;t redirect visitors or tell Google what language a page is in.
* Use one method (HTML `<link>`, HTTP `Link` header for PDFs, or sitemap `xhtml:link`); Google treats them as equivalent.
* Aim for a complete set of equivalent URLs with self-references and reciprocal links. Smaller reciprocal groups can still be processed; `x-default` is optional.
* Codes are ISO 639-1 plus an optional ISO 3166-1 alpha-2 region or ISO 15924 script: `en-GB`, not `en-UK`; `es-MX`, not `es-419`.
* Use consistent canonicals, usually self-referencing for distinct translations. Treat duplicate regional pages in the same language separately, and check return links with a crawler.

## Related questions

- [How do I set up SEO for a multilingual Next.js app?](https://generaltranslation.com/en-GB/kb/multilingual-nextjs-seo.mdx): Give each language its own prefixed URL, such as /en/about and /es/about, then publish html lang, a self-referencing canonical, hreflang alternates with x-default, a translated title and description, and a sitemap entry for each published language. Mark unpublished versions noindex. General Translation's gt-next routes each language to its own URL and translates titles with getGT.
- [Subdirectory vs. subdomain vs. ccTLD for multilingual SEO](https://generaltranslation.com/en-GB/kb/multilingual-url-structure.mdx): Use subdirectories such as example.com/de/ when one product shares a site and release process. Only a ccTLD signals a country, subdomains allow separate hosting, Google discourages ?lang= parameters, and it treats vanity domains such as .io and .ai as generic.
- [Language redirects and SEO: URL, cookie, or Accept-Language?](https://generaltranslation.com/en-GB/kb/locale-routing-seo.mdx): Let the URL decide the language of every page you want indexed, because Googlebot sends no Accept-Language header and crawls mostly from US addresses. Use a saved cookie choice, then Accept-Language, only to pick where a neutral root such as / redirects, with a temporary 302 or 307.
- [Language switcher best practices: UX, SEO, and accessibility](https://generaltranslation.com/en-GB/kb/language-switcher.mdx): Name each language in its own language, such as Deutsch or 日本語, and don't use flags, which represent countries. Link each option to the same page in that language with a crawlable link, remember the choice in a cookie, and never auto-redirect a localised URL.
- [Localisation testing checklist for multilingual apps](https://generaltranslation.com/en-GB/kb/multilingual-app-launch-checklist.mdx): Test nine things in every supported locale on a production build: whole journeys including error states and emails, language switching, plurals, currency and time zones, text expansion, right-to-left layout, accessibility, translation delivery, and crawlable language URLs. Give each check an explicit pass condition.

## Previous and next questions

- Previous question: [What is a translation management system (TMS)?](https://generaltranslation.com/en-GB/kb/what-is-a-translation-management-system.mdx)
- Next question: [What is ICU message format?](https://generaltranslation.com/en-GB/kb/icu-message-format.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/en-GB/kb/sitemap.md) for every article.
