Deadheading is cutting the spent growth off a plant.
This does that to your <head>. It finds markup that is harmful, deprecated or merely dead weight — and for every finding, links to a written explanation of why, with sources.
$ npx deadhead dist
dist/index.html
5:5 unnecessary meta/http-equiv-x-ua-compatible
X-UA-Compatible only ever controlled Internet Explorer document modes,
and no shipping browser reads it.
→ Delete it. Internet Explorer and legacy Edge modes no longer exist.
https://deadhead.cevdet.ch/rules/meta/http-equiv-x-ua-compatible
✖ 1 finding (1 unnecessary)Why another linter
Most tools tell you that something is wrong. The interesting question with head markup is why, and whether the advice you read in 2014 still holds. Every rule here is a markdown document carrying at least two independent sources, and the linter is generated from those documents. A rule that cannot be explained does not ship.
Severity says what the finding costs you, not how loudly the tool wants to say it: harmful breaks something for users, deprecated is formally obsolete but inert, unnecessary works but is dead weight.
Three runtimes, one rule set
CLI
Walks files, directories and globs. Stylish, JSON and SARIF output, so CI annotates the diff. Exit codes are the contract.
Bookmarklet
One IIFE with the rules inlined and no
fetch, so a strict CSP cannot block it. It inspects rendered output, which is ground truth.ESLint plugin
The same findings inside
eslint, with autofix, because a fix is a text edit over a byte range either way.
A conformance suite runs every fixture through all three and asserts identical findings. That, rather than shared code, is what stops them drifting.
The rules
46 so far, worst first. Browse them by namespace.
- document/doctypeHarmful
The page has no doctype, or one other than <!doctype html>; a missing or legacy doctype can put the browser in quirks or limited-quirks mode, where CSS layout follows old compatibility rules.
- document/html-langHarmful
An <html> without a non-empty lang leaves the page's language unknown, so screen readers guess how to pronounce it; WCAG 3.1.1, Level A.
- document/main-multipleHarmful
A document may have only one main element without the hidden attribute; with two, skip links and landmark navigation land on one and the other is missed.
- head/canonical-multipleHarmful
A page that declares more than one canonical URL has Google ignore all of them, so its duplicate URLs stop consolidating.
- head/charset-positionHarmful
The encoding declaration must be fully inside the first 1024 bytes or the parser never sees it.
- head/viewportHarmful
Without a viewport meta tag, mobile browsers lay the page out at a desktop width of about 980px and shrink it to fit.
- link/preload-asHarmful
A preload with no as has no destination, so the browser fetches nothing and the critical resource loads late, as if the hint were never written.
- link/preload-font-crossoriginHarmful
Fonts are always fetched in CORS mode; a font preload without crossorigin doesn't match that request, so the font is downloaded twice.
- meta/http-equiv-refreshHarmful
http-equiv=refresh moves the reader on a timer, which WCAG names as a failure and the back button cannot undo.
- meta/og-relative-urlHarmful
og:url, og:image, og:video and og:audio must be absolute http(s) URLs; a relative or scheme-less value isn't a URL to the crawlers that build link previews.
- meta/viewport-user-scalableHarmful
A viewport with user-scalable=no or a maximum-scale below 2 stops people zooming to read, failing WCAG 1.4.4.
- script/json-ld-syntaxHarmful
A <script type="application/ld+json"> whose contents don't parse as JSON is discarded whole, so none of its structured data reaches search engines.
- attr/table-presentationalDeprecated
align, bgcolor, border, cellpadding, cellspacing, frame, rules, summary and width on table are obsolete; browsers still apply the styling ones as low-priority hints, so move them to CSS and describe tables with caption.
- element/acronymDeprecated
acronym is obsolete; browsers already treat it exactly like abbr, so use abbr.
- element/appletDeprecated
applet is obsolete and nothing can run it: no browser loads Java applets, and the JDK itself removed the Applet API; only the fallback text shows.
- element/basefontDeprecated
basefont is obsolete and does nothing: only old Internet Explorer applied it, and the HTML Standard renders it as display:none. Set the font in CSS.
- element/bgsoundDeprecated
bgsound was Internet Explorer's autoplaying background sound; it is obsolete, and the HTML Standard makes it an unknown element that plays nothing. Use audio with controls.
- element/bigDeprecated
big is obsolete: it only makes text one size larger, with no meaning attached. Use CSS font-size, or the element that says why the text matters.
- element/blinkDeprecated
blink is obsolete and no current browser blinks it; don't recreate the effect, because blinking content fails WCAG 2.2.2 Pause, Stop, Hide.
- element/centerDeprecated
center is obsolete presentational markup for horizontal centering; do it in CSS with text-align: center for text and margin-inline: auto for blocks.
- element/dirDeprecated
The dir element is obsolete; browsers already treat it exactly like ul, so use ul. The global dir attribute is unrelated and current.
- element/fontDeprecated
font is obsolete presentational markup that still styles text through color, face and size hints; move the styling to CSS.
- element/framesetDeprecated
frameset, frame and noframes are obsolete; a frameset page is several separate documents in one window, so use one document with iframe and CSS, or server-side includes.
- element/isindexDeprecated
isindex is obsolete, and browsers dropped its search-form behaviour in 2016–17 over XSS concerns; it now renders nothing useful. Use a form with a text input.
- element/marqueeDeprecated
marquee is obsolete, yet every browser still scrolls it; auto-scrolling text with no pause control fails WCAG 2.2.2 Pause, Stop, Hide.
- element/nobrDeprecated
nobr is obsolete presentational markup that stops text wrapping; set white-space: nowrap in CSS instead.
- element/noembedDeprecated
noembed is obsolete and its content is never displayed, not even when the embed fails; put fallback content inside an object element instead.
- element/plaintextDeprecated
plaintext is obsolete and never ends: everything after it, including the page's closing tags, becomes text; serve a text/plain file or use pre and code with the content escaped.
- element/plugin-embedDeprecated
This object or embed loads a Flash, Java or Silverlight plug-in; no current browser runs plug-ins and all three platforms are end-of-life, so the content never appears.
- element/spacerDeprecated
spacer is obsolete and does nothing in any current browser; the gap it was meant to add is gone, so set it with CSS margin or gap.
- element/strikeDeprecated
strike is obsolete: it draws a line through text but tells assistive technology nothing; use del for a removal or s for text that is no longer accurate.
- element/ttDeprecated
tt is obsolete presentational markup for monospace text; use code, kbd, samp or var for what the text is, and CSS if you only want the font.
- element/xmpDeprecated
xmp is obsolete; it shows markup as literal text but can't hold character references or its own end tag, so use pre and code with < and & escaped.
- link/apple-touch-icon-precomposedUnnecessary
A second declaration of the home-screen icon whose only effect, skipping iOS's gloss overlay, has been the default since iOS 7.
- link/image-srcUnnecessary
A pre-Open Graph hint for social preview images that was never accepted as a link relation and is no longer read.
- link/mask-iconUnnecessary
A Safari-only monochrome pinned-tab icon that was never a link type; it should not be used.
- link/shortcut-iconUnnecessary
shortcut is not a link relation; browsers already parse rel="shortcut icon" as icon, so the token is inert.
- link/sitemapUnnecessary
Search engines find sitemaps through robots.txt and their webmaster tools; none documents reading a link to one from HTML.
- meta/apple-mobile-web-app-capableUnnecessary
Apple's pre-manifest switch for launching a Home Screen web app standalone; the manifest's display member replaces it.
- meta/application-nameUnnecessary
A standard metadata name for the web app's name, superseded in practice by the manifest's name and short_name.
- meta/http-equiv-x-ua-compatibleUnnecessary
X-UA-Compatible only ever controlled Internet Explorer document modes, and no shipping browser reads it.
- meta/keywordsUnnecessary
A comma-separated topic list that no major search engine ranks on, and that the HTML Standard itself warns against.
- meta/mobile-web-app-capableUnnecessary
Chrome's pre-manifest switch for launching a home screen shortcut as an app; the manifest's display member replaced it.
- meta/msapplicationUnnecessary
Internet Explorer pinned-site and Start-tile metadata; nothing that still ships reads it.
- meta/twitterUnnecessary
Twitter Card metadata; X has removed its Cards documentation, and every other preview consumer reads Open Graph.
- script/type-javascript-mimeUnnecessary
A JavaScript MIME type in script[type] is the default; the spec says to omit the attribute.
Get it
Installation and usage, or read the source on GitHub.