Technical SEO

Implementing hreflang with Google Tag Manager in three steps

How to generate hreflang from the language switcher without development work, and where the method reaches its limits.

Page /en/blog/ENDEGTM<head>hreflang enhreflang derendered
Eoghan HennFirst published: August 11, 2017Last updated: October 15, 20269 min read
In short
  • You can implement hreflang via Google Tag Manager without development work. The URLs come from the language switcher, and a Custom HTML tag writes the links into the head.
  • Google does not document hreflang via JavaScript. Our own test from 2017 suggests that Google evaluates the links in the rendered HTML.
  • hreflang in the source code remains the safer option. Tag Manager works as a stopgap.

Not every CMS can output hreflang, and not every team has time to implement it in the template. If Google Tag Manager is installed, there is a way without development work. You need a website with at least two language or country versions that link to each other, for example via a language switcher.

The task

Every page needs hreflang links to all its language and country versions, including a reference to itself. For a blog page in English and German, this looks like this on both versions:

<link rel="alternate" href="https://www.example.com/en/blog/" hreflang="en"> <link rel="alternate" href="https://www.example.com/de/blog/" hreflang="de">

Doing this by hand for every page is not feasible. Tag Manager should therefore generate the links for all pages at once. To do this, it needs the URLs of the other versions on every page. These are usually already in the language switcher. The hreflang guide explains the basics of codes and sets.

Step 1. Reading the links from the language switcher

Tag Manager has a variable type “DOM Element” that reads values from a page's HTML. You create one such variable for each language or country version.

  1. Under “Variables”, click “New” in the user-defined variables and choose the type “DOM Element”.
  2. Select “CSS Selector” as the selection method and give the variable the name of the target version, for example “Link to German version”.
  3. You find the selector in Chrome. Right-click the link in the language switcher, choose “Inspect”, then “Copy” and “Copy selector” on the highlighted code. An example is #menu-item-443-de > a.
  4. Enter “href” as the attribute name. Without an attribute, the variable returns the link text, for example “DE”.
  5. Repeat for every other version.

In preview mode, check whether the variables are correct. On every page, they must show the URLs of the other versions, not those of the current page. The copied selector is the quick route. If you know CSS selectors, you can usually find more robust ones that survive template changes better.

Step 2. One tag per language version

For each language version, create a tag of the type “Custom HTML”. The script for the English pages generates two links. The first uses the built-in variable {{Page URL}} to reference the page itself, with a fixed language code. The second takes the URL of the German version from the variable in step 1.

<script> // Self-reference of the English page var link_en = document.createElement('link'); link_en.rel = 'alternate'; link_en.href = '{{Page URL}}'; link_en.hreflang = 'en'; document.head.appendChild(link_en); // Reference to the German version from the language switcher var link_de = document.createElement('link'); link_de.rel = 'alternate'; link_de.href = '{{Link to German version}}'; link_de.hreflang = 'de'; document.head.appendChild(link_de); </script>

Repeat the second block for every other version. As the trigger, use all pages of the English version. For the German pages, create a second tag with “de” in the self-reference and the variable for the English version. With many versions, a lookup table lets you do it with a single tag, see below.

Older guides use jQuery('head').append(). The version above works without jQuery and does the same.

One tag for all versions with a lookup table

With many language and country versions, one tag per version becomes hard to manage. A lookup table then returns the hreflang code of the current page, in the example per domain. Further versions each get one row in the table and one entry in the script. How lookup tables work and what else you can use them for, such as GA4, is covered in the guide Lookup tables in Google Tag Manager.

Input (Page Hostname)Output (hreflang code of the page)
www.example.comen
www.example.dede

If the versions are in directories, use a RegEx table with {{Page Path}} as the input instead of the lookup table. It checks patterns such as ^/de/ from top to bottom, takes the first match and can set a default value. With either variant, a single Custom HTML tag is enough for all pages with a language switcher.

<script> (function () { // URLs of the versions from the variables in step 1 var versionen = { 'en': '{{Link to English version}}', 'de': '{{Link to German version}}' }; // Self-reference via the lookup table versionen['{{hreflang code of the page}}'] = '{{Page URL}}'; Object.keys(versionen).forEach(function (code) { var url = versionen[code]; if (!url || url === 'undefined') return; var link = document.createElement('link'); link.rel = 'alternate'; link.hreflang = code; link.href = url; document.head.appendChild(link); }); })(); </script>

The script takes the URLs of the versions from the DOM variables in step 1 and overwrites the entry for the current language with the page's own URL. It skips empty variables, so pages without a counterpart do not get broken links. In preview mode, check on one page per version whether the lookup table returns the right code.

Step 3. Adding x-default (optional)

In 2017, the root URL of searchviu.com redirected users to the German or English homepage depending on their browser language. This is the classic case for x-default. A third tag adds a link with x-default to the root URL on all homepage versions.

<script> var link_xd = document.createElement('link'); link_xd.rel = 'alternate'; link_xd.href = 'https://www.example.com/'; link_xd.hreflang = 'x-default'; document.head.appendChild(link_xd); </script>

If instead a whole language version should be the default, fire the link with {{Page URL}} on its pages and the link with the matching variable from step 1 on all other versions.

Does it work?

Our own test from 2017 suggests that Google evaluates hreflang in the rendered HTML, described in the JavaScript SEO experiment on hreflang. Google itself does not document hreflang via JavaScript. Tag Manager writes the links into the head of the rendered HTML.

Before the implementation, two searchVIU URLs ranked for brand searches on google.de, the German homepage and the root URL with an English snippet. About a week after the implementation, Google only showed the German version on google.de. Our explanation is that Google understood the language structure of the website better with hreflang and x-default on the root URL. It is one case on one website for brand searches, not a test with a control group.

MethodAdvantageDisadvantage
HTML in the source codeGoogle reads it without rendering, every tool sees itNeeds a CMS, plugin or development work
XML sitemapIndependent of the template, good for large websitesSeparate maintenance, errors surface late
Google Tag ManagerCan be implemented without development workOnly visible after rendering, depends on selectors

Limits of the method

  • Tools that only read the raw source code do not find the links. Check in the rendered HTML.
  • Google renders pages with a delay. It can take longer for the annotations to take effect than with hreflang in the source code.
  • The links must end up in the head. If an invalid element such as img or iframe appears there first, Google assumes the head has ended and does not read the following elements.
  • If the template changes, the selector may no longer match, and the variables return empty or wrong values.
  • Pages without a counterpart, such as products available in only one country, need a trigger condition. The tag then only fires if the link exists in the language switcher.

If your CMS or a plugin can output hreflang, that is the better choice. Tag Manager works as a stopgap or for websites whose templates you cannot access. The method also works across domains, as long as the versions link to each other.

What has changed since?

The “International Targeting” report in Search Console, where hreflang annotations appeared in 2017, no longer exists. Today you check whether Google sees the links with URL Inspection and the rendered HTML under “View crawled page”. Tag Manager's preview mode now runs via Tag Assistant. Google's hreflang documentation still names HTML, HTTP headers and sitemaps and does not mention JavaScript. For the canonical, Google writes that one set via JavaScript is picked up during rendering, but recommends the HTML. There is no corresponding statement for hreflang. The result above is an observation from 2017.

Common mistakes

  • The variable returns the page's own URL on every page instead of the URL of the other version.
  • The self-reference is missing because the tag only adds the other versions.
  • The tag of one language version fires on all pages, so the language codes are wrong on half of them.
  • The script uses jQuery, which is not loaded on the website.
  • The language switcher links to the homepage of the other language instead of the matching page.

Frequently asked questions

Can I implement hreflang with Google Tag Manager?

Yes. A Custom HTML tag writes the links into the head. On searchviu.com in 2017, by our observation, they took effect about a week after implementation. Google does not document this method.

Is hreflang via GTM as good as in the source code?

The source code is safer. Google first has to render the page for GTM annotations, and some tools do not see the links at all.

Why does my SEO tool not find the hreflang links?

Many tools only read the raw source code. The links from Tag Manager only appear in the rendered HTML.

Do I need a multilingual plugin?

No. All that matters is that the versions link to each other so that Tag Manager can read the URLs.

What is a lookup table in Google Tag Manager?

A user-defined variable that returns a set value depending on the value of another variable, here the hreflang code per hostname. More in the guide Lookup tables in Google Tag Manager.

Does this work across several domains?

Yes, as long as the language switcher links to the matching pages on the other domains.

searchVIU renders every page in Google Chrome and stores the raw source code and the rendered HTML. That way you see whether hreflang links from Tag Manager actually arrive in the head. The hreflang topic report shows what has changed since the last crawl. Find out more on the SEO Crawler page.

Request a demo

Sources

Retrieved on October 15, 2026. If something is out of date or missing, let us know.

  1. Google, Tell Google about localized versions of your page: https://developers.google.com/search/docs/specialty/international/localized-versions · As of Sep 21, 2026
  2. Google, JavaScript SEO basics: https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics · As of Mar 4, 2026
  3. Google, Valid elements in the head: https://developers.google.com/search/docs/crawling-indexing/valid-page-metadata · As of Dec 10, 2025
  4. Tag Manager Help, Preview and debug: https://support.google.com/tagmanager/answer/6107056
  5. Tag Manager Help, User-defined variables: https://support.google.com/tagmanager/answer/7683362
  6. Search Console Help, The International Targeting report has been deprecated: https://support.google.com/webmasters/answer/12474899