RoastMyOpsec

News

Content-Language vs HTML lang for Public Sites

August 10, 2022

Content-Language vs HTML lang for public sites is a header-versus-document decision: the lang attribute on <html> is what browsers, screen readers, and hyphenation use, while Content-Language is an HTTP metadata field for the intended audience language of the payload. Set lang (and xml:lang if you still ship XHTML) correctly on every document; Content-Language is optional extra that should match. RoastMyOpsec does not score either as a standalone F, and neither replaces hreflang for localized URL clusters.

The practical difference

HTML lang is a BCP 47 tag on the document or on a subtree (a French quotation inside an English article). Assistive tech uses it to pick a voice. Spellcheck and translation heuristics use it. Content-Language, per RFC 9110, describes the language of the representation for processors that never parse HTML — including some caches and non-browser clients.

They can disagree. A CDN that stamps Content-Language: en on every response while the page is lang="de" is how you get English voices on German copy. hreflang is a third layer: it maps URLs to locales for search. Do not fake Content-Language as a substitute for real localized pages.

Language tags are not a privacy signal and not a security header. They do not hide a path. They do prevent embarrassing a11y misses.

How to choose based on what you shipped

Always set <html lang> to the language of the page. Add Content-Language at the edge if non-HTML consumers or older proxies need it, matching the document. For mixed-language pages, lang on the root plus lang on the minority passages beats a confused header. Localized sites still need real URLs and hreflang, not headers alone.

OptionWhen it winsWatch-outTakeaway
html langEvery HTML documentCopy-pasting en on a translated pageThis is the one to get right
Content-LanguageYou want HTTP metadata for non-HTML clientsCDN default that disagrees with the pageOptional; keep it honest
hreflangYou ship multiple locale URLsUsing it on a single-language siteSearch cluster, not a11y
lang on a subtreeQuotations or widgets in another languageForgetting to reset after the widgetLocal override, not a header

When Content-Language still wins

Content-Language still wins on PDFs, plain text, and APIs that are not HTML but still have a human language. html lang still wins on documents. Missing Content-Language on a marketing HTML page is normal. Wrong html lang is the a11y miss.

Common mistakes

The first mistake is leaving lang="en" on a translated template because the layout started in English.

The second mistake is letting a CDN stamp Content-Language that contradicts the document.

The third mistake is treating a missing Content-Language header as a security finding.

What a URL roast can prove

RoastMyOpsec does not score Content-Language, html lang, hreflang, JSON-LD, or Open Graph as standalone Fs. The free roast is security headers, TLS, cookies, .git/HEAD, .env, package.json signatures, and js-secrets. Fix lang for humans and assistive tech; do not expect a URL roast to QA your locales.

Free audit the URL you own

RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.

Free audit now

FAQ

Is Content-Language a substitute for html lang?
No. Browsers and accessibility trees read the lang attribute. Set that even if you also send the header.
Does lang hide a page from crawlers?
No. It describes language. Indexing uses robots, noindex, and canonicals.
Does RoastMyOpsec fail missing lang?
No. Document language is not a scored F on the free roast.

Sources

Related guides