The html-has-lang rule flags a page whose <html> element has no lang attribute, or an empty one. Without it, screen readers, translation tools and browsers have to guess which language the page is in. The fix is one attribute in the page template: <html lang="en">.
What the rule means
WCAG 3.1.1 Language of Page requires that the default human language of each page can be determined by software. In HTML that is the lang attribute on the root element. Its value is a language tag such as en, en-GB or de.
The rule is deliberately simple: it passes when <html> carries a non-empty lang. It also passes with a non-empty xml:lang, but only for XHTML documents served as XML. On an ordinary HTML page, xml:lang without lang fails, because browsers ignore it there.
What the rule does not check:
- whether the value is a valid language tag – that is undefined;
- whether the tag names the language the page is actually written in – a German page marked
lang="en"passes; - passages in another language inside the page – that is WCAG 3.1.2 and the rule undefined.
Who is affected
Mainly blind and partially sighted people using a screen reader. The screen reader picks its voice and pronunciation rules from the page language. Without a lang, it falls back to the user's default, so an English page read with a German voice becomes hard to follow, and the other way round. Browser translation, hyphenation with hyphens: auto and spell checking in form fields also rely on the page language.
A missing page language is common: the WebAIM Million 2026 found it on 13.5% of the top one million home pages [3].
Why the check fails
- Hand-written or old templates that start with a bare
<html>. - Static site generators and starter kits whose base layout never set a language.
- CMS themes that print the attribute from a setting that is empty on this installation.
- Single-page apps where the server sends an empty shell and
langis only set, if at all, by JavaScript later. xml:langwithoutlang, copied from old XHTML templates.- Error pages, landing pages and email-to-web views built outside the main template.
How to fix it
- Find the one template or layout that outputs
<html>– every page inherits from it. - Add
langwith the page's main language. Use the short code (en,de); add a region (en-GB,de-AT) only when it matters, for example for spelling. - On a multilingual site, set the value per page from the locale, never as a fixed string in a shared template.
<!-- Before: no page language -->
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Delivery and returns – Example Shop</title>
</head>
<body>
<main><h1>Delivery and returns</h1></main>
</body>
</html>
<!-- After: the page language is set on the root element -->
<!doctype html>
<html lang="en-GB">
<head>
<meta charset="utf-8">
<title>Delivery and returns – Example Shop</title>
</head>
<body>
<main><h1>Delivery and returns</h1></main>
</body>
</html>
In a single-page app, render the attribute on the server or in the static index.html. Setting document.documentElement.lang after load helps, but a crawler or assistive technology that reads the first response still sees the page without a language.
How to test it manually
- Open the page source (Ctrl+U or Cmd+Option+U) and look at the
<html>tag – or rundocument.documentElement.langin the browser console. An empty result means no language. - Check that the value matches the language the text is written in. The rule cannot see a wrong language.
- Test pages from every template: home page, article, shop, search results, 404 page.
- With a screen reader (NVDA is free on Windows, VoiceOver is built into macOS and iOS), read the first paragraph and listen: does the pronunciation fit the language?
Related WCAG criterion
3.1.1 Language of Page, Level A. Related: 3.1.2 Language of Parts for passages in another language inside the page.
How Reviseberg reports it
Reviseberg runs html-has-lang on every crawled page at 1280px. Because the attribute comes from a shared template, the finding usually appears on every page at once – one element per page. The issue list shows the rule with its severity, WCAG criterion 3.1.1 and level, the number of pages, and the points you get back by fixing it. The detail view shows the selector (html), the HTML snippet of the opening tag and every affected page, so you can tell a site-wide template problem from a single stray page. You can mark a finding as "ignore", "can't fix" or "false positive" with a reason; every decision is logged.
Related rules
- undefined – the page has a
lang, but its value is not a valid language tag. - undefined – an invalid
langon a passage inside the page. - undefined – the other template-level fact every page needs.
Check the page language across your whole site – get a free scan