If your WordPress site exists in more than one language, hreflang tags are how you tell search engines which version to serve which user. Get them right and German visitors land on your German pages; get them wrong and Google either guesses or ignores your annotations entirely.
Does WordPress add hreflang automatically?
No. WordPress core has no hreflang support of any kind. Installing WordPress in another language, or adding a language pack, changes the admin and theme strings — it does not create alternate URLs and it does not emit a single hreflang annotation. Core's own sitemaps do not carry alternate-language annotations either.
This surprises people because WordPress has genuinely good internationalization plumbing for interface strings, documented in the Plugin Handbook. But translating your theme's "Read more" button is a different problem from publishing your content in four languages at four URLs. hreflang belongs to the second problem, and you have to solve it yourself.
What you need before you start
hreflang annotates URLs, so you need the URLs first. Before adding a single tag, be able to answer:
- What is the URL of each language version? Usually language-prefixed subdirectories:
/de/about/,/fr/about/. - Which language codes are you using? ISO 639-1, optionally with an ISO 3166-1 Alpha 2 region — see the language-code reference.
- What is your x-default? The page for visitors matching no specific version.
If you cannot list the URLs, you do not have a multilingual site yet — you have a plan for one. Start with making WordPress multilingual and come back.
The three rules that decide whether it works
1. Annotations must be reciprocal
If your English page links to the German page, the German page must link back. Google may ignore annotations that lack return links. In practice, every language version of a page carries the same complete set of hreflang tags, including a link to itself.
2. Use valid language and region codes
The value is an ISO 639-1 language code, optionally a hyphen and an ISO 3166-1 region: en, en-GB, pt-BR. Don't invent codes — the UK is en-GB, not en-UK — and never use a bare region code as if it were a language.
3. Always include x-default
Add an x-default entry pointing at your best neutral page for users who match none of your specific versions.
The complete hreflang explainer covers the reasoning behind each rule.
Option A: add hreflang manually with wp_head
For a small, fixed set of pages you can print the tags yourself. The standard approach is a function hooked to wp_head in your child theme's functions.php or a small site-specific plugin:
add_action( 'wp_head', function () {
$alternates = [
'en' => 'https://example.com/about/',
'de' => 'https://example.com/de/about/',
'fr' => 'https://example.com/fr/about/',
'x-default' => 'https://example.com/about/',
];
foreach ( $alternates as $lang => $url ) {
printf(
'<link rel="alternate" hreflang="%s" href="%s" />' . "\n",
esc_attr( $lang ),
esc_url( $url )
);
}
} );
Three things to note. The map includes the current page's own language — that is the required self-reference. Every URL is absolute and includes the protocol. And output is escaped with esc_attr() and esc_url(), which you should never skip when printing into markup.
The same block must appear on every language version of that page. For a page-by-page set of tags you can generate the markup with the free hreflang tag generator and paste it in.
Why manual breaks down
The example above hard-codes one page. A real site needs the map computed per request, which means a lookup table of every page in every language. The arithmetic is unkind: 50 pages in 4 languages is 200 annotation blocks that must all stay mutually consistent. Publish a page in English and forget to add it to the German set, and that set silently stops being reciprocal.
Manual hreflang is defensible for a landing page or a five-page brochure site. Past that, it is a maintenance liability.
Option B: let a plugin generate it
On any site with real content, hreflang should be generated from the same source of truth that produces the translated URLs. A translation plugin such as Gloty emits a reciprocal set with x-default, per-language self-referencing canonicals, and translated meta for every page — including WooCommerce products, categories, and archives — with no tags to maintain by hand.
Multilingual plugins including Polylang, WPML and MultilingualPress also generate hreflang from their own language configuration. The approaches differ substantially in how the translations get created — see the comparisons — but on hreflang specifically, any of them beats hand-maintained tags.
A warning about running two plugins
If you have an SEO plugin (Yoast, Rank Math) and a multilingual plugin, check whether both are emitting hreflang. Duplicate or conflicting annotations for the same page are worse than one clean set. View source on a translated page and count the rel="alternate" elements before assuming everything is fine.
How to verify hreflang is working
Adding the tags is the easy half. Verify in three steps:
- Inspect the markup. Load a translated URL in the free hreflang checker to see every annotation, whether
x-defaultis present, and whether any code appears twice. - Test reciprocity by hand. Take one URL from those results, open it, and confirm it points back at the page you started on. This is the check that catches the most common failure, and no amount of single-page inspection substitutes for it.
- Watch Search Console. Google Search Console's International Targeting report accumulates hreflang errors — missing return links, unknown language codes — from real crawls over time. It is the only one of the three that catches a set which broke last week.
Also confirm your canonicals: each language version should have a self-referencing canonical, not one pointing at the source-language page. A cross-language canonical contradicts your hreflang and is a common reason translations never get indexed.
Summary
- WordPress core emits no hreflang — you add it manually or via a plugin.
- Manual means a
wp_headhook printing an escaped, absolute, self-referencing set includingx-default. - Annotations must be reciprocal and use valid codes, or they may be ignored outright.
- Hand-maintenance stops being viable roughly where pages × languages exceeds what you can hold in your head.
- Verify with a checker, confirm reciprocity by opening a referenced URL, and keep an eye on Search Console.