Website fixes: code quality
The mistakes browsers hide.
Browsers are forgiving: when a page's HTML (the code that describes it) has a mistake, they repair it as they read and carry on, so mistakes go unnoticed until a layout breaks or content disappears. Each entry says what the browser does with the mistake and how to find it, whether you edit the site yourself or someone does it for you.
The page shows an error message from its own code
What it means. The visible page includes an error message printed by the software that builds it, such as "Fatal error", "Warning: Undefined variable", "Error establishing a database connection" or WordPress's "There has been a critical error on this website". Something on the server, often a plugin, a theme or custom code, has failed and printed the failure into the page.
Why it matters. Visitors see the message instead of, or alongside, your content, and part of the page may be missing after it. Error messages often reveal file paths and software details that help anyone looking for a way in.
How to fix it. Treat it as urgent. The report quotes the message, which usually names the file or plugin that failed.
- On WordPress, update or switch off the plugin or theme it names, then check again. After a critical error, WordPress usually emails the site's admin address with details and a link to its recovery mode.
- "Error establishing a database connection" means the site can't reach its database: contact your host.
- Ask your host or developer to stop errors showing on the live site and log them instead (PHP's
display_errorssetting, orWP_DEBUG_DISPLAYin WordPress). - If the page quotes an error on purpose, for example in a programming article, ignore this.
How the check spots it: it reads the visible text of the page you checked (not scripts or comments) for the exact wording of common error messages from PHP, WordPress, databases, JavaScript, Python and ASP.NET, such as "Fatal error", "Stack trace:" and "SQLSTATE[". Capitals matter, so ordinary sentences using the same words in lower case aren't flagged. It's always "Fix first".
An unclosed comment or tag hides the rest of the page
What it means. Some parts of HTML need a closing marker. A comment starts with <!-- and ends with -->; a <script>, <style>, <textarea>, <title>, <iframe> or <noscript> runs until its own closing tag. Here, one of them never ends.
Why it matters. The browser follows the rules exactly: everything after the opening marker, to the end of the file, becomes part of it. After a comment, the rest of the page doesn't appear. After a <script> or <style>, it's read as code. After a <textarea>, the rest of the HTML shows up as text in a form box. Visitors and Google miss whatever follows, often the footer, the contact details or a whole section.
How to fix it. The report gives the line where it starts. Open your browser's "view page source", which numbers the lines, and go there. On WordPress or a website builder, it's usually a pasted snippet (a tracking code, chat widget, embed or custom HTML block) that was cut short. Paste the full snippet again from where it came from, or add the missing --> or closing tag, then run the check again.
How the check spots it: as it reads the HTML, it notes a comment with no --> after it, or one of those tags with no closing tag before the end of the file. More than 2,000 characters after it is "Fix first"; less is "Worth fixing". If the page was over 1 MB or stopped loading part-way, the check only read part of it, so it doesn't report this at all. The check then doesn't report tags left open at the end of the page, since the browser never gets there normally.
The page embeds Flash, which no browser runs
What it means. Flash was a browser plug-in for animations, videos and menus. Adobe stopped supporting it at the end of 2020, and browsers have removed it. This page still embeds Flash content through an <embed> or <object> tag.
Why it matters. Whatever it showed is now an empty space or a broken plug-in icon. If it was a menu, an intro or a video, visitors are missing it, and many won't realise anything should be there.
How to fix it. The report gives its line in the page source. Work out what it showed and replace it: a video with an MP4 file or a video service's embed, an animation with an image or a short video, a menu with ordinary HTML. Then remove the old embed. On WordPress or another builder, it's usually an old embed block or shortcode in the page's content.
How the check spots it: it looks for an <embed> or <object> tag that names Flash's content type or a .swf file, and reports the line of the first one. It's always "Worth fixing".
The doctype is missing, late or old
What it means. The doctype is the first line of a web page, <!DOCTYPE html>, and tells the browser to use modern rules. Without it, with some of the doctypes used before HTML5, or with text or a tag before it, browsers switch to "quirks mode", an imitation of 1990s browsers kept so very old pages still display. A few old XHTML and HTML 4 Transitional doctypes trigger "limited quirks" mode instead, which changes only how images sit in table cells.
Why it matters. In quirks mode, sizes, spacing and tables follow different rules from the ones modern CSS is written for. Themes and page builders are designed for standards mode, so a page in quirks mode can look subtly wrong or break in ways that are hard to trace.
How to fix it. Make <!DOCTYPE html> the very first line of every page. On WordPress, the theme prints it, and a plugin printing something before it can cause the "late" version. On Shopify, it's at the top of the theme's layout file. Wix and Squarespace write it themselves; if the check reports it there, ask their support. MDN's page on the doctype explains the modes.
How the check spots it: it reads the doctype the way the HTML standard says browsers do, using the standard's list of old doctypes. No doctype, a late one or one that triggers quirks mode is "Worth fixing"; limited quirks mode is "Nice to have". Blank lines and comments before the doctype are fine, as they are for browsers.
The character encoding is missing, late or old
What it means. The character encoding tells the browser how to turn the bytes it downloads into letters. The web uses UTF-8, which holds every character and emoji. A page declares it with <meta charset="utf-8"> at the top of its <head>, or the server sends it in the Content-Type header. This finding means none is declared, an older one is (such as windows-1252), or the <meta charset> comes too late.
Why it matters. Without one, browsers guess, and a wrong guess turns curly quotes and accents into garbage like ’. The HTML standard says <meta charset> must be within the first 1,024 bytes; later than that, a browser may guess first and then have to start the page over. Older encodings can't hold every character people type into forms, such as emoji or names in other languages.
How to fix it. Make <meta charset="utf-8"> the first tag in <head>, and save the pages as UTF-8. Website builders and most themes do this for you, so a "too late" result usually means something was added above it, such as a large script or a tracking snippet. Your host can also add the charset to the Content-Type header.
How the check spots it: it looks for a charset in the Content-Type header, a <meta charset> tag or an older http-equiv tag. With none, it's "Worth fixing" if the text has characters beyond plain ASCII (basic English letters, digits and punctuation), otherwise "Nice to have". An encoding other than UTF-8 is "Nice to have", as is a <meta charset> more than 1,024 characters in when the header doesn't declare one.
Some links go nowhere
What it means. These links have no real address in their href: just #, nothing at all, or a script such as javascript:void(0). Some open a menu or pop-up through a script; others were meant to lead somewhere and never got an address. "undefined" or "null" usually means a script or page builder failed to fill it in.
Why it matters. If the link was meant to go somewhere, it's broken: tapping it does nothing, or jumps to the top of the page. If a script makes it work, keyboards and screen readers still treat it as a link rather than a button, which confuses the people using them, and Google can't follow it. Google's test checks for this too; when both find it, the report lists it once.
How to fix it. Go through the examples in the report:
- If it should lead to a page, give it the real address. In page builders, look for a button or image whose link field is empty or "#".
- If it opens a menu or pop-up, it should be a
<button>: a job for whoever looks after your theme or code. - A WordPress dropdown's top menu item is often a custom link to "#". Consider pointing it at a real overview page, then check the dropdown still opens on a phone.
- Remove links that do nothing. Links to a missing section are covered under sections that aren't there.
How the check spots it: it lists links on the page you checked whose href is empty, #, #!, a javascript: address that does nothing, or "undefined" or "null". Links with role="button", and <a> tags with no href at all, are left out. Five or more is "Worth fixing"; fewer is "Nice to have". Google's Lighthouse test also reports links it can't follow, as "Worth fixing", and the report keeps whichever rating is higher.
Elements are inside tags that can't hold them
What it means. HTML has rules about what goes inside what. A link can't contain another link or a button, a form can't contain another form, a heading can't start inside another heading, table cells belong in a table, and a page has one <html>, <head> and <body>. Blocks such as a <div>, a list or a heading can't sit inside a paragraph either.
Why it matters. The browser rebuilds the page to fit the rules, following the HTML standard: it ignores an inner form (so its fields are sent with the outer one), ends a link, heading or paragraph early where the next one starts, throws away table-cell tags outside a table and merges a second <body> into the first. A button inside a link may not work as expected. The rebuilt page rarely behaves quite as intended.
How to fix it. Each example gives a line and what the browser does. Look at it in "view page source", not the browser's inspector, which shows the page after the repair. Common causes:
- a card or banner made into a link, with another link or a button inside it
- a sign-up form widget placed inside another form
- a whole page, with its own
<html>and<body>, pasted into a custom HTML block
How the check spots it: it tracks which elements are open as it reads the HTML and notes these combinations. A block inside a paragraph counts only when a </p> turns up later for it, because leaving out a paragraph's closing tag before a block is legal HTML. It's "Worth fixing" when the examples include a link, button or form inside something that can't hold it, or a second <html>, <head> or <body>; otherwise "Nice to have".
A tag is missing a closing quote, or has an attribute twice
What it means. Attributes are the settings inside a tag, like href="…" or class="…". Either a quoted value never closes its quote, so it runs on into the tags after it, or one tag has the same attribute twice.
Why it matters. With a missing quote, the browser reads everything up to the next matching quote as the value, so a stretch of the page vanishes into it. With a repeated attribute, the browser keeps the first and ignores the second, so whatever the second was for, such as a style, doesn't happen.
How to fix it. The report names each tag and its line. In "view page source", check that every opening quote has a partner, and merge repeats into one: two classes become class="first second". In page builders, these usually come from a custom HTML block, a custom attribute or CSS class field, or a shortcode with a stray quote.
How the check spots it: it flags a quoted value that contains the start of a tag (<a, </p or <!--) and is more than 200 characters long or runs over more than one line. Short one-line values are left alone, since scripts sometimes hold HTML in them on purpose. A missing quote is "Worth fixing"; a repeated attribute alone is "Nice to have".
Tags don't match up
What it means. Most elements have an opening and a closing tag, like <div> and </div>. This finding covers an element opened and never closed, a closing tag with nothing to close (including </img> for elements that never have one), and an element written as <div/>, whose slash closes nothing in HTML.
Why it matters. The browser repairs each one: it closes the open element where it decides it must end, ignores the stray closing tag, and treats <div/> as <div>, so what follows ends up inside it. The repair is often right, which is why these go unnoticed. When it isn't, styles spread to the wrong parts, columns wrap or a menu swallows what follows, and the cause is hard to trace from the screen.
How to fix it. Each example says where to look and what closed the element for the browser. Use "view page source"; the browser's inspector shows the page after the repair. Most come from hand-edited HTML or snippets pasted into custom HTML blocks, widgets or theme files: an extra or missing </div>. One fix often clears several examples, so check again after each. The W3C HTML checker lists every mistake on the page, line by line.
How the check spots it: it tracks open elements and closes them the way a browser does. Elements whose closing tag is optional, such as <p>, <li> and table cells, don't count as unclosed, and crossed tags (<b><i></b></i>) count once. Ten or more is "Worth fixing"; fewer is "Nice to have". If the page was cut off or something hid the rest of it, elements open at the end aren't reported. Inline SVG, where /> works, is left alone.
Links point to sections that aren't there
What it means. A link can jump to part of the same page: #services scrolls to the element with id="services". These links name a section the page doesn't have, so tapping them does nothing.
Why it matters. On one-page sites they're usually the menu items and "Get a quote" buttons, the links people use most. When a section was renamed or removed, visitors tap, nothing happens, and some decide the site is broken.
How to fix it. For each name in the report, give the right section that name or point the link at one that exists. Names must match exactly, capitals included: #Contact doesn't find id="contact". In WordPress's block editor, it's the HTML anchor field in a block's advanced settings; most page builders have an anchor or ID setting on each section. Keep each name unique (see ids used more than once).
How the check spots it: it collects links to a #name on the same page (written as #name or after the page's own address) and looks for a matching id or old-style <a name>. It skips #top, which works without a target, and app-style addresses like #/page; if any name contains /, ? or =, it skips them all, since the site is probably using them as pages. Sections that JavaScript adds after loading aren't seen. A bare # is covered by links that go nowhere.
An id is used more than once
What it means. An id names one element so other things can find it: jump links (#pricing), form labels, scripts and styles. Each id should be used once per page; here, at least one is used several times.
Why it matters. Anything that looks up an id finds the first match and ignores the rest. A jump link lands on the wrong section, a label points at the wrong field and leaves the other without a name for screen readers, or a script works on only one copy of a widget.
How to fix it. The report lists each repeated id and how many times it appears; search "view page source" for id=" followed by the name. Common causes are a section duplicated in a page builder along with its custom ID, a form placed twice on one page, or a menu printed twice, once for computers and once for phones. Rename the extras or clear the ID from copies that don't need one. If a plugin or theme creates them, ask its developer.
How the check spots it: it counts every id attribute in the HTML of the page you checked and lists those used more than once, most repeated first. It's always "Nice to have". When there are three or more ids and all are unique, it records a pass.
Old HTML that browsers only keep for compatibility
What it means. Tags like <font>, <center> and <marquee>, and attributes like bgcolor, align and valign, style a page directly in the HTML. CSS (stylesheets) took over that job long ago, and the HTML standard lists them as obsolete. Comments written only for Internet Explorer (<!--[if IE]>) count here too.
Why it matters. Browsers still support most of them, so this isn't urgent. But they mark an old template or content pasted from an older site, and styling spread through the HTML is harder to change: a new font or colour in your stylesheet won't reach text that sets its own. Internet Explorer has been retired, so its comments are dead weight.
How to fix it. The report lists what appears, how often and from which line. If it comes from your theme, mention it to whoever maintains it; updating or replacing an old theme usually clears it. If it's in your own content, clear the formatting in your editor and restyle the text with its normal options.
How the check spots it: it looks for elements the HTML standard lists as obsolete (such as <font>, <center> and <frameset>), old styling attributes (such as bgcolor, valign, and align outside tables) and Internet Explorer comments. It ignores frameborder and scrolling, which come with most video and map embed codes. It's always "Nice to have".
Tables nested inside other tables
What it means. Before CSS (the code that styles a page) could arrange layouts, pages were built from tables, with tables inside tables for columns and boxes. This page has a table inside another, the usual sign of that kind of layout. It turns up in old templates and in content pasted from emails.
Why it matters. Table layouts have fixed columns, so they don't rearrange for a phone screen, and people scroll sideways or zoom out. Screen readers may read them as data tables, cell by cell. WCAG doesn't ban layout tables, but 1.4.10 Reflow (level AA) asks that content fits a narrow screen without scrolling in two directions.
How to fix it. Rebuild the section with your theme's columns or blocks, or with CSS layout (flexbox or grid). For pasted content, paste it again as plain text and format it in the editor. Real data, like a price list, belongs in a table; one table on its own is fine. If the whole site is built this way, the template is very old, and a new theme or a rebuild is usually the practical fix.
How the check spots it: It notes each <table> that starts while another is still open, with its line number in the HTML. It doesn't look at role="presentation", which marks a layout table for screen readers, because the phone problem remains either way. The report lists this under Code quality.