Fixing Hreflang in WordPress
WordPress has no hreflang support of its own, so the tags on a multilingual WordPress site come from whichever translation plugin, theme or SEO add-on you have installed, and sometimes from more than one of them.
Most errors trace back to that: two plugins writing competing tags, a plugin building codes from WordPress locales, translations linked in one direction only, untranslated posts served under a translated address, or a cache mixing up languages. This guide shows how to find what writes your tags, then fixes the common faults in WPML, Polylang and TranslatePress, and in multisite networks where each language is a separate site.
- Finding what writes your tags
- How the main translation plugins handle hreflang
- WPML: codes, display setting and fallbacks
- Polylang: locales, filters and imports
- TranslatePress and multisite networks
- Clashes with SEO plugins, caches and redirects
- The fix, step by step
- Keeping WordPress hreflang healthy
- Questions and answers
Finding what writes your tags
Open a translated post, view the source and search for hreflang. Note how many sets of
tags there are and where in the head they appear. Then list the active plugins. With WP-CLI:
wp plugin list --status=active --fields=name,version
Look for these families, any of which may output hreflang:
- Translation plugins such as WPML, Polylang and TranslatePress, which generate tags for the translations they manage.
- Multisite linking plugins such as MultilingualPress, which connect separate sites in a network and generate tags between them.
- Dedicated hreflang plugins, installed to fill a gap at some point and then forgotten.
- Themes and child themes, where a developer may have hard-coded tags in
header.phpor a function hooked towp_head. - SEO plugins such as Yoast SEO and Rank Math, which do not usually write hreflang themselves but do control canonicals and sitemaps, both of which interact with it.
To see which hooked function prints the tags, search the theme and plugin folders:
grep -rl "hreflang" wp-content/themes wp-content/plugins wp-content/mu-plugins
If more than one plugin appears in the results, deactivate them one at a time on a staging copy and reload the page source. The set of tags that disappears tells you which plugin wrote it.
How the main translation plugins handle hreflang
| Plugin | How it creates tags | Where codes come from | Typical fault |
|---|---|---|---|
| WPML | Adds tags for linked translations, with an option in its SEO settings to show alternative languages in the head | Each language's settings, including a hreflang code field | Codes left at defaults; posts set to fall back to the default language |
| Polylang | Adds tags automatically for translations that exist | Derived from the WordPress locale set for each language | Region missing or unexpected; translations linked on one side only after imports |
| TranslatePress | Adds tags for each published language of a page | Language settings and the URL slug chosen for each language | Custom slugs mistaken for codes; automatic translation left partly finished |
| Multisite with a linking plugin | The linking plugin adds tags between connected sites | Each site's language setting in the linking plugin | Posts not connected across sites; one site's permalinks changed |
Plugin menus and option names change between versions, so use the table as a guide to where to look rather than exact labels.
WPML: codes, display setting and fallbacks
WPML lets you set the hreflang value for each language when you edit the language list, so an incorrect
value can usually be fixed there without code. Check that British English is en-GB rather
than a bare en if you also run a US version, and that no value uses an underscore. In WPML's
SEO options, confirm that alternative languages are being shown in the head, and that the position setting
places the tags where you expect.
Watch the translation mode for each post type. WPML can be told to show the default language when a translation is missing. That is convenient for visitors, but it can mean a page at a translated address is in the original language, and if hreflang is also printed for it, the tag claims a translation that does not exist. For content you want found in each language, translate it; for post types that are not translated, do not let them join clusters.
If you need to change the output in code, WPML passes the list of tags through a filter keyed by hreflang value. For example, to drop a language that is still being translated:
add_filter( 'wpml_hreflangs', function ( $hreflangs ) {
unset( $hreflangs['nl'] ); // Dutch not ready yet
return $hreflangs;
} );
Remove a language this way only if you remove it from every page, so the cluster stays symmetrical.
Polylang: locales, filters and imports
Polylang derives hreflang from the locale you choose for each language, such as en_GB,
and converts it to the hyphen format. Depending on your set of languages, it may output only the language
part, for example en, or the language and region. If you need a specific value, the pll_rel_hreflang_attributes
filter receives the list keyed by code and lets you change it:
add_filter( 'pll_rel_hreflang_attributes', function ( $hreflangs ) {
if ( isset( $hreflangs['en'] ) ) {
$hreflangs['en-GB'] = $hreflangs['en'];
unset( $hreflangs['en'] );
}
return $hreflangs;
} );
Put code like this in a small custom plugin or a child theme's functions.php, not in
the parent theme, where an update would overwrite it.
Polylang only prints tags for translations that are linked together. After bulk imports, migrations or copying posts between sites, translations are often published but not linked, so each post prints only itself, or links exist on one side only. Open the post list, filter by language and look at the translation columns: a missing link icon marks a post that is not connected. Re-link them, one by one or with the import tool you used, and recrawl.
If the language switcher settings are set to detect the browser language on the home page, crawlers can be sent to a different language from the one they asked for. Check the behaviour with a request that has no cookies and no language header.
TranslatePress and multisite networks
TranslatePress
TranslatePress serves each language from a subfolder based on the slug chosen for it, and prints tags
for each published language of a page. Two faults come up often. First, a custom URL slug such as /uk/
is only an address; check that the language behind it is set up as English (United Kingdom) so the tag
reads en-GB, not something derived from the slug. Second, automatic translation may have
been switched on and later limited, leaving some pages partly translated while tags still claim the language.
Review the plugin's advanced settings for hreflang options and decide which pages are complete enough
to offer.
Multisite networks
Running one site per language in a multisite network keeps languages separate, but WordPress does not know that post 12 on the English site is post 40 on the French site. A linking plugin, such as MultilingualPress, stores those connections and prints the tags. Errors arise when posts are never connected, when a site's permalink structure changes so stored addresses redirect, or when one site in the network is on a different domain whose protocol or host differs from what the plugin expects. Check connections for your most important content first, then crawl each site.
Clashes with SEO plugins, caches and redirects
- Canonicals. Each translation must be its own canonical. If an SEO plugin, a theme or a custom field sets every language's canonical to the original post, the translations are declared duplicates and the cluster fails. Yoast SEO and Rank Math are designed to work with the major translation plugins, but custom canonical fields filled in during a migration can override that.
- Sitemaps. If your translation plugin outputs hreflang in the HTML and a separate tool adds it to the sitemap, the two can disagree. Keep one.
- Page caching. A cache must store each language separately. If language is chosen by cookie rather than by address, a cached page can serve French text on the English address with English tags. Addresses that carry the language, such as a subfolder, avoid this; if you rely on cookies, configure the cache plugin to vary by that cookie.
- Language redirects. Plugins and hosting rules that redirect by browser language can stop crawlers from reaching some versions. Prefer a suggestion over a redirect.
- Trailing slashes and permalinks. Changing permalink settings on one language, or on one site in a network, turns stored addresses into redirects.
The fix, step by step
Step 1
Find out what writes the hreflang tags
Run the Hreflang Checker on a translated post and on the home page of each language, then
view the source of the same pages and count the sets of hreflang tags. List the active plugins with WP-CLI
or the Plugins screen and search the theme, plugin and must-use plugin folders for hreflang.
On a staging copy, deactivate candidate plugins one at a time until you know exactly which one produces
each set of tags.
Step 2
Keep one source and switch off the rest
Choose the plugin that manages your translations as the only source of hreflang, because it is the one that knows which posts are linked. Remove hard-coded tags from the theme or child theme, deactivate standalone hreflang plugins that duplicate its output, and make sure no second tool adds hreflang to the sitemap. Reload the page source and confirm only one complete set of tags remains on each page.
Step 3
Correct the language and region codes
Check every value the plugin outputs. In WPML, edit the hreflang code in the language settings.
In Polylang, set the right locale for each language, or use the pll_rel_hreflang_attributes
filter if you need a different value. In TranslatePress, confirm each language is the right variant regardless
of its URL slug. Every value should be a hyphenated language code with an optional country code, such
as en-GB.
Step 4
Link every translation in both directions
Open the post and page lists filtered by language and look for translations that exist but are not connected, especially after imports or migrations. Connect them in the translation plugin, or in the linking plugin on a multisite network, so each version knows about every other version. Once linked, the plugin prints the full cluster on each page, which fixes the missing return tags the checker reports for those posts.
Step 5
Stop untranslated content appearing as a translation
Find post types or pages served in the default language at a translated address, whether through a WPML fallback setting, partial automatic translation in TranslatePress or a theme template that ignores the language. Translate the content that matters, and for the rest, stop offering it in that language so no tag claims a translation. Hreflang should only ever describe pages whose text really is in the labelled language.
Step 6
Check caching, redirects and canonicals
Request each language version without cookies and check it loads directly, without a browser-language redirect. Confirm the canonical on every translation points to itself, not to the original. Make sure the page cache stores each language separately, then purge it. If any SEO plugin or custom field was overriding canonicals, correct those settings, because a canonical to another language cancels the hreflang for that page.
Step 7
Recrawl and add checks to updates
Rerun the Hreflang Checker on each language and confirm one clean cluster per page with valid codes and working targets. Add a check to your routine after updating the translation plugin, the SEO plugin or the theme, and after any bulk import, because those are the changes that most often bring back duplicates, unlinked translations or altered codes. Keep the custom filter code in version control.
Keeping WordPress hreflang healthy
- One plugin owns translations and hreflang; nothing else adds tags.
- Custom changes live in a small plugin or child theme, never in the parent theme or a plugin's own files.
- Imports are followed by a check for unlinked translations.
- Language is always in the address, so caches and crawlers see one language per address.
- Plugin and theme updates are tested on staging with the Hreflang Checker before they reach the live site.
For cluster basics, see the Hreflang Guide. If you also run a Shopify store for some markets, the Shopify guide covers linking the two.
1. Does WordPress add hreflang tags by itself?
No. WordPress core has no built-in support for multiple languages of the same content, so it does not output hreflang. The tags come from a translation plugin such as WPML, Polylang or TranslatePress, from a multisite linking plugin, from a dedicated hreflang plugin or from code in the theme. That is why the first step in any WordPress fix is finding out which of these is writing the tags on your pages, and whether more than one is.
2. Why do I have two sets of hreflang tags in WordPress?
Two things are writing them. Common pairings are a translation plugin plus a standalone hreflang plugin installed earlier, or a translation plugin plus tags hard-coded in the theme by a previous developer. Search the theme and plugin folders for hreflang and deactivate candidates one at a time on a staging site to find the second source. Keep the translation plugin's output, because it knows which posts are linked, and remove the other.
3. Do Yoast SEO or Rank Math handle hreflang?
They do not usually generate hreflang themselves; that is left to the translation plugin. They do control canonical tags and XML sitemaps, which affect how hreflang works. Make sure each translation's canonical points to itself, not to the original language, and that you are not adding hreflang to sitemaps with one tool while the translation plugin adds it to the HTML. Both SEO plugins are designed to work alongside the major translation plugins.
4. How do I change the hreflang code Polylang outputs?
Polylang builds the value from the locale you set for each language, so the first step is choosing the correct locale, for example English (United Kingdom). If you need a value Polylang does not produce, such as adding a region where it outputs only the language, use the pll_rel_hreflang_attributes filter in a small custom plugin or a child theme. It receives the list of codes and addresses, so you can rename a key before the tags are printed.
5. Why are some translated posts missing from the hreflang cluster?
Usually because the translations exist but are not linked to each other in the translation plugin. This is common after imports, migrations and copying content between sites, where posts arrive as separate items with the right language but no connection. Filter the post list by language and look for missing translation links, then connect them. Once linked, the plugin prints the full cluster on every version, and the missing return tag errors disappear.
6. Is multisite or a single site better for hreflang?
Both can produce correct hreflang. A single site with a translation plugin keeps translations linked automatically and is simpler for most businesses. A multisite network suits teams that run each language or country independently, with different content, plugins or editors, but needs a linking plugin to connect equivalent posts, and those connections must be maintained. Choose based on how your teams work, then check the output with a crawl either way.
7. Can a caching plugin break hreflang?
Yes, when the same address can show more than one language. If the language is chosen by cookie or browser setting rather than by the address, the cache may store one language and serve it to everyone, so the text and the tags no longer match. Put the language in the address, such as a subfolder, or configure the cache to vary by the language cookie. Always purge the cache after changing translation or hreflang settings.
The Hreflang Checker follows each cluster of locale pages and validates return tags, language codes, x-default, sitemap consistency, target reachability and canonical alignment.
Open Hreflang Fixes โ