Google's John Mueller once called hreflang "one of the most complex aspects of SEO, if not the most complex one." He wasn't exaggerating. I've audited bilingual Indonesian-English sites for clients across Jakarta, Surabaya, and Singapore. The pattern is consistent: hreflang is either missing entirely, or it's implemented wrong. Both outcomes produce the same result. Google ignores your language signals, and your pages compete against each other instead of complementing each other.

This is not a theoretical problem. When Google can't figure out which version of your page belongs to which audience, it picks one. Sometimes it picks the wrong one. Your Indonesian audience sees the English page. Your English audience sees the Indonesian page. Neither converts. Both bounce. Your rankings drop on both versions.

If you're running a bilingual site and haven't touched hreflang, this essay is the fix.

Key concept: Hreflang tags don't boost rankings. They prevent suppression. Without them, Google treats your two language versions as competing duplicates and picks a winner. The loser disappears from search results.

What hreflang actually does

The hreflang attribute tells search engines: "This page has an alternate version in another language. Here's the URL." That's it. It's a relationship declaration, not a ranking signal.

The tag goes in the <head> section of your HTML. Each language version of a page must reference all other versions, including itself. This self-referential requirement is where most implementations break.

Three places you can implement hreflang:

  1. HTML link tags in the <head> (most common for static and small sites)
  2. HTTP headers (for non-HTML content like PDFs)
  3. XML sitemap (best for large sites with hundreds of language pairs)

For most bilingual Indonesian-English sites, HTML link tags are sufficient. You're not managing 40 language variants. You have two. Keep it simple.

The correct implementation

Here's what correct hreflang looks like for a bilingual Indonesian-English website using subdirectory structure:

On the English page (example.com/en/about/)

<link rel="alternate" hreflang="en" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="id" href="https://example.com/id/about/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/about/" />

On the Indonesian page (example.com/id/about/)

<link rel="alternate" hreflang="en" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="id" href="https://example.com/id/about/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/about/" />

Notice three things:

  1. Both pages contain the exact same set of hreflang tags.
  2. Each page references itself (self-referential tag).
  3. The x-default points to the fallback version for users whose language doesn't match either option.

These three rules are non-negotiable. Break any one of them, and Google ignores the entire set.

The incorrect implementation (what I keep finding)

Here's what I see on most bilingual Indonesian sites during audits:

Mistake 1: Only one direction

<!-- On the English page only -->
<link rel="alternate" hreflang="id" href="https://example.com/id/about/" />

<!-- Indonesian page has NO hreflang tags at all -->

This is the most common error. The English page points to the Indonesian version, but the Indonesian version doesn't point back. Google requires reciprocal references. One-way links get ignored [1].

Mistake 2: Missing self-reference

<!-- On the English page -->
<link rel="alternate" hreflang="id" href="https://example.com/id/about/" />
<!-- Missing: hreflang="en" pointing to itself -->

Every page must include a self-referential hreflang tag. Without it, Google doesn't know which language the current page is. You'd think it could figure that out from the content. It can. But hreflang is a declaration system. It needs explicit statements, not inferences.

Mistake 3: Wrong language code for Indonesian

<!-- WRONG -->
<link rel="alternate" hreflang="in" href="https://example.com/id/about/" />

<!-- CORRECT -->
<link rel="alternate" hreflang="id" href="https://example.com/id/about/" />

The ISO 639-1 code for Indonesian is id, not in. The code in doesn't exist in the standard. But developers confuse it because Indonesia's country code (ISO 3166-1) is ID. The language code and the country code are the same letters, different standards. This trips up teams constantly, especially when the developer isn't Indonesian and is guessing [2].

How hreflang relationships work

The relationship between language versions isn't one-way. It's a cluster. Every page in the cluster must acknowledge every other page in the cluster. Here's how that looks for a bilingual site:

graph LR EN["English Page
/en/about/"] ID["Indonesian Page
/id/about/"] XD["x-default
(fallback)"] EN -->|hreflang='id'| ID ID -->|hreflang='en'| EN EN -->|hreflang='en'| EN ID -->|hreflang='id'| ID EN -->|hreflang='x-default'| XD ID -->|hreflang='x-default'| XD

Every arrow must exist. Remove one, and the cluster breaks. Google processes hreflang as a set. Partial sets get discarded entirely.

URL structure options for bilingual sites

Before implementing hreflang, you need a URL structure that separates your language versions. Three common approaches:

StructureExampleProsCons
Subdirectoriesexample.com/en/ and example.com/id/Single domain authority. Easy to manage. Works with most hosting setups.Requires server-side routing.
Subdomainsen.example.com and id.example.comClean separation. Independent hosting possible.Subdomains treated as separate sites by Google. Split domain authority.
Separate domainsexample.com and example.co.idStrong geo-targeting signal.Two domains to maintain. Double the cost. Split authority completely.

For most Indonesian businesses running bilingual content, subdirectories win. You keep one domain, one SSL certificate, one hosting account. Your domain authority stays consolidated. And hreflang implementation is straightforward because everything lives under one roof.

This is the same principle behind static sites being easier to structure for AI discovery. Simplicity in architecture pays compound interest.

Common hreflang errors: the full table

Here's every hreflang error I've encountered across bilingual Indonesian-English site audits, ranked by how often I see them:

ErrorWhat happensFix
Missing reciprocal tagsGoogle ignores the entire hreflang setEvery page must link to every other language version AND itself
No self-referential tagGoogle can't confirm the current page's languageAlways include a tag pointing to the page itself
Wrong language code (in vs id)Invalid code, tag ignoredUse ISO 639-1: id for Indonesian, en for English
Tags outside <head>Search engines can't find themPlace all hreflang link tags inside <head>
Missing x-defaultNo fallback for unmatched usersAdd x-default pointing to your primary language version
Hreflang conflicts with canonicalCanonical says "this is the one true page," hreflang says "there are alternates." Contradiction.Canonical must point to itself. Each language version is its own canonical.
Pointing to non-200 URLsTag references a page that 404s or redirectsOnly reference URLs that return HTTP 200
Relative URLs instead of absoluteSearch engines can't resolve the pathAlways use full absolute URLs with https://
Mixed implementation methodsUsing HTML tags AND sitemap annotations simultaneously creates conflictsPick one method and stick with it
Not updating after site redesignOld URLs in hreflang, new URLs on the siteAudit hreflang tags after every URL structure change

The canonical + hreflang conflict

This one deserves its own section because it's subtle and destructive.

A canonical tag tells Google: "This is the authoritative version of this page." An hreflang tag tells Google: "This page has an alternate in another language." If your canonical tag on the Indonesian page points to the English page, you're telling Google: "The Indonesian version isn't real. The English version is the authority." Google then suppresses the Indonesian page.

The rule is simple. Every language version must have a self-referencing canonical tag.

<!-- On the English page -->
<link rel="canonical" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="id" href="https://example.com/id/about/" />

<!-- On the Indonesian page -->
<link rel="canonical" href="https://example.com/id/about/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="id" href="https://example.com/id/about/" />

Each page is canonical to itself. Each page references all alternates. No contradictions.

Hreflang and structured data

Here's where bilingual SEO connects to the broader entity infrastructure work I write about. Your schema markup and your hreflang tags work together, but they serve different masters.

Hreflang tells Google which language version to serve. Schema tells Google what the page is about. If you're implementing Person schema across your site, you need to ensure the JSON-LD on each language version uses the correct language attributes.

A common mistake: identical JSON-LD on both language versions with no inLanguage property. Google can still parse it, but you're leaving disambiguation on the table. Add the inLanguage property to your structured data:

{
  "@context": "https://schema.org",
  "@type": "WebPage",
  "name": "About Ibrahim Anwar",
  "inLanguage": "en",
  "url": "https://example.com/en/about/"
}

For the Indonesian version:

{
  "@context": "https://schema.org",
  "@type": "WebPage",
  "name": "Tentang Ibrahim Anwar",
  "inLanguage": "id",
  "url": "https://example.com/id/about/"
}

The inLanguage property reinforces the hreflang signal. Belt and suspenders. When search engines and AI systems process your site, they get consistent language signals from multiple sources.

Implementation checklist for PHP sites

If you're running a PHP site (like this one), here's a practical implementation pattern. Put this in your shared header file:

<?php
// Set these per page
$lang = 'en';  // or 'id'
$alternate_url = 'https://example.com/id/about/';  // URL of the other version
$current_url = 'https://example.com/en/about/';
$alternate_lang = ($lang === 'en') ? 'id' : 'en';
$default_url = $current_url;  // or whichever is your default
?>

<link rel="alternate" hreflang="<?= $lang ?>" href="<?= $current_url ?>" />
<link rel="alternate" hreflang="<?= $alternate_lang ?>" href="<?= $alternate_url ?>" />
<link rel="alternate" hreflang="x-default" href="<?= $default_url ?>" />
<link rel="canonical" href="<?= $current_url ?>" />

Template-driven. Set two variables per page, and the header handles the rest. No manual tag writing. No forgetting the self-reference. No canonical conflicts.

This is the same philosophy behind all good technical SEO. Build it into the system once. Let the system enforce correctness. Human memory is unreliable. Templates are not.

How to verify your implementation

After implementing hreflang, verify it before waiting for Google to crawl.

  1. View source on both pages. Check that both pages contain the complete set of hreflang tags. Not just one. Both.
  2. Google Search Console. Go to International Targeting. Look for hreflang errors. GSC reports missing return tags, invalid codes, and canonical conflicts.
  3. Screaming Frog or Sitebulb. Crawl both language versions. These tools flag broken hreflang clusters, one-way references, and non-200 targets.
  4. Manual browser check. Visit each page. View source. Search for "hreflang." Count the tags. Two language versions means three tags per page (en, id, x-default). If you count fewer, something's missing.

Audit quarterly, or after any URL structure change. Hreflang errors are silent. Your pages don't break visibly. They just stop appearing in the right search results for the right audience.

What about AI search engines?

This is the emerging question. Do ChatGPT, Gemini, and Perplexity respect hreflang?

The honest answer: not directly. AI search systems don't process hreflang the way Google does. They crawl content, chunk it, and embed it into vector databases. Language detection happens at the content level, not the tag level.

But hreflang still matters for AI search indirectly. When Google properly indexes both language versions of your site, AI systems that use Google's index as a source (which most do, to some degree) inherit that structure. Your Indonesian content shows up for Indonesian queries. Your English content shows up for English queries. Without hreflang, Google might only index one version. Then AI systems only see one version.

The chain matters: correct hreflang helps Google index correctly, which helps AI systems find the right version of your content. It's not a direct signal. It's infrastructure that makes every other system work better.

A real audit pattern

When I audit a bilingual site's hreflang implementation, I follow this sequence:

  1. Crawl both language roots. Map every URL pair (en/page + id/page).
  2. Check for orphans. Pages that exist in one language but not the other. These need either a translation or a removal of the hreflang tag.
  3. Validate every cluster. For each URL pair, verify reciprocal tags, self-references, x-default, and canonical alignment.
  4. Check HTTP response codes. Every URL referenced in an hreflang tag must return 200. Not 301. Not 404. 200.
  5. Cross-reference with sitemap. If using sitemap-based hreflang, verify it matches the HTML tags. If using HTML tags only, verify the sitemap includes both language versions.

Most bilingual sites fail at step 2. They have 30 pages in English, 12 pages in Indonesian, and hreflang tags on all 30 English pages pointing to Indonesian URLs that don't exist. Google sees those broken references and discards the entire hreflang set. Not just for the missing pages. For all of them [3].

If you can't translate every page, that's fine. Only add hreflang tags to pages that actually have a counterpart in both languages.

The cost of getting this wrong

On one client audit (an Indonesian manufacturing company with EN/ID content), fixing hreflang implementation increased organic traffic to the Indonesian version by 34% within six weeks. The Indonesian pages had been live for over a year. They were well-written. Good schema. Decent backlinks. But the hreflang was broken, one-directional. Google was serving the English version to Indonesian users.

The fix took two hours. Template-based. Applied globally. The impact took six weeks to manifest because Google needed to recrawl and reprocess the hreflang annotations.

Two hours of work. 34% traffic increase. That's the kind of ROI you get from fixing infrastructure problems that nobody can see.

Do it once. Do it in the template.

The core lesson: hreflang is not a per-page task. It's a system-level concern. Build it into your header template. Make it automatic. Make it impossible to forget the self-reference or the reciprocal tag.

If you're still manually adding hreflang tags to individual pages, you will make mistakes. Not might. Will. The question is how long those mistakes suppress your rankings before someone notices.

Build the system. Let the system do the work.


Frequently Asked Questions

Does hreflang work for Bing and other search engines?

Bing uses a different approach. Instead of hreflang, Bing relies on the content-language meta tag and the lang attribute on the HTML element. However, Bing has increasingly started to support hreflang as well. For maximum compatibility, use hreflang for Google, set the lang attribute on your <html> tag, and include the content-language meta tag. Cover all bases.

Should I use "id" or "id-ID" for the Indonesian hreflang code?

Use id (language only) unless you're targeting Indonesian speakers in a specific country other than Indonesia. The format id-ID (language-country) specifies Indonesian as spoken in Indonesia specifically. For most bilingual sites, the language-only code id is sufficient. Adding the country code only matters if you have, say, Indonesian content for Malaysian audiences (id-MY), which is rare.

Can incorrect hreflang cause a Google penalty?

No. Google doesn't penalize incorrect hreflang. It ignores it. That might sound better, but it's arguably worse. A penalty is visible in Search Console. Ignored hreflang is invisible. You think everything is working. Meanwhile, Google is serving the wrong language version to your audience, and you have no warning unless you're actively monitoring language-specific search performance.

What is x-default and do I really need it?

The x-default value tells search engines which page to show users whose language or region doesn't match any of your hreflang tags. For a bilingual English-Indonesian site, a visitor from France wouldn't match either en or id. The x-default sends them to your chosen fallback (usually the English version). It's technically optional, but there's no reason to skip it. It takes one extra line of code and handles edge cases cleanly.

How long does it take for hreflang changes to take effect?

Typically 2 to 8 weeks. Google needs to recrawl all pages in the hreflang cluster, reprocess the annotations, and update its index. You can speed this up by submitting updated pages through Google Search Console's URL Inspection tool and resubmitting your sitemap. But there's no instant switch. Plan for a minimum of two weeks before checking results.

References

  1. Google Search Central. "Tell Google about localized versions of your page." Google Developers, 2024. Link
  2. Hashmeta. "Why Multilingual Hreflang Mistakes Destroy Rankings." Hashmeta Blog, 2024. Link
  3. Collaborada. "Common Hreflang Mistakes to Avoid." Collaborada Blog, 2024. Link
  4. Mueller, John. Cited in Seobility. "Frequent SEO issues on multilingual websites and how to fix them." Seobility Blog, 2024. Link
  5. Search Engine Journal. "Ask An SEO: What Are The Most Common Hreflang Mistakes?" Search Engine Journal, 2024. Link

Related notes

2026-03-28

The companies that show up in ChatGPT are the ones that bothered to be verifiable.