Back to blog
Hreflang Validator9 min readSeptember 3, 2026

How to Implement Hreflang in WordPress: WPML, Polylang and Without a Plugin

A practical guide to hreflang in WordPress. When WPML is worth it, when Polylang is enough, how to do it without a multilingual plugin, and the errors each method produces.


In WordPress you will almost never write hreflang tags by hand: the multilingual plugin generates them. The problem is that each plugin generates them differently, with its own blind spots, and none of them tells you when the result is wrong. This guide covers the three real scenarios — WPML, Polylang and no multilingual plugin — with what to configure in each and what tends to break.

Before picking a plugin: decide your URL structure

WordPress supports subdirectory, subdomain or a dedicated domain per language, and whichever plugin you pick has to support the one you need. It is the most expensive decision to change later, so make it before installing anything. If you are still weighing it up, the full comparison — authority, cost, geographic signals — has its own guide:

When in doubt, subdirectory. It is what most multilingual WordPress sites use and the best supported option across plugins.

Option 1: WPML

WPML is the most complete multilingual plugin in the WordPress ecosystem. It handles hreflang automatically for all translated content, including the return tag and x-default. It is paid, from around $39 a year. It is the sensible choice if you run WooCommerce, custom fields, or enough content that manual maintenance is not realistic.

  1. 1.Install and activate WPML Multilingual CMS. To translate theme strings and widgets you will also need WPML String Translation.
  2. 2.Go to WPML → Languages and add your site languages. This is also where you choose the URL format: subdirectory, subdomain or own domain.
  3. 3.Under WPML → Languages → "Browser language redirect", turn off automatic redirection if it is on: it can stop Google crawling versions that do not match its IP.
  4. 4.Translate each page from the WPML panel in the editor. Only content linked as a translation joins the hreflang group.
  5. 5.WPML injects the tags into every page head automatically. There is nothing else to configure.

What usually goes wrong with WPML

  • Translated but unlinked content: if you duplicate a page instead of translating it from the panel, WPML leaves it out of the group and that URL ends up with no hreflang.
  • Partial translations: if a page exists in Spanish and French but not English, the group will only include the two that exist. That is correct behaviour, but worth knowing before you panic during validation.
  • Aggressive caching: some cache plugins serve one language’s cached page on another language’s URL, and the wrong hreflang with it. Purge the cache before validating.

Option 2: Polylang

Polylang has a genuinely usable free version for small and mid-sized sites. It handles translations of posts, pages, categories and tags, and adds hreflang automatically to linked content. The Pro version adds WooCommerce support, custom post types and slug translation.

  1. 1.Install Polylang from the official WordPress repository.
  2. 2.Go to Languages → Languages and add each language with the correct locale code (es_ES, en_US, fr_FR). This value ends up in the hreflang tag, so a mistake here propagates across the whole site.
  3. 3.Under Languages → Settings choose the URL format. "The language is set from the directory name in pretty permalinks" is the subdirectory option.
  4. 4.On every post or page use the "Languages" panel in the editor to link the translations to each other. Without that link there is no hreflang group.
  5. 5.Check under Languages → Settings whether "Hide URL language information for default language" is set the way you want: if enabled, the main language is served with no prefix and hreflang must reflect that.

Free Polylang does not translate WooCommerce products. If you run a shop, either move to Polylang Pro with the WooCommerce connector or your products will sit outside the hreflang group even though the rest of the site is fine.

Option 3: no multilingual plugin

If the translations already exist as standalone pages and you do not want to set up a whole multilingual stack, you can inject the tags yourself. It makes sense with few pages and a stable structure; it stops making sense as soon as the content grows.

The clean way is to hook into wp_head from your child theme’s functions.php, with an explicit translation map:

php
// child theme functions.php
add_action('wp_head', function () {
    // page ID => [ hreflang code => URL ]
    $groups = [
        12 => [
            'en' => 'https://example.com/services/',
            'es' => 'https://example.com/es/servicios/',
        ],
        34 => [
            'en' => 'https://example.com/services/',
            'es' => 'https://example.com/es/servicios/',
        ],
    ];

    $id = get_queried_object_id();
    if (!isset($groups[$id])) {
        return;
    }

    foreach ($groups[$id] as $lang => $url) {
        printf(
            '<link rel="alternate" hreflang="%s" href="%s" />' . "\n",
            esc_attr($lang),
            esc_url($url)
        );
    }
    // x-default pointing at the default language
    printf(
        '<link rel="alternate" hreflang="x-default" href="%s" />' . "\n",
        esc_url($groups[$id]['en'])
    );
});

Note that every page in the group declares the full group, itself included. That is why the map repeats the same URLs for IDs 12 and 34: self-reference and the return tag are mandatory, and this method breaks them easily if you add a translation and forget to update the array.

Never add these tags through a "insert code in head" plugin that applies site-wide: you would declare the same hreflang group on pages that do not belong to it, and Google would discard all of them.

Verify before assuming it works

None of the three methods warns you when the output is wrong: the plugin generates tags and treats its own output as correct. Take every URL in one group — the English version, the Spanish one and any other — and validate them together. Hreflang faults only show up when you compare the pages in a group against each other, never by looking at one alone.

Try the tool for free

Analyze your URLs with Hreflang Validator by iRankly. No sign-up, no credit card.

Use tool for free

If you are on another platform, or want the conceptual detail of what each attribute does:


Try the tool for free

Analyze your URLs with Hreflang Validator by iRankly. No sign-up, no credit card.

Use tool for free