Guide Published 21 August 2026 14 min read

International SEO Audit: Hreflang, Indexation by Locale and the URL Decision

An international SEO audit reads one site market by market: which page Google serves in which country, whether every locale is in the index, and where two targeting signals disagree. This is the order I run it in, with the reports and filters named.

Roman Makuev
Last updated

The Pages report says most of the site is indexed, and the meeting moves on. Filter the same report to one locale folder and the picture changes: the home market is nearly complete, one European locale sits at about half, and the newest Asian locale is barely in the index at all. No single page was wrong. The European locale’s canonical pointed at the English original, the Asian folder sat behind an IP redirect the crawler never got past, and the site-wide figure averaged both into a number nobody questioned. That is the shape of most international findings I have written up in the last few years. The fault sits between two markets, and a single-market audit checks each market on its own and reports it clean.

An international SEO audit checks whether Google understands which page belongs to which country and language, market by market, and whether every market is crawled, indexed and served the way the site intends. Most of what ranks for this query stops at the hreflang tag; the tag is where the problems start. What follows is the audit in the order the trade-offs bite, with the tool filters, Search Console statuses and document limits named, because a check you can’t reproduce isn’t a check.

What an international SEO audit checks that a standard one doesn’t

A standard audit asks what is wrong with a site. An international audit asks which page belongs to which user, in which country, in which language, and whether Google agrees. The object under review is the intersection of website, country, language and intent, and the failures hide in the gaps between those four.

A standard audit checksAn international audit adds
Crawlability and indexation, site-wideIndexation and crawl per locale
One keyword set, home marketNative demand per market
Site-wide Core Web VitalsField data by country
One backlink profileReferring domains by country
Duplicate content within the siteDuplicates across locales
Canonical tags in isolationCanonical, hreflang and indexability as one unit
Structured data valid or notSchema per locale: language, currency, entity

One distinction has to be settled before any check makes sense: multilingual, multiregional, or both. A Swiss site in German, French and Italian serving one country is multilingual. A German-language site serving Germany, Austria and Switzerland is multiregional: one language, three markets, three SERPs. Plenty of sites are both, and that is the point where hreflang stops being a tag you add and becomes an architecture you maintain.

THE FOUR TARGETING SIGNALS

Underneath every check sits one question: what tells Google which country and language a page is for? Four signals, unequal in weight. The URL: a ccTLD like example.de states a country by itself; a subdirectory states nothing until something else does. Hreflang, built for the job: it names language and region and ties the set together. The language of the content. And server or CDN geography, which nudges rather than declares. There used to be a fifth, the country-targeting setting in Search Console. Google removed it in September 2022 together with the International Targeting report, which was the only built-in hreflang error report, saying the feature had little value for the ecosystem. There is no manual override anymore and no place inside Google’s tools that validates hreflang; the validation is a crawl you run yourself. When the four remaining signals agree, targeting is clean. Most international failures are two of them disagreeing.

Market validation before the audit

Demand validation is not part of the audit, and it comes before it, because the most expensive international failure happens before a page exists: translating a site into a language whose market never asked for it. Validating demand takes a week; maintaining a dead locale takes years. For each country on the list I trace one chain, what people search, with what intent, on which SERP, against which local competitors, and where a gap sits that the site could realistically own. A market whose raw keyword export clusters into a few dozen intents is four pages of content, and that is an answer too, cheaper to hear before the translation invoice. The ceiling for most teams is one new market per cycle, two when the language is shared, because every locale opened is permanent: its own research, its own content to keep at parity, its own links built from nothing.

URL structure: subdirectory, subdomain or ccTLD

Three options, and the audit question is whether the one in place was chosen or inherited.

A ccTLD (example.de) buys the cleanest country signal and charges the highest rent, and the rent is the part nobody costs out loud. Each country domain is a separate site in Google’s eyes, earning its own links from zero. Since the July 2026 rewrite of Google’s crawl-budget documentation it is also a separate crawl allowance: every site starts on the same conservative default limit, the limit grows only as the server proves it can take more, and the allowance is shared across all of Google’s crawlers. Three ccTLDs are three cold starts on crawl as well as on authority. I have watched a company launch three country domains in one quarter and stay invisible on all three for a year, one domain’s authority split into three newborns and none of them fed. I sign off on a ccTLD when the market is a decade-long commitment, when a local legal entity forces it, or when the business can resource link-building per country.

A subdirectory (example.com/de/) consolidates authority. Every link to any locale strengthens all of them, geo-targeting comes from hreflang and content, and there is one CMS, one crawl allowance, one migration when things change. For most companies expanding past their first market this is the default, and the burden of proof sits on anyone arguing against it.

Because a subdirectory carries no geo-signal in the URL, and Search Console no longer lets you assign one, the check is what Google infers. Create a URL-prefix property per locale folder, open the Performance report with the Country dimension, and read which market Google serves each folder to. A /de/ folder getting most of its impressions from Austria and Switzerland is telling you something about the hreflang set.

A subdomain (de.example.com) is the option I flag more often than any other, because it is almost always chosen by accident. Google’s public line is that it treats subdomains and subdirectories the same. What I see across audits is that authority consolidates worse on a subdomain than on a folder, the geo-signal stays weaker than a ccTLD, and under the per-site crawl rule a subdomain is its own cold start too. The tell, when I audit one, is that nobody can explain why it exists. The defensible reasons are organizational: separate infrastructure per country, separate teams, an acquisition not yet merged. If one of those is true, live with it. If none is, the dev set it up that way.

THE TEST I APPLY

Default to a subdirectory. Move to a subdomain only when you can name, in one sentence, the specific piece of infrastructure that makes a subdirectory impossible: a separate platform you can’t route under one domain, a CDN or CMS boundary that won’t bend, a team that owns its own stack. If you can’t name it in one sentence, you don’t have that constraint. “It depends on your needs” fails here because it never says which need decides.

Mixed structures get their own line in every report: a ccTLD for Germany, a subdirectory for France, a legacy subdomain for Japan. Each choice was locally sensible the day someone made it. Together they split authority three ways and triple the maintenance.

Hreflang audit: codes, cluster, placement and canonical conflicts

Hreflang is an annotation that tells search engines which language and region a page variant is for, so a searcher in Austria gets /de-at/ instead of /de-de/. It is a signal for serving the right version. It is not a ranking factor and it does not resolve duplicates. What Google evaluates is the cluster, the complete set of alternate URLs that all have to agree with each other, and one broken member drags down the set. With the Search Console report gone, the only way to see the cluster is to crawl it.

Language and region codes

Language is ISO 639-1, region is ISO 3166-1 alpha-2, in that order. en-GB is valid, en-UK is not, and en-UK ships constantly because UK feels right to whoever typed it. A region on its own (hreflang="GB") is invalid; a language on its own (hreflang="de") is fine. Script goes between them as an ISO 15924 code, zh-Hant, zh-Hans-SG, and a Chinese site shipping zh-TW and zh-CN without the script is telling Google about regions when the difference it cares about is the writing system.

Cluster structure: self-reference, return links, x-default, live targets

Every page references itself. Every reference is reciprocated; Google’s documentation says one-way annotations may be ignored, and in practice they are. An x-default catches everyone who matches nothing, and it should point at the selector page rather than at the English version by habit. Each alternate URL returns 200: never a redirect, never a 404, never a soft error page dressed as a 200, and never a page carrying noindex. The noindexed member is the one people miss. Google drops it from the cluster, and every member that referenced it loses a return link.

Then the incomplete clusters. Fourteen pages exist in English, nine in German, and the template writes the hreflang set for all fourteen pairs regardless. The five phantom members are not harmless; they break the annotations of the nine that exist.

Where the annotations live

Google accepts three placements: a <link rel="alternate" hreflang="de-AT" href="…"> in the head, an HTTP Link: header (the only option for PDFs and other non-HTML files), and xhtml:link entries in the XML sitemap. On a site with twelve locales the head version means twelve link tags on every page, maintained by whoever touches the template. Past a handful of locales the sitemap is the placement that stays correct, because it is generated rather than hand-edited. I recommend it on nearly every site that has grown past three markets, with one sitemap per locale, which also gives the indexation check below something to read. One documented condition for ccTLD and subdomain setups: a sitemap may only carry hreflang entries for URLs on other hosts if every one of those hosts is verified in the same Search Console account, otherwise the cross-host entries are ignored.

Reading the cluster in Screaming Frog

Crawl with hreflang extraction on; the Hreflang tab does the cluster reading. Each filter is one of the failures above:

  • Non-200 Hreflang URLs: targets that redirect or 404
  • Unlinked Hreflang URLs: alternates found only in annotations, never in a link, which usually means orphans
  • Missing Return Links: the reciprocity failure
  • Inconsistent Language & Region Return Links: page A says B is fr-FR, B says it is fr-CA
  • Non-Canonical Return Links and Not Using Canonical: annotations pointing at URLs that canonicalize elsewhere
  • Noindex Return Links: the noindexed member
  • Incorrect Language & Region Codes: en-UK lives here
  • Missing Self Reference, Missing X-Default, Outside <head>

Export from Reports → Hreflang and read the counts per template rather than per URL. A broken cluster on a product template is one bug repeated ten thousand times and one fix.

Canonical and hreflang conflicts, and how they read in Search Console

This is where most audits pass a site that is broken. A canonical pointing across markets tells Google the German page is a copy of the English one while hreflang insists it is an alternate. When the two contradict each other Google resolves the conflict for you, and it resolves it by ignoring the hreflang. I check the triangle, hreflang, canonical, indexability, as one unit, because a clean result on any one leg tells you nothing. Each conflict has a signature in the Pages report:

ConflictPages report status on the affected localeWhat Google did with the cluster
Locale canonical points at the home-market URLAlternate page with proper canonical tagLocale folded into the home market; its hreflang ignored
Two locales nearly identical (en-US / en-GB) with an incomplete or missing clusterDuplicate, Google chose different canonical than userGoogle picked one locale and dropped the other
Hreflang target redirectsPage with redirect on the targetMember dropped; return link missing on the rest
Hreflang target carries noindexExcluded by 'noindex' tagMember dropped; cluster loses reciprocity
Hreflang points at a parameter or non-canonical URLAlternate page with proper canonical tag on the target; Screaming Frog Non-Canonical Return LinksAnnotation ignored for that pair

When one of those statuses clusters in a single locale, the locale is being folded into another. The fix is not more unique copy for its own sake. It is either localization that changes the page (terms, prices, currency, examples) or a complete hreflang cluster telling Google the overlap is intentional.

Indexation by locale: the method

Indexation per locale is the check that the site-wide number hides, and it needs its own reporting surface before it can be read. The method:

  1. A property per locale. A URL-prefix property for each folder (example.com/de/), or a domain property with the Pages report filtered by path. Subdomains and ccTLDs are separate properties by construction. Without this step every status in Search Console is a site-wide average.
  2. A sitemap per locale, submitted separately. The Sitemaps report then shows submitted against indexed for each locale, and the gap between the two is the first number in the audit. A locale whose sitemap is largely unindexed is a structural finding before a single URL has been opened.
  3. Pages report by reason, per property. Whether the excluded pages cluster in one locale is the diagnosis. Random gaps across locales point to crawl allowance. An entire locale under one exclusion reason points to a structural block, and the table above says which.
  4. A URL Inspection sample. The URL Inspection API allows 2,000 requests a day per property; a sample of one to two hundred URLs per locale, across templates, is enough to see which canonical Google selected and which crawler fetched the page. The line between page-level work and a structural block is a pattern rather than a percentage: one exclusion reason accounting for most of a locale’s gap, and that same reason close to absent in the home market. When the sample shows that, I stop reading pages and go to the template, the canonical rule or the redirect that produced it.
  5. Server logs by locale prefix. The Crawl stats report works at the property level and won’t split by folder, so a locale that Search Console says is Discovered – currently not indexed needs the logs to explain itself. Segment verified Googlebot hits by prefix. The usual picture is the crawler spending its allowance on the home-market templates it already knows and reaching the new locale a few hundred requests a month, which on a twenty-thousand-URL folder is a refresh cycle measured in quarters. Under the July 2026 rule that allowance is shared across all of Google’s crawlers, so a heavy AdsBot run against the home market is spending the new locale’s crawl too.

IP redirects and geo-blocking

Redirecting users by IP to “their” version also redirects Googlebot, which Google’s documentation says crawls primarily from US addresses, so the crawler never sees the other locales. Geo-blocking, where the German site only answers to German IPs, does the same thing more bluntly. Both look like UX decisions in a meeting and turn out to be indexation decisions in production. The test is a fetch of each locale’s key pages from a US IP with no cookies and no Accept-Language header, and the live test in URL Inspection is the fetch that matters, because it comes from Google’s own addresses. A site that wants to suggest a locale can do it with a banner and a crawlable link; a 302 on entry is the version that removes the locale from the index.

Localization audit: slugs, metadata, currency and information gain against the local SERP

There is a ladder, and each rung costs more than the last: machine translation, human translation, localization, local search behavior. Most international sites stop at rung two and then wonder why competitors sitting on rung four outrank them.

Same language does not mean same market. A US travel site expanding to the UK translates nothing, and still “vacation rentals” has to become “holiday lettings” or the pages target demand that doesn’t exist there. Currency, date formats, units, the legal wording in commercial claims, which trust signals convince a local buyer: all of it is market work no translator was briefed to do. URL slugs and metadata get skipped even by teams that localize their body copy, so /en/seo-services/ ends up with a German page behind an English slug, an English title tag, sometimes an English meta description showing in the German SERP. Those are ranking surfaces. They localize or they leak.

Translation preserves the words and loses the differentiation, because uniqueness is relative to a SERP and every market has its own. A page can be original against the US top ten and add nothing against the German one, because the German competitors already cover every point it makes. For English-language markets this is measurable: an information-gain run scores the page against the target market’s SERP corpus rather than the home market’s, and the pattern I see most is a flagship guide that scores well at home and low against the UK or Australian top ten, because it was written against a SERP those markets never see. For other languages I don’t have that number. The tooling reads English corpora only, so a German or Japanese page gets a manual comparison against its local top ten, point by point: slower, less exact, and a gap in the method until the tooling catches up.

Keyword research per market

A translated keyword list fails along a predictable chain: English keyword, translation, native search term, local SERP, intent, and every arrow is a place to lose the market. Sometimes the dictionary gets lucky. “Car insurance” into German is “Autoversicherung,” which happens to be what people search. Often it doesn’t. “Attorney” becomes “Rechtsanwalt,” which carries a fraction of the volume; the demand sits under “Anwalt” and then splits by specialty in a way the US market doesn’t, “Anwalt Arbeitsrecht,” “Anwalt Familienrecht,” “Fachanwalt für Mietrecht,” so a page built for the translated head term is built for a query nobody types.

Research starts from native terms, never from the existing list run through translation: autocomplete in the target language, the terms local competitors rank for, volumes pulled with the country switched, because the same string carries different demand in different countries. Then read the SERP itself, which is Google telling you for free what it believes the intent is. One phrase, three countries, three result pages: guides and an AI Overview in one, service providers and a map pack in another, marketplaces in a third. Ignore that and you optimize a page type the market’s SERP doesn’t rank.

Performance and authority by country

A site-wide Core Web Vitals score is an average, and averages hide the markets you are failing. A site served from Frankfurt can post a green LCP overall while users in Tokyo wait several seconds for the same page, and the Japanese locale’s field data reflects Tokyo. I read performance per country: real-user metrics split by country where the data exists, synthetic tests from in-market locations where it doesn’t. The Chrome UX Report only holds numbers for URLs with enough traffic, so a small locale usually has no per-URL data and inherits the origin’s figures, which is how a failing Japanese template hides behind a passing German one. The CrUX API takes a country filter; the PageSpeed Insights front end doesn’t, and most reports are built from the front end. The findings are unglamorous: no CDN edge near the market, images shipped at full weight across an ocean, a JavaScript-only locale switcher that leaves the crawler on the default language.

Authority localizes the same way content does. Domain-level metrics say a site is strong; the country breakdown says where. A referring-domain profile that is overwhelmingly from one country is a strong site in that country and a weak one everywhere else, and rankings in a new market are contested by the sites that market’s publications link to, not by a domain rating. I break the profile down by the country of the referring domain and set it against the competitors ranking in that market; the distribution usually settles the argument at a glance. From there it is ordinary link work aimed locally: local publications, industry directories, partners and PR inside the target market.

THE LANGUAGE SELECTOR

Small section, constant finding. The selector should be crawlable links, one per locale, with an <a href> underneath whatever the JavaScript does on top. Auto-detection can suggest and never force, because a forced redirect traps users and crawlers alike on one version. A flag is a country, not a language, so a flag-only selector tells a Spanish speaker in the US nothing useful. And the cross-market internal links people forget entirely, the German article linking back to its English original, pass signals hreflang alone doesn’t.

Structured data per locale: inLanguage, priceCurrency, Organization

The general technical audit checks that schema is present, valid and matches the page. The international one adds a question the validators can’t answer: does the markup on the German page describe a German page?

Usually it doesn’t. The JSON-LD is generated once by the template and never localized, so /de/ ships an Article with an English headline, a Product whose Offer carries priceCurrency: "USD" under a price shown in euros, and an Organization whose sameAs points at the US LinkedIn page. Each of those is markup contradicting the visible page, which is what Google’s structured-data policies are written against, and it contradicts the hreflang as well: the tag says this is the German version, the entity data says it is American.

The checks are short. inLanguage on Article and WebPage matches the locale. priceCurrency matches the currency on the page. Organization is either one global entity with a per-locale ContactPoint carrying availableLanguage and areaServed, or a LocalBusiness per market with a local address, and not the US entity pasted onto every locale. Author Person markup, if it exists, links to a profile the local reader can read. Run the Rich Results Test per locale, because a template error in the localized string handling shows up only where the strings were localized. AI Overviews and AI Mode read entity data per language, so a German page whose entity layer says “American company, USD” is a weaker candidate for a German answer than its content deserves.

Search engines other than Google

Everything above assumes the engine reading the signals is Google. Bing’s documentation treats hreflang as a weak signal and asks for content-language, so a site that dropped the <meta http-equiv="content-language"> tag because Google ignores it is under-signaling the second engine in the US and, through Yahoo, in Japan. Yandex reads hreflang from the head and the sitemap. Baidu and Naver don’t read hreflang at all: Baidu goes by content-language, in-country hosting and a Chinese-language site, and Naver expects registration in its own Webmaster Tools and weights its own platforms above the open web. If China or Korea is on the market list, the audit for that locale is a different document.

Compliance checkpoints that become search problems

Not legal advice; a short list of places where a compliance decision becomes a search decision without anyone noticing. A consent wall configured for the EU that renders the page only after a click: Googlebot doesn’t click “accept,” so whatever sits behind the wall doesn’t exist for it. The tell in Search Console is a locale whose pages sit in Crawled – currently not indexed while the URL Inspection screenshot shows the banner and nothing else. Commercial claims carrying legal wording that varies by market, pricing-display rules in Germany, financial-product language in the UK, health claims almost everywhere, where the compliant copy sometimes changes the target keyword. Content that legally can’t ship to some markets, where the geo-restriction mechanic (block, redirect, serve a variant) lands differently on indexation: a 451 status keeps the URL out of that market’s index without dragging the others. The job in the audit is to make sure the compliance plumbing isn’t strangling the site’s own search presence, which happens more often than either the lawyers or the SEOs expect.

Measuring market by market

“Organic traffic grew 20%” hides everything worth knowing. Decomposed by country, the same quarter asks which locale dropped after which change, and whether the newest one broke or never worked. Every metric, impressions, position, CTR, conversions, gets carried down to the locale level; Search Console gets read with the Country filter, and positions get tracked per locale, because a keyword’s position is a different number in every country.

AI visibility gets tracked per market too, because the AI surfaces arrived market by market and still behave that way. AI Overviews reached the US in May 2024 and most of the EU through 2025. France was the last major European market without them; Google wrote to French publishers on 29 June 2026 promising a launch by 23 September, and switched AI Overviews and AI Mode on there on 22 July, two months early. A French locale audited in spring 2026 and again this autumn is being audited against two different SERPs. Since June 2026 Search Console has a Generative AI performance report under Performance, filterable by country, and its documentation says each follow-up inside an AI Mode conversation counts as a new query with its own impressions and position, so a locale’s impression growth this year may be conversation depth rather than reach. What I can’t tell you yet is how citation rates differ between languages for the same site; the report splits by country and not by language, and I don’t have a large enough multi-locale sample to say it myself.

International SEO audit checklist: ten checks and where to read each one

Ten places to look, where I read each one, and the status or filter that surfaces the failure. None of the rows is an audit on its own; the audit is the reading across them.

Check Where I read it What surfaces the failure
Market demandNative keyword export, clustered to intents; local SERP by handAn export that collapses into a few dozen intents
URL architectureCrawl segmented by host and folder; GSC Performance → Country per locale propertyMixed structures; a folder served to the wrong country
Hreflang clusterScreaming Frog Hreflang tab, Reports → HreflangMissing Return Links, Non-Canonical Return Links, Incorrect Language & Region Codes
Canonical × hreflangPages report per locale propertyAlternate page with proper canonical tag or Duplicate, Google chose different canonical than user clustering in one locale
Indexation per localeSitemap per locale: submitted vs indexed; URL Inspection API sampleOne locale’s sitemap largely unindexed
Crawl per localeVerified Googlebot hits in logs, segmented by locale prefixDiscovered – currently not indexed piling up in one folder
Geo-redirect / geo-blockFetch each locale from a US IP, no cookies; URL Inspection live testEvery locale returning the home-market page
Localization depthInformation-gain run against the target market’s SERP corpusA page that scores well at home and low against the target SERP
Performance and authority by countryCrUX API with country filter; referring domains by country vs the ranking local competitorsOrigin-level fallback masking a locale template; most links from a market you’re not entering
Schema per localeRich Results Test per locale; JSON-LD diff across templatespriceCurrency or inLanguage disagreeing with the page

What to recheck four to eight weeks after the fixes

Hreflang and canonical changes take effect at Google’s recrawl pace, and on a locale that was under-crawled to begin with that pace is the slow one. The recheck is a shorter list than the audit:

  • Sitemaps report per locale: has submitted versus indexed moved for the locale that was folded?
  • Pages report per locale property: has the dominant exclusion reason changed, or only its count?
  • URL Inspection on the same sample: does the Google-selected canonical now match the declared one?
  • Performance → Country on each locale property: is the folder being served to its own market?
  • Screaming Frog Hreflang tab on a fresh crawl: zero in Missing Return Links and Non-Canonical Return Links, or the template regressed.

If none of the five has moved after eight weeks, the fix shipped to the template and not to the crawl, and the logs are where to look next.

What I’d expect to change first: by 2028 the per-locale information-gain read stops being a tooling curiosity and becomes the standard localization check, because “translated” and “competitive” have stopped meaning the same thing in every market I look at, and the AI surfaces, now live language by language, will make the gap visible to clients before it is visible in rankings. If you want the single-market foundation this builds on, crawl, render, index, the technical layer read as one path, that is our SEO audit.