Hreflang: so that language versions don't eat each other
Configuring hreflang on a site is four mutually agreed-upon things: the language code of the document, mutual tags for each version, the specified x-default, and the presence of all language branches in the sitemap. We do them together and check not the markup, but the actual answer: whether the Ukrainian page is actually given to the address declared as Ukrainian. In the store we measured with three versions, it was given in Russian — both on the Ukrainian link and on the Polish one.
document language, mutual hreflang, x-default, canonical, language branches in the sitemap
after a free audit
Price
30
Guarantee
days after project sign-off
document language, mutual hreflang, x-default, canonical, language branches in the sitemap
What are we doing?
5-15
Term
working days - depends on the number of versions and the state of the templates
free language markup check, 2-3 business days
Before the estimate
hreflang is set twice by different mechanisms, the sets contradict each other
The most difficult case in its own dimension
two versions with mutual hreflang and x-default on a catalog of 3,565 products
Good condition from the same measurement
items in another country: markup governs version selection, not issue location
What we do not promise
Who it's for
Situations where this service delivers results
Scenario 1 of 4
Two versions of the same page compete with each other for rendering
The symptom is recognisable: one request shows a Russian page today, a Ukrainian one tomorrow, and none of them hold. For a search engine, these are two almost identical documents without an explanation of who proofreads them. Hreflang is such an explanation. Google writes directly in the documentation: it ignores asymmetric annotations - half of the work done correctly does not work at all.
We'll review your situation in a free audit
Scenario 2 of 4
The version opens, but gives the wrong language
Worst case scenario, because everything looks fine from the outside. In an underwear store with a catalog of 115,456 items, we found two parallel hreflang sets: the first led to the root addresses, the second to the language subdirectory. Links marked as Ukrainian and Polish provided Russian content. The markup was formally valid and at the same time lying.
Scenario 3 of 4
Add a language for a new market
Here, the marking is done once and for a long time, so it is cheaper to do it right away. The brand store of the Bulgarian cosmetics brand holds three versions - bg, en and ru - with hreflang and six types of structured data on the main one. A fair limit: the translation of the shop window is not a market. A buyer from another country also needs local payment, delivery and support - the site closes the first task and none of the rest.
Scenario 4 of 4
The language marking is correct, and there is no second language in the search
It also happens that way, and then you need to dig not in hreflang. In a heating equipment store with 3,565 products, both versions are declared correctly, with x-default, and /sitemap.xml returns an HTTP 200 with an empty body: the bot receives a response with zero addresses. At the same time, the map itself does not guarantee anything - Google documents it as a hint, and not as an obligation to index.
Cases
Tasks and results in numbers — all metrics measured by us
online retail underwear store, catalog of over 110,000 items
Task
Find out why the three language versions behave unpredictably in output.
Solution
We read the language markings for each version and checked what is actually provided for each declared address.
Result
Hreflang is initialized twice by two different mechanisms, and the sets conflict with each other: the first points to the root addresses, the second to the language subdirectory. Links marked as Ukrainian and Polish provide Russian content. Additionally: the Ukrainian version has an invalid language code, and the Polish version does not include variables in the template - literally lang="code". What works right there: canonical is self-referential on each of the three versions. The catalog at the time of measurement — 115,456 product items. Measurement 01.08.2026.
online retail store of heating equipment, catalog of about 3.5 thousand items
Task
Maintain two language versions of the directory without mutual competition in search.
Solution
Two versions uk and ru with mutual hreflang and given x-default, self-referencing canonical links.
Result
The language markup is correct on the catalog of 3,565 products — the number was calculated using a regular search engine. First server response 524.8ms, full load 662.7ms with 295.7KB markup. The found defect lies outside the language part: /sitemap.xml returns 200 with an empty body, and there is no structured data on the main page. That is, a correct hreflang does not save if the search engine has nothing to bypass. Measured on 07/31/2026.
a brand of natural cosmetics, a branded store for the EU market
Task
Enter the European market in three languages so that the versions do not compete with each other in publishing.
Solution
Three language versions bg, en and ru with full set of hreflang; structured store, contact and work schedule data on each version.
Result
Three working language versions with hreflang, six structured data types on master, first response 512.6ms with markup weight 220.4KB. The assortment is 23 items, this is the entire catalog of the brand. The defects found are again outside the language part: the site map and robots.txt give an empty body, there is no external analytics on the site at all - not a single counter. Measured on 07/31/2026.
What's included
Complete list of work and what you get as a result
We bring the language codes of the document to the standard on each version — "ua" is not the Ukrainian code, the correct one is uk, and this error is silent: the browser does not show it, the validator is silent
Full set of mutual hreflang tags: each version names all the rest and itself - otherwise the annotation is one-sided, and one-sided ones are rejected entirely by Google
Given x-default - you decide what a visitor whose language does not match any of the declared ones sees, instead of the algorithm deciding it
The second, parallel set of language markup is found and removed - precisely because of two mechanisms at the same time, the tags begin to contradict each other
Self-referential canonical within each version, no cross-references between languages: canonical from minor language to major overrides hreflang and excludes version from index
Running the actual response for each declared address as a list, not a sample - this is where the discrepancy between the tags and what the server returns
Language branches in the sitemap: all in one file or a separate map for each language, with a Sitemap directive in robots.txt so that the bot knows where they lie
Unique title and description on each version instead of copied from the main one - a translated interface with untranslated meta tags gives a duplicate output
Map and report analysis of international targeting in Search Console after implementation
"before and after" document with the date of measurement: declared language, actually given language, presence in the site map - for each version
When this service isn't right
What's not included — so there are no surprises at delivery
Content translation is a separate job, and we do not recommend machine translation of the directory
Local payment and delivery methods for the new market
Legal texts under the legislation of another country
Promises regarding items in the extradition of another country
Process steps
Transparent stages with approval at every step
Total duration:5–15 days
1
A snapshot of the language markup
1-3 working days
We read the code of each version: document language, hreflang, canonical, meta tags. At the same time, we drag the site map and count how many addresses of each language there are in it. Everything with the date of measurement is a basis for comparison after the works.
2
Solutions for languages and x-default
1 working day
It's your decision, not ours: which versions stay, which is the main one, which x-default gets. Here we screen out languages for which there is neither a market nor a person to support. You can change this later only with the addresses.
3
Edits in templates
1-5 working days
Language codes, full set of reciprocal tags with x-default, self-referential canonical. We turn off the second, parallel set of markup in the place where it comes from - in the theme or in the module, and do not cover it with the third one.
4
Site map and version meta tags
1-3 working days
All language branches in the map: one file or separate maps per language, with a directive in robots.txt. At the same time, we dilute the title and description on the versions so that they cease to be copies of the main language.
5
Address scanning and transmission
1-3 working days
We take the list of declared addresses and read the language of the actual answer to each one. We submit the maps in Search Console, analyze the international targeting report, give the "before and after" table with the same lines that were taken at the first stage.
Free language markup check
Language versions break quietly: the site looks correct, the switch works, but in the search, one version replaces the other, or the wrong language opens at all. It is visible only from the code of the pages and the site map, so we do the first check ourselves and for free — access is not required for it.
What we measure
Document language attributeWhat is in html lang on each version. Found both invalid language codes and template variables that did not match - literally lang="code" on the Polish version of the live store.
The hreflang setAre all versions listed, are cross-references, or is x-default specified. Separately, we check whether the set is not started twice by different mechanisms: the topic and the module together give a direct conflict.
Which is given by the linkThe most important point. We take the address declared as Ukrainian and read the language of the answer. Russian content was delivered to Ukrainian and Polish addresses in the dimension of the underwear store.
Language branches in the site mapHow many addresses of each language got there and whether the map is declared in robots.txt. There have been cards that give 200 with an empty body — formally, the answer is there, the address in it is zero.
Meta tags on versionsAre the title and description translated or copied from the main language? A translated interface with untranslated meta tags is a duplicate, not a language version.
Canonical linksIs canonical on every version and does it lead to itself and not to another language. In the underwear store, canonical turned out to be self-referencing on all three versions, the only thing that worked correctly there.
What you get
Table for each version: declared language — actual response language — presence in the site map.
List of conflicts with specific addresses where they are visible.
A fix plan with a split between what is done in the template and what is done in the CMS settings.
A 30-minute conversation on the document, where you can argue with the conclusions.
Timeline: 2-3 working days
Why is it free
Because the check is short - a few requests and code analysis - and the price of a mistake is high: incorrect language markup divides your own traffic between versions, and it costs more than any audit.
What's next
After the inspection, we name the list of works and the term by stages. If the language marking is correct, but the problem is something else, we will say it directly and show you exactly where to look.
Short form: your contact and site URL
Contract, act and 30-day warranty
Every project gets a written contract: scope, deadlines, amount, acceptance procedure. After delivery — act and invoice, then 30 calendar days of warranty.
Sole proprietor & bank transfer
The contractor is a registered sole proprietor. Payment by invoice with closing documents.
Rights & access — yours
Code, design and materials transfer to you after full payment. Domain, hosting, repository and analytics are registered to you.
Client portal instead of email chains
During the project you get access to a portal: contracts, invoices, acts and project status in one place.
European clients
Among our work — projects for Norway, Bulgaria, Moldova and Spain.
Verifiable numbers
Every case in the portfolio comes with a link to a live site and a technical measurement.
Audit first, then pricing
There is no price list on the site intentionally: the scope of the same work differs multiples between clients.
We say "no" when unsure
If the task isn't ours or the deadline is unrealistic — we tell you upfront.
Did not find your case?
Describe how it works on your side — we will tell you whether “Configuring hreflang and language versions” fits and what it means in your situation. No brief and no call: one question, one answer.
What affects the price
Why two seemingly identical tasks are priced differently
How many places in the code start the language markupA prime factor, and it is never visible from the problem description. One site gives tags from the theme template, the second from the module, the third from both at the same time, and then first you have to figure out which set to keep and which to turn off without breaking the switch for people.
Number of versions and whether all of them are neededThe number of pairs grows faster than the number of languages: three versions are already nine mutual references, which must be checked one by one. Sometimes the cheaper answer is to remove a version that no one supports.
Where versions liveA subdirectory of one address is the simplest case, everything is done in one template. Subdomains and individual domains add rights confirmation in Search Console to each resource: reciprocal tags cannot be set on one side.
Site map statusA map that has all branches is zero extra work. A map that returns an empty body or contains one of two languages means that it needs to be regenerated and declared in robots.txt, which is an adjacent amount of work.
Meta tags on minor versionsIf the title and description are copied from the main language, the markup alone will not solve the problem: the versions will remain identical in output. Volume is calculated by the number of templates and categories to be expanded, not by the number of languages.
Who implements the editsWe have access to templates - we work by ourselves. Edits are made by your team - we prepare accurate fragments and check the result; it is not technically more difficult, but calendar-wise longer.
Technologies & integrations
What we build on and what it connects to
Stack
hreflang in head is the main way, it is fixed in the template. Limit: Google rejects one-sided annotations entirely
hreflang in the sitemap — when getting into the template is expensive. Limit: the map should be generated automatically, such a file should not be held by hand
x-default - controls the selection for those whose language did not match. Boundary: This is a hint, not a redirect
html lang with ISO 639-1 codes. Limit: "ua" is not a Ukrainian code, the correct one is uk, and the browser will not warn about it
rel=canonical self-referential. Boundary: canonical between languages overrides the entire hreflang with a single line
Search Console, International Targeting Report. Limit: delayed data, rights to the resource are confirmed by the domain owner
OpenCart, WPML and Polylang multilingualism. Boundary: A theme often adds its own set on top of the module's - hence the double sets
nginx language routes. Border: geolocation redirect hides part of the versions from a bot that comes from the same country
Integrations
Google Search Console
Bing Webmaster Tools
GA4
Cloudflare
Hreflang in head vs hreflang in sitemap
How this option differs from the alternative
Announcement of languages in the site mapOur approach
Where is it fixed?in the map generator - access to templates is not requiredin the page template - requires access to the theme code
How much does a page weigh?zero weight on the page, everything is in a separate fileeach version adds a line to the head of each page
How it breaksloud: the map stops generating and returns an empty bodyquiet: theme and module give two sets that contradict each other
How many versions can withstandany number, it's just lines in the fileup to three or four it is read normally, then the head swells
How to checkonly by running the entire file, you can't check a separate page that wayindividually by page - it is immediately visible in the code
What we need from you
We can't start without this — best to prepare in advance
1The list of languages and markets for which you actually work is not "let it be", but where you sell.
2Access to templates: language markup lives in the code of the page, it is mostly invisible from the admin.
3Access to Search Console per version if they are on different domains or subdomains.
4The decision which version is the primary one for a visitor without a specific language is what x-default is.
5A person from your side who will say "yes" to the list of languages: technically, this decision is not accepted.
6The translated title and description for secondary versions, if they are not available now, are your texts, the arrangement is ours.
If something is missing — let us know, we'll help you gather it or do it as a separate task.
FAQ
Most frequently asked questions — with concrete answers
Why hreflang if I just have two versions with a switch?
The switch is for a person. Hreflang is for the search engine: it says that these two pages are not duplicates, but versions of the same content, and which one to show to whom. Without it, two versions compete with each other in the same issue, and the search engine chooses itself. Another nuance, because of which half of the settings do not work: the annotation must be mutual. If the Ukrainian page names the Russian one, and the Russian page does not name the Ukrainian one, Google rejects such a pair and remains with its choice.
What is x-default and is it required?
This is the default version for a visitor whose language did not match any of the declared languages. Formally not mandatory, but practically required if you want to manage this choice yourself, and not give it to an algorithm. In the heating equipment store we measured, the x-default is set, and the speech pair is the only thing there, about which there were no complaints. It's important to understand the boundary: x-default is a hint about which version to show, not a redirect. He does not carry anyone anywhere.
How to tell if hreflang is broken?
The most reliable way is to open the address declared as Ukrainian and see in what language the reply came. Markup can be valid and yet lie. In the underwear store, we found exactly this: the hreflang was set up twice by different mechanisms, the first set leads to the root addresses, the second to the language subdirectory, and the links marked as Ukrainian and Polish give Russian content. No validator shows this, because both sets are syntactically correct.
We have lang="ua" in our code - is this an error?
Yes. The language code "ua" does not exist: Ukrainian is uk, and ua is the country code in another standard. The error is silent, the page opens normally, no one highlights anything in red. In the same lingerie store, the Ukrainian version gave exactly lang="ua", and the Polish version - lang="code", that is, the variable simply did not fit into the template. Therefore, we check the language of the document on each version separately, rather than looking at one page and assuming that the rest are the same.
Do language versions have to be in the sitemap?
All must be included, otherwise the second language exists for the visitor and does not exist for the bot. Just don't expect more from a sitemap than it delivers: Google documents it as a way to find pages, not as a promise to index them. And it should not be checked based on the fact of having an address. In the heating equipment store, the card returns an HTTP 200 - looks good - and the response body is empty: zero addresses in both languages.
Is it necessary to translate the entire site or is the interface enough?
Translation of the interface without translation of metatags gives a duplicate, not a language version: the output has the same page title and the same description. The minimum that should be translated on each version is the title, description, page header, category names, and payment, shipping, and returns service pages. Product cards can be filled in queues. We do not recommend machine translation of the catalog: the buyer sees it in the first second, and the search engine does not from the first, but sees it.
How many languages does it make sense to keep?
As many markets as you truly serve - with payment, delivery and support in that language. The site closes only the shop window, and this is not a figure of speech: in the company store of the Bulgarian cosmetics brand, there are three versions, bg, en and ru, with correct marking - and behind each of them there is a real market. Each extra version multiplies the amount of content someone has to maintain with each new product. Therefore, at the first stage, we more often suggest removing the language than adding it.
Will traffic increase after fixing the markup?
Directly - no, and it would be dishonest to promise such a thing. The fix does another thing: your custom pages stop competing with each other, and the search engine shows the version you assigned to that visitor. Growth from a new market does not follow from this - it requires content, demand and local sales conditions. What will appear for sure: controllability and the ability to check. Announced language, actually given language and presence in the site map are three numbers that we take before and after the work, and then the reaction is visible in your Search Console with a delay of several days.
Send the address of the site and the list of languages that are announced on it.
The answer is a table for each version: what is written in the markup, what is actually given, what is in the site map. If the language part is correct, we will write it and show where the real reason is.
From measured casesThe language markup is correct on the catalog of 3,565 products
There is no price list on the site on purpose: the same work differs several times over between two clients, and a “from” figure explains nothing in that case. First a free audit — we count your pages, duplicates and speed — then we name the sum and the deadline and fix both in the contract.