76 checks · every weight public
Every check we run
Each one explains what it looks for, why it matters, and how to fix it. Nothing here is a secret formula: the score is these checks and their weights, added up.
No check matches that.
SEO
45 checks · 35% of the total
Title, description, canonical, headings, structured data, the sitemap, the robots rules that quietly remove a page from search, and whether AI answer engines may quote it.
Page is not blocked by a robots meta tag
A noindex directive removes the page from search entirely. It is the most damaging single line on a page, and it is nearly always left behind by accident after a staging deploy.
Fix Remove noindex from <meta name="robots">, or narrow it to the pages that genuinely should stay out of the index.
Page has a title
The <title> element is the headline Google shows in search results and the label the browser puts on the tab. A page without one is listed under a machine-picked fallback that nobody chose.
Fix Add a unique <title> inside <head> describing this page specifically, not the whole site.
No noindex in the X-Robots-Tag header
X-Robots-Tag does the same job as the robots meta tag but travels in the HTTP response, so it is invisible in the page source. A page can look perfectly indexable while the server quietly forbids it.
Fix Remove the noindex directive from the X-Robots-Tag header in the web server or application config.
Page has a meta description
The meta description is the snippet under the title in search results. Without one Google assembles a snippet from whatever text it finds, which is often a navigation menu or a cookie banner.
Fix Add <meta name="description" content="..."> summarising what the visitor gets on this page.
No insecure subresources
An https page that pulls scripts, styles, or images over http has those requests blocked or downgraded by the browser. Layout and behaviour break with no server-side error to show for it.
Fix Serve every subresource over https, or let the asset pipeline emit protocol-relative paths.
robots.txt lets Google crawl the page
A Disallow rule in robots.txt stops Googlebot from fetching the page at all, so everything else on this report stops mattering: the page can only appear in results as a bare URL with no description, or not at all.
Fix Remove or narrow the Disallow rule that matches this path, or add a longer Allow rule for it.
Page has exactly one H1
The H1 states what the page is about. Zero leaves the topic unstated; several compete with each other and make the page read as a list of unrelated sections.
Fix Keep one H1 for the page subject and demote the rest to H2.
Page declares a canonical URL
Without a canonical, every URL variant that reaches this page (tracking parameters, trailing slash, uppercase path) can be indexed as a separate page, splitting the ranking signal between duplicates.
Fix Add <link rel="canonical" href="..."> pointing at the absolute preferred URL of this page.
Images have alt text
Alt text is how a screen reader describes an image and how a search engine understands it. Missing alt attributes are an accessibility defect and lost image-search traffic at the same time.
Fix Give every content image a short alt describing what it shows.
Title length is in range
Below about 30 characters the strongest on-page signal is left half unused; past about 60 Google truncates the title in the results page and the tail is wasted.
Fix Rewrite the title to roughly 30-60 characters, front-loading the term the page should rank for.
Canonical points at this page
A canonical aimed somewhere else tells Google to index that other URL instead of this one. Done by accident, usually by copying a template, it removes the page from search while everything still looks fine on screen.
Fix Point the canonical at this page's own absolute URL, unless it is genuinely a duplicate of another page.
Document declares its language
The lang attribute tells screen readers which pronunciation rules to use and helps search engines serve the page to the right audience. Without it a screen reader may read English text with the wrong phonetics.
Fix Set the language on the root element, for example <html lang="en">.
Meta description length is in range
Descriptions shorter than about 120 characters leave the snippet half empty; past about 160 the end is cut off mid-sentence.
Fix Aim for 120-160 characters and put the reason to click in the first half.
AI crawlers may read the page
ChatGPT search, Perplexity, Claude and Google's AI features quote pages their crawlers are allowed to read. A robots.txt rule that blocks GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot or Google-Extended keeps the page out of those answers. Blocking the training crawlers can be a deliberate choice; blocking all of them is rarely intended.
Fix Remove the Disallow rules for the AI crawlers you want to be quoted by, or add a group for them that allows this path.
AI crawlers are not refused at the edge
robots.txt can welcome AI crawlers while the CDN or firewall in front of the site turns them away. Cloudflare has blocked AI crawlers by default on new sites since July 2025, and many security plugins do the same. We request the page as GPTBot, OAI-SearchBot, ClaudeBot and PerplexityBot and look for refusals and challenge pages. Our requests do not come from those companies' addresses, so a rule that checks the address as well may treat the real crawlers differently.
Fix In Cloudflare, open Security, then Bots, and review "Block AI bots" and "AI Crawl Control": allow at least OAI-SearchBot and PerplexityBot, which fetch pages to cite them.
One address serves the site
The same page reachable at http and https, or with and without www, is several copies of the site as far as search engines are concerned. They pick one, split links and signals between the copies, and sometimes pick the wrong one. The other addresses should redirect permanently to the one you use.
Fix Redirect http to https and the www or bare variant to your preferred host with a 301, in the server or CDN.
Content is in the HTML, not only in JavaScript
GPTBot, ClaudeBot and PerplexityBot download a page's JavaScript but do not run it, so a page that builds its text in the browser is blank to them. Google does run JavaScript, but later and not always. This counts the words in the HTML as it arrives, before any script runs.
Fix Render the page's main content on the server: server-side rendering or static generation in Next.js, Nuxt, SvelteKit or Angular, or prerendering for a single-page app.
Heading levels are not skipped
Screen readers and crawlers read headings as an outline. Jumping from H2 straight to H4 implies a missing level, so the outline reports a section that does not exist.
Fix Step heading levels one at a time.
Page links to the rest of the site
Internal links are how crawlers reach the rest of the site and how ranking signal flows between pages. A page with none is a dead end that hoards whatever authority it earns.
Fix Link to related pages in the body copy, not only from the shared navigation.
Structured data parses as JSON
A JSON-LD block with a trailing comma or an unescaped quote is discarded whole. The rich result it was meant to earn silently never appears, and nothing in the page looks broken.
Fix Fix the JSON syntax in the failing block, then confirm it in Google's Rich Results Test.
Local business markup is complete
For a business people visit or call, LocalBusiness markup with a name, an address and a phone number is how Google connects the page to its map listing and local results. A page that lists a phone number but does not say whose it is leaves that to guesswork.
Fix Add a LocalBusiness JSON-LD block (or the closest subtype, such as Dentist or Plumber) with name, address as a PostalAddress, and telephone, matching your Google Business Profile exactly.
Open Graph tags are present
Open Graph decides what a link to this page looks like when someone shares it on Slack, WhatsApp, LinkedIn, or Facebook. Missing tags turn a share into a bare URL with no image, which measurably costs clicks.
Fix Add og:title, og:description, and og:image meta tags, with the image at least 1200x630.
Structured data has what rich results require
Markup only earns stars, prices, FAQs or breadcrumbs in search results when it carries the properties Google requires for that type: an Article needs a headline, an image and a publication date; a Product needs an offer with a price and currency, a review or a rating. Markup that is present but incomplete earns nothing.
Fix Add the missing properties listed, then confirm the page in Google's Rich Results Test.
hreflang alternates are absolute URLs
Google requires fully qualified URLs in hreflang annotations. Relative hrefs are ignored without any error, so the language targeting simply never happens and nobody notices.
Fix Replace relative hreflang hrefs with absolute ones, including scheme and host.
The page's main topic is in its title and H1
The words a page repeats most are what it is about, as far as a reader skimming it can tell. When those words do not appear in the title and the main heading, the page promises one thing in search results and talks about another, and it ranks for neither.
Fix Put the page's main topic, in the words your visitors use, into the title tag and the H1.
Page does not redirect with a meta refresh
A <meta http-equiv="refresh"> that sends visitors elsewhere is a redirect search engines treat as weaker and slower than a real one, and screen readers and the back button handle it badly. A refresh that only reloads the page throws away whatever the visitor was reading.
Fix Replace the meta refresh with a 301 redirect on the server.
Phone number matches the markup
Local search trusts a business whose name, address and phone agree everywhere they appear. A telephone in the markup that differs from the number the page asks people to call is a contradiction Google has to resolve, and it may resolve it against you.
Fix Use one phone number: the same in the LocalBusiness markup, in the page's tel: links and in your Google Business Profile.
Site has a reachable XML sitemap
A sitemap is the list of pages a site wants crawled. Without one Google finds pages only by following links, so new or thinly linked pages can take weeks to be discovered.
Fix Publish a sitemap at /sitemap.xml (most CMSs and frameworks generate one) and name it in robots.txt with a `Sitemap:` line.
Search and AI answers may quote the page
nosnippet, max-snippet and data-nosnippet tell Google how much of the page it may show. They also govern Google's AI Overviews and AI Mode: a page that forbids snippets is not quoted there either, and a very short max-snippet leaves it little to quote.
Fix Remove nosnippet and max-snippet:0 from the robots meta tag and the X-Robots-Tag header, raise a small max-snippet or set it to -1, and keep data-nosnippet for fragments such as prices or legal text, not the main content.
Missing pages answer 404
When an address that does not exist answers 200, search engines index error pages as real ones, and a broken link on another site looks like it works. Sending every missing page to the homepage is treated the same way: Google calls it a soft 404. We ask the site for a page that cannot exist and look at the answer.
Fix Make the server or CMS return status 404 (or 410 for pages removed on purpose) for unknown addresses, with a helpful error page in the body.
Page describes itself with structured data
schema.org markup tells search engines what the page is: an article, a product with a price, a business with an address. Without it Google infers that from the text, and rich results such as stars, prices and FAQs are not available at all. It is often sold as a way into AI answers; measured, it is not: a 2025 Ahrefs study of 1,885 pages found no effect on AI citations, and AI assistants fetching a page live do not read JSON-LD. Add it for Google's rich results.
Fix Add a JSON-LD block with the schema.org type that matches the page (Article, Product, LocalBusiness, FAQPage...) and check it in Google's Rich Results Test.
Title and H1 are related but not identical
The title is written for the search results page and the H1 for the visitor who already clicked. Making them byte-identical wastes a second chance to describe the page, while sharing no vocabulary at all suggests the two disagree about the topic.
Fix Keep the shared subject in both, but let the title carry the search phrasing and the H1 the human phrasing.
Page has enough text to rank
There is no minimum word count in Google's rules, but a page with only a sentence or two gives search engines and AI answers little to understand, quote or rank. Pages that rank for competitive searches usually explain the subject in a few hundred words at least.
Fix Say more about what the page offers: who it is for, what is included, answers to the questions buyers ask.
Article says when it was published and updated
Search engines and AI assistants favour recent, dated content: pages cited in AI answers are measurably fresher than the average Google result. An article with no machine-readable date looks undated, one last touched years ago looks stale, and a modified date earlier than the published one is a contradiction.
Fix Put datePublished and dateModified in the Article markup (or article:published_time and article:modified_time meta tags), keep them true, and update dateModified only when the content really changes.
Facts in the markup are also on the page
A phone number or price that exists only inside JSON-LD is invisible to visitors, and to the AI assistants that read pages live: tests of ChatGPT, Claude, Perplexity and Gemini found none of them reading JSON-LD when fetching a page. Google also expects markup to describe what the page shows.
Fix Show the phone number and price in the page text as well, in the same form as in the markup.
Address is shown on the page
An address that lives only in the markup is invisible to the visitor, and Google weighs what the page shows over what it declares. The street and postcode people can read are also what confirms the page belongs to the business on the map.
Fix Show the full address in the page itself, usually in the footer or on the contact section, written the same way as in the markup and your Google Business Profile.
Phone number can be tapped to call
Most local searches happen on a phone. A number written as plain text has to be copied or retyped; a tel: link calls it in one tap.
Fix Wrap the phone number in a link: <a href="tel:+15550100100">(555) 010-0100</a>.
Opening hours are in the markup
Hours are among the first things people check before they call or visit. Stated in the LocalBusiness markup, they can be read by search engines and assistants instead of guessed from the page's text.
Fix Add openingHoursSpecification (or openingHours) to the LocalBusiness block, with the same hours as your Google Business Profile.
Title names the city
Local searches name a place ("dentist portland"), and the title is the strongest on-page signal of what a page is about. A business page whose title leaves out its city leaves that match to the pages that say it.
Fix Put the city from your address in the title, for example "Family Dentist in Portland | Smile Co".
The address reaches the page in at most one redirect
Every redirect is a round trip before the page even starts to load, and every hop is a place where link value and crawl budget leak. A chain like http to https to www to a trailing slash usually means redirects were added one at a time and never merged.
Fix Point every old address straight at the final one, in a single 301, and update internal links to the final address so they need no redirect at all.
Twitter card type is declared
Without a card type, X renders a shared link as plain text even when Open Graph tags exist. It is one line of markup that decides whether the share carries an image.
Fix Add <meta name="twitter:card" content="summary_large_image"> alongside the Open Graph tags.
URL is short and readable
Long parameter-laden URLs get truncated in results, are harder to share, and multiply into near-duplicate variants of the same page.
Fix Shorten the path, use hyphens rather than underscores between words, and drop parameters that do not change the content.
Site publishes an llms.txt
llms.txt is a proposed plain-text summary of a site written for language models: what it is, and links to the pages that matter. It is a convention, not a standard, and no major AI engine has confirmed reading it, so it weighs almost nothing here. It is cheap to add and harmless.
Fix Publish /llms.txt: a # title, a one-line > summary, then sections of markdown links to your key pages.
Page links to the business on Google Maps
A link to the Maps listing, or an embedded map, gives visitors directions in one tap and ties the page to the listing its reviews and hours live on.
Fix Link to your Google Maps listing (Share, then Copy link, in Google Maps) near the address, or embed the map from Share, then Embed a map.
Page links to the business's social profiles
Links to the business's own profiles, or sameAs in its Organization markup, tie the site to the same entity elsewhere on the web. Search engines and AI assistants use those connections to be sure who the site belongs to, and visitors use them to check the business is real.
Fix Link your Instagram, LinkedIn, Facebook or YouTube profiles from the footer, and list them as sameAs in the Organization or LocalBusiness markup.
Best practices
14 checks · 15% of the total
Valid markup, HTTPS and its certificate, security headers, JavaScript errors in the console, and Lighthouse's own best practices audit.
Viewport meta tag is present
Without it mobile browsers render the page at desktop width and shrink it to fit, which fails Google's mobile usability bar and leaves text unreadable on a phone. Editor linters such as webhint (Edge Tools in VS Code) report the same problem as "A 'viewport' meta element was not specified."
Fix Add <meta name="viewport" content="width=device-width, initial-scale=1"> to <head>.
HTML parses without errors
Browsers repair broken markup silently, and each one repairs it slightly differently. Unclosed and misnested tags are where a layout that works in one browser collapses in another.
Fix Fix the reported positions: usually an unclosed tag, a stray closing tag, or an attribute missing its quote.
Element ids are unique
An id must appear once per document. Duplicates break label-to-input pairing, in-page anchors, and any script that looks an element up by id, and every one of those failures is silent.
Fix Rename the repeated ids so each is used once, or switch to a class where the value was never meant to be unique.
Page is served over valid HTTPS
Browsers mark plain http pages as not secure, and an expired certificate replaces the whole page with a warning that almost nobody clicks through. Certificates now last 90 days or less, so a renewal that silently stopped working takes a site down within weeks. Protocols older than TLS 1.2 are refused by current browsers.
Fix Serve the page over HTTPS with automatic renewal (Let's Encrypt through certbot, or a CDN that manages the certificate), and check the renewal job actually runs.
Character encoding is declared
With no declared charset the browser guesses, and the guess is wrong often enough that accented characters, quotes, and emoji turn into mojibake for some visitors and not others.
Fix Put <meta charset="utf-8"> first inside <head>, or send it in the Content-Type response header.
Lighthouse best practices score
Lighthouse's best practices audit covers what makes a page trustworthy to a browser: HTTPS everywhere, no deprecated APIs, no errors in the console, images shown at their real aspect ratio, a valid doctype and charset.
Fix Work through the failing audits listed.
Page uses the HTML5 doctype
A missing or legacy doctype drops the browser into quirks mode, where box sizing and layout follow rules from the 1990s. The page then differs from every modern stylesheet assumption.
Fix Make the very first line <!DOCTYPE html>.
Response sends the standard security headers
A handful of response headers switch on protections the browser already has: HSTS keeps visitors on HTTPS, a Content-Security-Policy limits where scripts may come from, nosniff stops files being run as the wrong type, frame protection stops the page being embedded to trick clicks, and Referrer-Policy and Permissions-Policy limit what leaks and what the page may ask for. Security scanners and clients' IT teams grade sites on exactly this list.
Fix Add the missing headers in the web server or CDN, for example Strict-Transport-Security: max-age=31536000, X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy: strict-origin-when-cross-origin.
No JavaScript errors in the console
An error logged while the page loads usually means a script stopped halfway: a menu that does not open, a form that does not submit, an analytics tag that records nothing. Visitors never see the message, only the broken behaviour.
Fix Open the browser console on the page and fix the first error listed; later ones are often caused by it.
No deprecated presentational tags
Tags like <center> and <font> were removed from the standard. Browsers still honour them today, but they mix styling into the markup and no longer respond to CSS the way the rest of the page does.
Fix Replace them with CSS: text-align, font-family, font-size, and their friends.
Page declares a favicon
The favicon is how a user finds this tab among twenty others and how the site appears in bookmarks and history. Without a declared one the browser requests /favicon.ico and usually gets a 404.
Fix Add <link rel="icon" href="/favicon.ico"> or an SVG equivalent to <head>.
Styling lives in stylesheets, not attributes
Inline style attributes cannot be cached, overridden by a stylesheet, or covered by a Content Security Policy that forbids inline styles. In quantity they also bloat every HTML response.
Fix Move repeated inline styles into CSS classes and keep style attributes for values genuinely computed at runtime.
Links that open a new tab use rel=noopener
A page opened with target="_blank" can reach back to the page that opened it and send it elsewhere. Current browsers assume noopener, but older ones and some in-app browsers do not, and saying it costs nothing.
Fix Add rel="noopener" (or noreferrer) to every link with target="_blank".
Email addresses are not left for spam bots
Bots crawl the web collecting every address written in a page or a mailto: link, and those mailboxes fill with spam within weeks. A contact form, or an address the page assembles with a little JavaScript, keeps the inbox usable.
Fix Replace the address with a contact form, or put it together in JavaScript so it is not in the HTML as one string.
Accessibility
5 checks · 15% of the total
Google's Lighthouse accessibility run (contrast, names, labels, landmarks) plus what we read in the HTML ourselves: form labels, link text, icon buttons, zoom.
Lighthouse accessibility score
Google runs the axe accessibility rules against the page as a phone renders it: colour contrast, names on controls and links, labels on form fields, alt text, headings and landmarks. The score is the weighted share of those rules the page passes. Accessibility is also law in more places each year, including the European Accessibility Act since June 2025.
Fix Start with the failing rules listed: they are ordered by how much each one weighs.
Buttons have accessible names
A button whose only content is an icon announces itself to a screen reader as just "button", which tells the user nothing about what pressing it will do.
Fix Give each icon-only control an aria-label, or put visually hidden text inside the button.
Form fields have labels
A screen reader announces a field by its label. Without one it says "edit text" and the user has to guess what goes in it. A placeholder is not a label: it disappears as soon as someone types, and many screen readers do not read it.
Fix Put a <label for="id"> next to each field, or wrap the field in its label.
Links have discernible text
A link that holds only an icon or an image without alt text is announced as "link" followed by its address, or nothing at all. Search engines read link text too: it is how they learn what the target page is about.
Fix Give icon links an aria-label ("Follow us on Instagram"), and give linked images an alt that says where the link goes.
Users can zoom the page
Disabling zoom locks out everyone who needs to magnify text. On modern browsers it buys nothing in return.
Fix Remove user-scalable=no and any maximum-scale below 2 from the viewport meta tag.
Links
2 checks · 10% of the total
Every link on the page is resolved, capped at fifty, so dead targets and downgrades to plain http surface before a visitor finds them.
Links resolve
A link that answers 404 or refuses the connection wastes the visit, and on an internal link it also wastes the crawl budget Google spends on the site.
Fix Repair or remove the dead targets.
No links to plain http
Sending a visitor from an https page to an http one drops them onto an unencrypted connection and usually raises a browser warning that reads as "this site is unsafe".
Fix Point the links at the https version of the target, which nearly every site now serves.
Speed
10 checks · 25% of the total
Core Web Vitals from Google's own Lighthouse run, plus the transport facts we measure ourselves: first byte, weight, compression, caching.
Lighthouse performance score
Google's own composite of loading, interactivity, and visual stability, measured on a simulated mid-range phone. This is the number PageSpeed Insights puts at the top of its report.
Fix Work the opportunities below in order: they are ranked by how much loading time each one gives back.
Real visitors get a fast page
The Chrome UX Report measures Core Web Vitals on real Chrome users over the last 28 days, and it is the data Google's ranking uses, not the lab run. It exists only for pages, or whole sites, with enough traffic; when only the site has data, that is what this shows.
Fix Fix whichever of LCP, INP and CLS is outside the good band below.
Largest Contentful Paint
LCP is the moment the biggest element in the viewport finishes rendering, which is when a visitor feels the page has arrived. Google's threshold for good is 2.5 seconds.
Fix Preload the hero image or font, serve images sized for their slot in a modern format, and remove render-blocking CSS.
Cumulative Layout Shift
CLS measures content jumping around after it appears. It is why a visitor taps the wrong button when an ad or a late-loading image pushes the page down under their finger.
Fix Reserve space with width and height attributes on images and iframes, and never insert content above what is already on screen.
Interaction to Next Paint
INP is how long the page takes to visibly respond after a tap or a click. Above 200ms the interface starts to feel like it is ignoring the user. INP replaced First Input Delay (FID) as a Core Web Vital in March 2024.
Fix Break up long JavaScript tasks, defer work that is not needed for the first interaction, and cut third-party scripts on the critical path.
HTML is compressed in transit
Gzip or Brotli typically cuts HTML to a quarter of its size. Serving it uncompressed wastes that bandwidth on every single request, and it is usually one line of server config.
Fix Enable gzip or brotli for text responses in nginx, the CDN, or the application server.
Time to first byte
How long the server took to start answering, measured on our own request. A slow first byte delays everything downstream no matter how well the front end is built.
Fix Cache the response, remove slow database queries from the request path, or move the origin closer to visitors with a CDN.
Response carries cache headers
Without a usable Cache-Control header, browsers and CDNs re-fetch the page on every visit, so returning visitors pay the full load cost again for content that did not change.
Fix Send Cache-Control with a max-age suited to how often the page really changes, even if that is only a few minutes.
HTML document weight
This is the HTML document alone, before images and scripts. A multi-megabyte document usually means the whole page was rendered server-side into one response, and every visitor pays for it on every load.
Fix Paginate long lists, move repeated markup into components, and stop inlining large data blobs into the document.
Images are not oversized
Images are usually most of a page's bytes. A photo straight from a camera or a design tool can weigh several megabytes when a few hundred kilobytes would look the same on screen, and every visitor on a phone downloads all of it. We ask each of the page's first ten images for its size, without downloading it.
Fix Export photos at the size they are shown, in WebP or AVIF, at quality 75-85.