Free Online HTML Linter
Paste your HTML and get a line-by-line quality report: missing alt text, duplicate ids, deprecated tags, skipped heading levels, unbalanced tags and 24 more checks — plus an accessibility score. No signup, no uploads.
Choose rules to check (29 available)
Press Lint HTML to analyze your markup.
Worked examples
Each example loads into the editor with one click. The issue counts below are computed live by the linter engine itself.
Missing alt text + deprecated tags
1 error(s), 2 warning(s), 0 info note(s) \u2014 computed by the linter engine
<!DOCTYPE html> <html lang="en"> <head> <title>Gallery</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <center> <font size="4">Welcome to our gallery</font> <img src="hero.jpg"> <img src="logo.png" alt=""> </center> </body> </html>
Unclosed & mismatched tags
2 error(s), 0 warning(s), 0 info note(s) \u2014 computed by the linter engine
<!DOCTYPE html> <html lang="en"> <head> <title>Broken page</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <div class="wrap"> <p>Hello, world! </div> </span> </body> </html>
Missing document basics
0 error(s), 4 warning(s), 0 info note(s) \u2014 computed by the linter engine
<html> <body> <h1>Hello</h1> <p>No doctype, no lang, no title, no viewport.</p> </body> </html>
Duplicate ids
2 error(s), 0 warning(s), 0 info note(s) \u2014 computed by the linter engine
<!DOCTYPE html> <html lang="en"> <head> <title>IDs</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <div id="main">First</div> <p id="main">Second</p> <span id="main">Third</span> </body> </html>
Skipped heading levels
0 error(s), 3 warning(s), 0 info note(s) \u2014 computed by the linter engine
<!DOCTYPE html> <html lang="en"> <head> <title>Headings</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Page title</h1> <h3>Skipped h2</h3> <h1>Second h1</h1> <h5>Skipped h2-h4</h5> </body> </html>
Buttons without type + inline style overuse
0 error(s), 13 warning(s), 0 info note(s) \u2014 computed by the linter engine
<!DOCTYPE html> <html lang="en"> <head> <title>Form</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <form> <button>Submit</button> <button style="color:red">A</button> <button style="color:blue">B</button> <button style="color:green">C</button> <button style="color:black">D</button> <button style="color:gray">E</button> <button style="color:pink">F</button> </form> </body> </html>
Uppercase tags & sloppy attributes
0 error(s), 1 warning(s), 6 info note(s) \u2014 computed by the linter engine
<!DOCTYPE html> <html lang="en"> <head> <title>Case</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <DIV CLASS="box" ID='x'> <!-- TODO: clean this up --> <P>Fish & chips</P> </DIV> </body> </html>
Invalid nesting
2 error(s), 0 warning(s), 0 info note(s) \u2014 computed by the linter engine
<!DOCTYPE html> <html lang="en"> <head> <title>Nesting</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <a href="/x"><button type="button">Click</button></a> <p>Some text <div>a block</div> more text</p> </body> </html>
Clean document (zero issues)
0 error(s), 0 warning(s), 0 info note(s) \u2014 computed by the linter engine
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Clean page</title> </head> <body> <h1>Hello</h1> <h2>Section</h2> <p><img src="a.png" alt="A small icon"> <a href="/about">About</a></p> <button type="button">OK</button> </body> </html>
Rules reference
| Rule | Severity | What it checks |
|---|---|---|
mismatched-tag | error | A closing tag does not match the currently open element (e.g. <div> closed by </span>). |
unclosed-tag | error | An element was opened but never closed before the end of the document. |
unexpected-close | error | A closing tag with no matching open tag anywhere in the document. |
duplicate-id | error | The same id value is used on more than one element; ids must be unique per page. |
duplicate-attribute | error | The same attribute name appears twice on one element. |
missing-alt | error | <img> without an alt attribute. Screen readers and search engines need it; use alt="" only for purely decorative images. |
img-missing-src | error | <img> without a src attribute will render as a broken image. |
unclosed-comment | error | <!-- without a matching --> swallows the rest of the document. |
invalid-nesting | error | Elements nested in a way HTML forbids: interactive content inside <a>/<button>, <p> inside <p>, or block elements inside <p>. |
doctype-first | warning | The document should start with <!DOCTYPE html> so browsers use standards mode. |
missing-lang | warning | <html> needs a lang attribute (e.g. lang="en") so screen readers pronounce text correctly. |
missing-title | warning | Every document needs a <title> in <head> for the tab label, bookmarks and SEO. |
empty-title | warning | A <title> element is present but has no text. |
missing-viewport | warning | No <meta name="viewport"> — the page will not scale properly on mobile devices. |
deprecated-tag | warning | Obsolete presentational tags (center, font, marquee, big, strike, tt, applet, ...) — use CSS instead. |
heading-order-skip | warning | Headings must not skip levels (h1 followed by h3) — screen readers use heading order for navigation. |
multiple-h1 | warning | More than one <h1> on the page weakens the document outline; use one h1 per page. |
button-missing-type | warning | <button> without type="button|submit|reset" defaults to submit and can submit forms unexpectedly. |
inline-style-overuse | warning | More than 5 elements use inline style="..." — move presentation to a stylesheet for maintainability. |
unquoted-attribute | warning | Attribute values should be wrapped in quotes; unquoted values break on spaces and special characters. |
unescaped-ampersand | warning | A bare & in text must be written as & (or another entity) to avoid parsing surprises. |
tagname-uppercase | info | HTML convention is lowercase tag names. |
attr-uppercase | info | HTML convention is lowercase attribute names. |
single-quote-attribute | info | Attribute uses single quotes; double quotes are the common convention. |
todo-comment | info | Comment contains TODO, FIXME, XXX, HACK or BUG — consider resolving before shipping. |
trailing-whitespace | info | Line ends with spaces or tabs. |
line-too-long | info | Line exceeds 200 characters — hard to review and diff. |
placeholder-link | info | <a href="#"> is a placeholder; keyboard and screen-reader users get a dead link. |
empty-document | info | There is nothing to lint. |
Frequently asked questions
Is this HTML linter a replacement for the W3C validator?
No. The W3C Nu HTML Checker answers a different question: is this markup valid HTML5 according to the specification? This linter answers: is this markup well-written? It checks style, best practices and accessibility — missing alt text, skipped heading levels, deprecated tags, duplicate ids, buttons without a type — things a validator does not grade you on. Use both: validate for correctness, lint for quality.
Which rules does the HTML linter check?
29 rules across three severities. Errors: mismatched tags, unclosed tags, unexpected closing tags, duplicate ids, duplicate attributes, images missing alt text, images missing src, unclosed comments and invalid nesting. Warnings: missing or misplaced doctype, missing lang on <html>, missing or empty <title>, missing viewport meta, deprecated tags (center, font, marquee and 14 more), skipped heading levels, multiple <h1>, buttons without a type, inline style overuse, unquoted attributes and unescaped ampersands. Info notes: uppercase tag/attribute names, single-quoted attributes, TODO comments, trailing whitespace, long lines, placeholder links and empty documents. The full list with descriptions is in the rules reference table below.
Is my HTML uploaded to a server?
No. The linter is 100% client-side JavaScript: tokenizing, rule checks and the accessibility score all run locally in your browser tab. There are zero network requests — no uploads, no signup, no file-size gate imposed by a server. You can even disconnect from the internet after the page loads and it keeps working.
What accessibility issues does it catch?
Nine rules feed the accessibility score: images missing alt text, <html> missing a lang attribute, skipped heading levels, multiple <h1> elements, <button> without a type, deprecated presentational tags, duplicate ids, and missing or empty <title>. Each one maps to a real barrier: screen readers announce images by their alt text, use heading order and the lang attribute to navigate and pronounce content, and depend on unique ids for form labels and skip links. Fixing these also helps SEO, since search engines use the same signals to understand your page.
How is the accessibility score calculated?
The score starts at 100 and deducts 10 points per accessibility error, 4 per warning and 1 per info note, floored at 0. Only the nine accessibility-related rules count — pure style notes like trailing whitespace never touch your score. The result is graded A (90+) through F (below 60). Treat it as a triage signal, not a certification: a page can score 100 and still have usability problems no automated check can see.
Can the linter fix my HTML automatically?
No — deliberately. Unlike formatting, most lint fixes need human judgment: only you can write a good alt description or choose the right heading level. Instead of guessing, every issue explains exactly what is wrong and how to fix it, with the precise line number. Use the Copy report button to paste the full issue list into your editor or a pull request.
Why doesn't the linter use the browser's built-in HTML parser?
Because the browser's parser is too forgiving to be a linter. DOMParser silently repairs broken markup — unclosed tags get auto-closed, misplaced elements get moved — so a parser-based check literally cannot see the errors you most need to find. This linter uses its own hand-rolled tokenizer, so it keeps going after an error, reports every issue with an exact line number, and checks style and accessibility rules that a parser never looks at.
What are the limitations of this HTML linter?
It is a heuristic linter, not a specification validator: it does not check your markup against the HTML5 spec the way the W3C checker does. It never fetches external resources, never executes JavaScript or CSS, and assumes the snippet you paste is the document you mean (a fragment without <html> will flag missing doctype/lang/title — paste full documents for document-level rules). On severely broken markup the error recovery is best-effort, so fix fatal tag-balance errors first and re-lint.
Related tools
Free tool by NRTechStudio