← Blog

SEO

Schema and hreflang: the Dutch SEO playbook for 2026

A client in Eindhoven was outranked in their own city by a Belgian competitor for three months. The fix was not content. It was hreflang and a JSON-LD typo.

Jacob Molkenboer· Founder · A Brand New Company· 3 Jun 2026· 9 min
Open leather atlas showing a map of the Low Countries, brass compass on the page, green silk ribbon across the spine, cream index card with wax seal.

A real ranking puzzle in Eindhoven

In April a B2B SaaS team in Eindhoven called us with a painful Search Console screenshot. For their main category query, they ranked position 3 in Belgium and position 14 in the Netherlands. Their competitor, a Brussels company with a third of their content and a slower site, ranked position 4 in their home city.

They had spent six months on content. Two senior writers. A new pillar page strategy. Nothing moved.

The fix took ninety minutes. Two missing hreflang return tags, one wrong language code (nl instead of nl-NL), and a JSON-LD Organization block with addressCountry set to "Belgium" because someone had copied it from a Schema.org sample and never changed it. Three weeks after the fix they were position 2 in NL and stable at 3 in BE.

Why this signal still matters in 2026

The conventional wisdom keeps shifting. AI Overviews are eating the top of the SERP. ChatGPT and Perplexity are stealing zero-click traffic. Some SEO blogs argue schema is dead because Google extracts entities from raw HTML now.

We see the opposite in client data. When a model has to pick which of three Dutch-language pages to surface for a Belgian query, the strongest signals it can find are still the same three:

  • The hreflang cluster, which tells the crawler which version is for which audience.
  • The JSON-LD markup, which tells the crawler which entity owns the page.
  • The canonical, which tells the crawler which URL is the source of truth.

When those three agree, the page wins more queries than its content alone would predict. When they disagree, even great content sits stuck. AI Overviews amplify this rather than replace it: when a model needs to cite a source, it picks the page whose entity is least ambiguous.

The four hreflang traps we keep finding

Most Dutch sites we audit fall into one of four hreflang traps. The fix for each is mechanical.

Trap 1: using nl when you mean nl-NL

The nl tag means "any Dutch speaker, anywhere". If your site only serves the Netherlands but the tag says nl, you are telling Google your page is equally relevant to a reader in Antwerp and a reader in Utrecht. Google will sometimes show your page in Belgium where your shipping does not work, then bury it in the Netherlands because the signal is muddy.

Be explicit:

<link rel="alternate" hreflang="nl-NL" href="https://example.nl/contact" />
<link rel="alternate" hreflang="nl-BE" href="https://example.be/contact" />
<link rel="alternate" hreflang="x-default" href="https://example.nl/contact" />

If you only have a Dutch version, still mark it nl-NL. The hreflang spec uses ISO 639-1 for language and ISO 3166-1 Alpha 2 for region. Mixing them is the most common reason audits flag your site as having hreflang errors.

Trap 2: missing return tags

If page A links to page B with hreflang, page B has to link back to page A. This rule breaks more often than any other, because most CMS templates only emit the alternates for the current language. They forget the self-reference and the back-reference.

A correctly tagged Dutch contact page should include all three tags above. The exact same three tags appear on the Belgian version. Symmetric. If they are not symmetric, Google ignores the cluster. See Google's hreflang documentation for the formal spec.

Trap 3: x-default pointing to a redirect

We often find x-default pointing to a URL that 301s (example.com/ redirects to example.nl/). The redirect resolves fine for users, but the hreflang validator sees a 301 and disregards the whole cluster. Point x-default at the final URL.

Trap 4: hreflang in sitemap AND in head

You should pick one location: either embed hreflang inside your XML sitemap, or emit it in the <head> of each page. Both is technically allowed. In practice we see them drift apart within six months and Google ends up confused. Pick <head> for small sites and sitemap for large ones, then enforce it in code review.

Warning

Multilingual WordPress plugins emit hreflang in both the head and the sitemap by default. Turn off one source on day one, or you will fix the same drift bug twice a year.

The JSON-LD that actually moves the needle

Most Dutch sites we audit have either no schema or schema generated by a plugin that does too much. The version we install on every project has four blocks, no more.

Organization, on every page

{
  "@context": "https://schema.org",
  "@type": "Organization",
  "@id": "https://example.nl/#organization",
  "name": "Voorbeeld B.V.",
  "url": "https://example.nl",
  "logo": "https://example.nl/logo.svg",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "Keizersgracht 123",
    "postalCode": "1015 CJ",
    "addressLocality": "Amsterdam",
    "addressCountry": "NL"
  },
  "vatID": "NL123456789B01",
  "sameAs": [
    "https://www.linkedin.com/company/voorbeeld",
    "https://nl.wikipedia.org/wiki/Voorbeeld"
  ]
}

Three things to notice:

  • The @id is a stable URL with a fragment. We use the same @id across every page. This lets Google connect every page to one entity instead of inferring one Organization per URL.
  • addressCountry is "NL", two letters, ISO 3166-1. Not "Nederland", not "The Netherlands". The crawler expects the country code.
  • vatID carries the Dutch BTW number. It is one of the strongest entity signals we know of for Dutch B2B sites, and most plugins skip it entirely.

LocalBusiness, only on the contact page

Skip LocalBusiness on every page. Use it on /contact only, with opening hours and geo coordinates. Google deduplicates anyway, and one rich block beats five sparse ones. See Schema.org's LocalBusiness reference for the field definitions.

BreadcrumbList, on every non-home page

Cheap, always works, helps the SERP rendering. We generate it from the URL path at build time so nobody has to maintain it.

Article or Product, where it applies

One Article block per blog post. One Product block per product. Both reference the Organization @id via publisher or brand. The graph stays connected, which is exactly what the model needs to disambiguate entities.

The 30-minute audit you can run today

You do not need a paid tool. Here is the audit we run on every new client before we touch their site.

Step 1: render the head

curl -s https://yourdomain.nl | grep -E "(hreflang|canonical|application/ld\+json)"

If hreflang lines are missing, your CMS is not emitting them. If they are present, copy the cluster somewhere.

Step 2: validate the cluster

For each page that emits hreflang, fetch every URL in the cluster and confirm three things: every URL returns 200 (no 301s, no 404s), every URL in the cluster points back to every other URL in the cluster, and the language codes match across all members.

Ten pages you can eyeball. For a hundred pages, write a script:

URLS=(https://example.nl/ https://example.be/ https://example.nl/contact https://example.be/contact)
for u in "${URLS[@]}"; do
  echo "== $u =="
  curl -s "$u" | grep -oE 'hreflang="[^"]+" href="[^"]+"'
done

If page A lists B and page B does not list A, that is a missing return tag.

Step 3: validate the JSON-LD

Paste your Organization block into Schema.org's validator, then into Google's Rich Results Test. Both pass for different reasons. Check three things specifically: addressCountry is the two-letter code, @id is identical on every page that emits an Organization block, and sameAs only points to URLs you control or that genuinely describe you.

Step 4: Search Console diff

In Search Console, open the International Targeting report. Errors here are the smoking gun. Cross-reference with the Performance report filtered by country. If a market is underperforming relative to your content depth, the cluster is broken.

The entity layer beneath AI Overviews

A fair question in 2026: does any of this matter when LLMs answer the query before the user clicks?

We have watched this carefully across the client domains we run. The pattern is consistent. When an AI Overview cites a source, it cites the page whose schema names the same entity the Knowledge Graph already trusts. Sites with a clean Organization block, a Wikipedia or Wikidata link in sameAs, and a consistent @id across pages get cited. Sites with sparse or contradictory schema get summarized but not cited.

If your goal is to be the source the model points back to, schema is not optional. It is the cheapest entity signal you can ship.

When we rebuilt a Drenthe-based B2B site last spring as part of a legacy migration, the recurring failure was a theme that emitted partial hreflang clusters on every page rebuild. We solved it by moving the hreflang generation out of the theme and into the sitemap, with a build-time validator that fails the deploy if any cluster is asymmetric. That kind of small, boring fix is most of what we do under the heading of migration, and it is what shifts rankings without writing a single new word.

If your Dutch site is underperforming for queries you should win, run the four-step audit above this afternoon. The mistakes that cost the most are usually the ones that take the least time to fix.

Key takeaway

Hreflang and schema are not a content strategy. They are a clarity strategy. When the signals agree, your existing content ranks.

FAQ

Does hreflang still matter if I only sell in the Netherlands?

Yes. Marking your pages nl-NL keeps Google from showing them to Belgian or Surinamese audiences and burying them at home. A single nl-NL plus x-default cluster is enough.

What is the difference between nl and nl-NL?

nl means any Dutch speaker anywhere. nl-NL means a Dutch speaker in the Netherlands. nl-BE means a Dutch speaker in Belgium. Use the region-specific tag unless you genuinely target all three.

Do I need schema markup if Google can read my HTML directly?

Google can read your HTML, but schema is the cheapest way to tell the crawler which entity the page is about. It is the difference between being summarised and being cited.

Will AI Overviews replace structured data?

The opposite. AI Overviews need to pick one trusted source per query. Clean schema is how a model decides which Dutch page represents the entity it already knows about.

seostrategyarchitecturelegacy sitesweb designwordpress

Building something?

Start a project