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.
| Option | When it wins | Watch-out | Takeaway |
|---|---|---|---|
| html lang | Every HTML document | Copy-pasting en on a translated page | This is the one to get right |
| Content-Language | You want HTTP metadata for non-HTML clients | CDN default that disagrees with the page | Optional; keep it honest |
| hreflang | You ship multiple locale URLs | Using it on a single-language site | Search cluster, not a11y |
| lang on a subtree | Quotations or widgets in another language | Forgetting to reset after the widget | Local 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 nowFAQ
- 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.