Website audit checks
The 127 checks Full Stack Audit runs on a website, across security, SEO, performance, DNS, email, accessibility and more: what each one looks for, why it matters and how to fix it.
Security
- Certificate supports CRL revocation
Reads the site's TLS certificate and looks for a CRL Distribution Points extension. If one is present it downloads the CRL, verifies it against the issuing certificate and looks for the certificate's serial number: listed means fail, not listed means pass. A certificate without a CRL URL gets a warning.
- Content Security Policy (CSP) headers
Fetches the homepage and reads the Content-Security-Policy header. No CSP at all fails. A policy sent only as Content-Security-Policy-Report-Only, or one containing unsafe-inline, unsafe-eval or a bare * source, gets a warning. An enforced policy without those passes.
- Cross-origin isolation headers (COOP / COEP / CORP)
Fetches the homepage and reads three response headers. Each counts as set only with a strong value: Cross-Origin-Opener-Policy of same-origin or same-origin-allow-popups, Cross-Origin-Embedder-Policy of require-corp or credentialless, and Cross-Origin-Resource-Policy of same-origin or same-site. All three set passes; any missing gives a warning.
- Directory listing disabled
Requests twelve common directory paths (/images/, /uploads/, /assets/, /files/, /static/, /backup/, /tmp/ and similar) and looks at the response body for an auto-generated index ("Index of /", "Parent Directory", "Directory listing for"). Any listing found fails; none found passes.
- Exposed sensitive files
Requests 24 well-known sensitive paths, including .env files, .git/config and .git/HEAD, .htpasswd, .aws/credentials, editor backups of config files, database.yml, SQL dumps, backup archives and dependency lockfiles. Any that answers 200 with a non-HTML content type fails (HTML responses are treated as a catch-all page, not the file). None exposed passes.
- External links security (noopener)
Parses each page for links that open in a new tab (target="_blank") and point to another host. Any such link without rel="noopener" or rel="noreferrer" fails; pages with none, or where every one has them, pass.
- HSTS headers with subdomains and preload
Fetches the homepage and reads Strict-Transport-Security. No header fails. A max-age under one year (31536000 seconds), or a header missing includeSubDomains or preload, gets a warning. A complete header is then looked up on hstspreload.org: preloaded passes, pending or not listed gets a warning.
- Iframe sandbox security
Parses each page for iframes. An iframe loading another host without a sandbox attribute fails. A sandbox that combines allow-same-origin with allow-scripts, or allows allow-top-navigation, gets a warning. Pages with no iframes, or only safely sandboxed ones, pass.
- No mixed content (HTTP on HTTPS)
On HTTPS pages, parses the HTML for resources referenced with an http:// URL. Scripts, stylesheets, iframes, object/embed and form actions over HTTP are active mixed content and fail. Images, audio and video over HTTP are passive mixed content and get a warning. No HTTP references passes; pages served over HTTP are skipped.
- No open redirect endpoints
Requests twelve common redirect endpoints (/redirect, /redir, /go, /out, /click, /login, /logout and the root) with parameters such as url, to, next and redirect pointing at an external test address. Any that answers with a 3xx redirect to that external host fails; none does, it passes.
- Permissions-Policy header
Fetches the homepage and reads Permissions-Policy (or the legacy Feature-Policy). A missing or empty header fails. It then looks at eleven sensitive features (geolocation, camera, microphone, payment, usb, bluetooth, display-capture, midi, magnetometer, gyroscope, accelerometer): any set to * or to external origins gets a warning, none of them restricted gets a warning, and at least one restricted to () or (self) with none left open passes.
- Referrer-Policy header
Fetches the homepage and reads Referrer-Policy. A missing header fails. unsafe-url, or any value that is not a standard policy, gets a warning. A header made only of recognised values (such as strict-origin-when-cross-origin, same-origin or no-referrer) passes.
- SSL/TLS configuration graded A or A+
Connects with every protocol from SSL 3.0 to TLS 1.3, enumerates the cipher suites the server accepts and inspects the certificate chain and HSTS header, then grades the configuration the way SSL Labs does. A or A+ passes, A- gets a warning, and B or below fails, with the specific issues listed (legacy protocols, weak or non-forward-secret ciphers, incomplete chain, small keys, missing TLS 1.3 or HSTS).
- Server / framework version disclosure
Fetches the homepage and looks for response headers that reveal the software stack: a Server header containing a version number, or any X-Powered-By, X-AspNet-Version, X-AspNetMvc-Version, X-Generator, X-Drupal-Cache or X-Rails-Version header. Any of them gets a warning; none passes.
- TLS v1.0 and v1.1 disabled (only TLS 1.2+ allowed)
Tries to complete a handshake with SSL 3.0, TLS 1.0 and TLS 1.1. If the server accepts any of them the check fails; if only TLS 1.2 and newer work, it passes. Sites not on HTTPS are skipped.
- Valid and secure SSL certificate
Opens a TLS connection to the site and reads the certificate. A handshake error, an expired certificate or one not yet valid fails. A certificate expiring in fewer than 30 days gets a warning; otherwise it passes. Sites not on HTTPS are skipped.
- X-Content-Type-Options header
Fetches the homepage and reads X-Content-Type-Options. A missing header fails, any value other than nosniff gets a warning, and nosniff passes.
- X-Frame-Options header
Fetches the homepage and reads X-Frame-Options. A missing header fails, a value other than DENY or SAMEORIGIN (including the obsolete ALLOW-FROM) gets a warning, and DENY or SAMEORIGIN passes.
Privacy
- Cookie consent banner
Uses an AI model to read the page's visible text and structure and decide whether it shows a cookie consent banner, in any language, and checks whether the page sets cookies. Passes when a banner is found or no cookies are set, warns only when cookies are set and the model is confident there is no banner, and skips when AI features are off for the project.
- Cookie security attributes
Fetches the site root and inspects every Set-Cookie header for the Secure, HttpOnly and SameSite attributes. Fails when a session-like cookie (its name contains session, sid, auth or token) lacks HttpOnly, or a cookie uses SameSite=None without Secure; warns on any other missing attribute; passes when every cookie has all three, or when no cookie is set.
Performance
- CDN usage for static content
Looks for a CDN in the page's response headers (cf-ray, x-amz-cf-id, x-served-by, x-vercel-id, x-netlify-cache, or a Server header naming Cloudflare, CloudFront, Akamai, Fastly or Varnish) and in the hostnames of its scripts, stylesheets and images; when neither shows one, it checks the headers of up to three external assets. Passes when any CDN is found and warns when none is.
- Critical CSS inlined
Looks at how the page delivers its CSS: inline styles in the head (good up to about 14 KB, better when they style above-the-fold elements such as body, header, nav or h1), stylesheets preloaded or loaded asynchronously, and how many stylesheets block rendering (more than 2 is an issue). Passes when at least one of those optimizations is in place and warns when none is.
- Font loading optimization
Detects web fonts loaded from third-party font services (Google Fonts, Adobe Typekit, Font Awesome, fonts.net), font files linked directly and @font-face rules in the page. Passes when the page uses no custom fonts, or when every @font-face declares font-display swap, optional or fallback and third-party font stylesheets come with a font preload; warns otherwise.
- Gzip/Brotli compression enabled
Requests the page announcing support for Brotli, gzip and deflate, and reads the Content-Encoding of the response. Passes when the HTML comes back as br, gzip or deflate, fails when it comes back uncompressed, and warns on any other encoding.
- HTML document size
Measures the size of the page's rendered HTML document, uncompressed. Passes up to 200 KB and warns above it (200 KB of raw HTML is roughly 50 KB on the wire once compressed).
- HTTP/2 or HTTP/3 enabled
Reads the HTTP version the server negotiates for the site. HTTP/2 and HTTP/3 pass; a server that only speaks HTTP/1.x fails.
- Image optimization and modern formats (WebP, AVIF)
Looks at the file extension of every image on the page. Passes when at least one image is WebP or AVIF or the page uses picture elements, warns when every recognizable image is JPEG, PNG or GIF, and is skipped on pages without images.
- JavaScript bundling and optimization
Reviews the page's script tags: external scripts that block rendering because they lack async, defer or type=module (more than 2 is an issue), more than 10 external scripts, and inline scripts over 5 KB count against it; all scripts non-blocking, 3 or fewer scripts, ES modules, minified or hashed filenames and signs of a bundler or code splitting count in its favor. Passes when the good practices outweigh the issues and warns when they do not.
- Keep-alive connections enabled
On servers that speak HTTP/1.x, fetches the page and reads the Connection response header. Fails when it is Connection: close and passes otherwise, since HTTP/1.1 keeps connections open by default; skipped on HTTP/2 and HTTP/3, where connections are always persistent.
- Proper caching headers for static assets
Sends a HEAD request to the first 10 stylesheets, scripts and images the page references and looks for a Cache-Control, Expires or ETag header on each. Passes when every asset has one, warns when some assets have none, and fails when none of them do.
- Static assets caching with versioning
Checks whether the page's scripts and stylesheets carry a cache-busting version in their URL (a content hash, a ?v= parameter, a timestamp or a version number) and reads the Cache-Control of up to 10 of them. At least half the assets versioned, a max-age of 30 days or more and the immutable directive count in its favor; fewer than half versioned or a max-age under one day count against it. It passes when the good practices outweigh the issues and warns otherwise.
- Time to First Byte (TTFB)
Times a request to the page and reports the time to first byte, split into DNS lookup, TCP connection, TLS handshake and server processing. Up to 200 ms passes, 201 to 1000 ms warns, and over 1000 ms fails.
- Total page requests and resources
Counts the requests the page's HTML triggers: scripts, stylesheets, images, font links, iframes, and video and audio sources. Passes up to 80 requests unless there are more than 15 scripts, more than 5 stylesheets or more than 50 images (then it warns), warns between 81 and 100 requests, and fails above 100.
Lighthouse
- Browser Cache Policy
Reads Lighthouse's cache policy audit, which lists the static resources (scripts, stylesheets, images, fonts) the page loads with a short or missing cache lifetime. Passes when the audit scores 100%, warns from 50%, and fails below 50%. It runs on the pages chosen for Lighthouse.
- Core Web Vitals (LCP, INP, CLS)
Grades Largest Contentful Paint, Interaction to Next Paint and Cumulative Layout Shift at the 75th percentile of real Chrome users on phones (Chrome UX Report), for the page or for the whole site when the page has too little traffic. Google's thresholds apply: LCP 2.5 s good and over 4 s poor, INP 200 ms and 500 ms, CLS 0.1 and 0.25; any poor metric fails and any metric needing improvement warns. Without real-user data it falls back to a Lighthouse lab estimate, with Total Blocking Time standing in for INP, which can warn but never fails.
- First Contentful Paint (FCP)
Reads First Contentful Paint, the moment the first text or image appears, from a Lighthouse mobile run on the page. Under 1.8 s passes, under 3 s warns, and 3 s or more fails. It runs on the pages chosen for Lighthouse.
- Lighthouse Accessibility Score
Reads the Accessibility category score from a Lighthouse run on the page, simulating a mid-range phone. A score of 90 or above passes, 50 to 89 warns, and below 50 fails. It runs on the pages chosen for Lighthouse.
- Lighthouse Best Practices Score
Reads the Best Practices category score from a Lighthouse run on the page, simulating a mid-range phone. A score of 90 or above passes, 50 to 89 warns, and below 50 fails. It runs on the pages chosen for Lighthouse.
- Lighthouse Performance Score
Reads the Performance category score from a Lighthouse run on the page, simulating a mid-range phone on a slow 4G connection. A score of 90 or above passes, 50 to 89 warns, and below 50 fails. It runs on the pages chosen for Lighthouse.
- Lighthouse SEO Score
Reads the SEO category score from a Lighthouse run on the page, simulating a mid-range phone. A score of 90 or above passes, 50 to 89 warns, and below 50 fails. It runs on the pages chosen for Lighthouse.
- Speed Index
Reads Speed Index, how quickly the visible part of the page fills in during load, from a Lighthouse mobile run on the page. Under 3.4 s passes, under 5.8 s warns, and 5.8 s or more fails. It runs on the pages chosen for Lighthouse.
- Unused JavaScript
Reads Lighthouse's unused JavaScript audit, which measures how much of each script the page downloads but never runs during load. Under 50 KB of unused code passes, 50 to 150 KB warns, and 150 KB or more fails. It runs on the pages chosen for Lighthouse.
SEO
- Alt attributes on all images
Looks at every <img> on the page and checks that it carries an alt attribute (an empty alt="" counts: it marks a decorative image on purpose). Passes when every image has one, warns when some are missing it and fails when none of them do. Pages without images are skipped.
- Article pages declare Article structured data
Applies only to pages that look like articles (an <article> element, og:type=article or Article-family JSON-LD). Passes when the page declares Article, BlogPosting, NewsArticle, TechArticle, ScholarlyArticle or Report in JSON-LD, and warns when it declares none of them.
- Breadcrumb navigation and structured data
Looks for breadcrumb navigation in the HTML (a nav labelled breadcrumb, a .breadcrumb or .breadcrumbs list, or BreadcrumbList microdata) and for a BreadcrumbList in JSON-LD. Passes when the JSON-LD is there; warns when a visible trail has no JSON-LD counterpart, or when the BreadcrumbList has no items or items without a name. Pages with no breadcrumbs at all are skipped.
- Canonical / hreflang consistency
Runs on pages that have both hreflang tags and a canonical URL. Warns when the canonical URL is not one of the page's hreflang alternates (nor its x-default), or when it is the alternate of a different language than the page's own lang attribute. Passes otherwise.
- Canonical URL (presence and consistency)
Reads the page's <link rel="canonical">. Fails when there is none, when its href is empty or when it is not a valid URL. Warns when it is relative, when it points to another domain, or when it differs from the page's own URL only by protocol, www, a trailing slash or query parameters the page does not have. Passes otherwise.
- Content-Language consistency
Compares the language the home page declares in three places: the <html lang> attribute, a <meta http-equiv="Content-Language"> tag and the Content-Language HTTP header. Fails when none of them is set, warns when they name different languages, and passes when they agree.
- Crawl depth (3-click rule)
Builds the link graph between the audited pages and walks it from the home page. Warns when any audited page is more than 3 clicks away from the home page, or cannot be reached from it by following links at all. Passes when every page is within 3 clicks. Needs at least two audited pages, one of them the home page.
- Duplicate titles and meta descriptions across pages
Compares the titles and meta descriptions of all audited pages. Warns when two or more pages share the same title or the same meta description, and passes when every one is unique.
- External link attributes and equity
Looks at every link on the page that points to another domain and reports how many pass link equity versus carry rel="nofollow", "sponsored" or "ugc". It warns when any external link opens in a new tab (target="_blank") without rel="noopener" or rel="noreferrer", and passes otherwise.
- Homepage declares a site entity and WebSite schema
Reads the home page's JSON-LD. Passes when it declares both who runs the site, an Organization (or a subtype such as LocalBusiness, Corporation or OnlineStore) or a Person, and a WebSite entity. Warns when either of them is missing.
- Hreflang reciprocity and indexability
Follows the hreflang alternates between the audited pages. Warns when page A lists page B as an alternate but B does not list A back, or when an alternate is marked noindex. Passes otherwise. Alternates that were not among the audited pages are reported as unverified, not as failures.
- Hreflang tags (internationalization)
Reads the page's <link rel="alternate" hreflang> tags; a page without any passes (a single-language site does not need them). When present, it checks that every language code is a valid ISO code (en-GB, not en-UK), that every URL parses, that the page lists itself, and that an x-default is declared. Passes when all of that holds and warns otherwise.
- Image SEO optimization
Looks at the images in the page content (outside nav, header and footer) for four things: width and height attributes, a descriptive file name (not IMG_1234, DSC_, image1, screenshot, a UUID or a hash), a srcset or <picture>, and a loading attribute. Fails when any file name is non-descriptive or more than half the images lack dimensions, warns on smaller gaps and passes when every image has all four.
- Internal linking structure
Counts the page's links to other pages on the same site. Fails when the page has no internal links at all, warns when some link targets are not valid URLs, and passes otherwise.
- Language declaration (lang attribute)
Reads the lang attribute of the home page's <html> element. Fails when it is missing or empty, warns when it is not a valid BCP 47 language tag (for example en-UK instead of en-GB), and passes when it is valid.
- Locale content is actually translated
Compares the visible text of pages that declare each other as different-language hreflang alternates. Any pair that is 90% or more similar is flagged as probably untranslated (warning); the check passes when every compared pair is clearly different, and is skipped when the audit found no cross-language pairs.
- Meta descriptions present
Reads the page's <meta name="description"> tag. It fails when the tag is missing or empty, warns when the description is shorter than 50 or longer than 160 characters, and passes when it is between 50 and 160 characters.
- Mobile content parity and usability
Looks for three mobile problems: a missing viewport meta tag, a viewport that blocks zooming (user-scalable=no or maximum-scale=1), and large content blocks (div, section, article, main or p with more than 20 words) hidden with inline display:none or visibility:hidden. One problem is a warning, two or more fail the check, and none passes.
- No duplicate meta tags
Looks for tags the page declares more than once: <title>, meta description, canonical, viewport, charset (or Content-Type), robots, and each Open Graph and Twitter Card property. Passes when there are none, warns when one kind of tag is duplicated and fails when two or more are.
- Orphan pages (no inbound internal links)
Builds the internal link graph across all pages crawled in the audit and lists the pages (other than the home page) that no other audited page links to. It warns when there is at least one orphan, passes when every page has an inbound internal link, and needs at least two crawled pages to run.
- Page title optimization
Reads the page's <title>. It fails when the title is missing, empty, duplicated (more than one <title> tag) or looks keyword-stuffed (the same word of three or more letters repeated more than twice). It warns when the title is under 20 or over 60 characters, contains characters like < > { } [ ], or is all uppercase, and passes otherwise.
- Pagination SEO implementation
Detects paginated pages through rel="next" / rel="prev" links, links containing page=, /page/ or /p/, or a pagination nav. When page links exist but neither rel="next" nor rel="prev" is declared, or a declared rel link has an empty href, it warns; otherwise it passes. Pages with no pagination or infinite scroll are skipped.
- Product pages declare Product structured data
On pages that look like product pages (og:type=product, Open Graph product tags or another product signal), checks the JSON-LD for a Product-type entity (such as Product or SoftwareApplication). It passes when one is declared and warns when it is missing; non-product pages are skipped.
- Proper heading hierarchy (H1-H6)
Reads the visible headings (H1 to H6) in document order. Flags a missing H1, more than one H1, empty headings, skipped levels (an H2 followed by an H4) and a first heading that is not an H1. Warns when the only problem is several H1s, fails on any other problem and passes when there is none.
- Render-blocking resources
Counts the resources in <head> that block the first render: stylesheets without a print or conditional media query, external scripts without async, defer or type="module", and inline scripts over 1,000 characters. None passes, one to three is a warning, and four or more fail the check.
- Robots directives (noindex, nofollow, and others)
Collects robots directives from <meta name="robots">, googlebot, bingbot and yandex meta tags, and from the X-Robots-Tag HTTP header. It warns when the page is noindex, or when it carries another restrictive directive (nofollow, noarchive, nosnippet, noimageindex or none); it passes when there are no directives or only non-restrictive ones.
- Sitemap URLs blocked by robots.txt
Takes the URLs listed in the XML sitemap and tests each one on the site's own host against the robots.txt rules that apply to Googlebot. It warns when any sitemap URL is disallowed, passes when none is (or when there is no robots.txt), and is skipped when there is no sitemap.
- Sitemap and page hreflang agreement
For every URL whose XML sitemap entry declares hreflang alternates (xhtml:link) and that was crawled in the audit, compares the sitemap's language/URL pairs with the hreflang links in the page's own <head>. It warns when the two sets differ for any URL, passes when they all match, and is skipped when the sitemap has no hreflang annotations.
- Sitemap lists only indexable, canonical URLs
Checks that the sitemap only lists URLs worth indexing. Sitemap URLs crawled in the audit are flagged when they are noindex or declare a canonical pointing elsewhere; up to 25 sitemap URLs the audit did not crawl are fetched directly and flagged when they redirect or return an error. Any finding is a warning; otherwise the check passes.
- Social link previews (Open Graph and Twitter Cards)
Checks the Open Graph tags that drive link previews. It fails when the page has no og: tags at all and warns when og:title, og:type, og:url or og:image is missing, og:title is outside 15 to 60 characters, og:description is outside 60 to 200 characters, or og:image is not an absolute URL that answers 200. When the project enables social validation it also requires a valid twitter:card with twitter:title, twitter:description and (for summary cards) twitter:image.
- Structured data (Schema.org) presence and validity
Looks for structured data in JSON-LD, Microdata or RDFa and validates the JSON-LD against the properties each Schema.org type requires (for example Article needs headline, author and datePublished; Organization needs name and url). It fails when there is no structured data or every JSON-LD block is invalid JSON, warns when some blocks do not parse or a type is missing required properties, and passes otherwise.
- Thin content detection
On article-style pages, counts the words of main body text, leaving out navigation, header and footer. It passes at 300 words or more and warns below that; pages that are not article-like are skipped.
- URL structure and SEO friendliness
Inspects the page's URL for SEO-unfriendly patterns: a path over 75 characters, uppercase letters, underscores, double slashes, more than 4 directory levels, legacy extensions (.php, .asp, .aspx, .jsp, .cgi, .cfm), more than 3 query parameters, or encoded spaces. One issue is a warning, two or more fail the check, and none passes.
- XML sitemap
Fetches /sitemap.xml and, if that is not found, the first Sitemap: URL in robots.txt. It passes when a valid XML sitemap or sitemap index is found, and fails when the sitemap has XML errors or the wrong root element, exceeds the protocol's size limits, or cannot be found in either place.
- robots.txt file
Fetches /robots.txt from the site root. It fails when the file does not answer 200, is empty, or lists a Sitemap: URL that is unreachable or not a valid sitemap; it warns when the file has no User-agent line; and it passes when the file is present, has User-agent rules and every listed sitemap is valid.
AI Visibility
- AI/LLM scraping restrictions
Reads robots.txt, the robots meta tags and the X-Robots-Tag header for rules aimed at AI crawlers: citation bots such as GPTBot, ChatGPT-User, OAI-SearchBot, PerplexityBot, ClaudeBot and Google-Extended, training bots such as CCBot, Bytespider and Applebot-Extended, Cloudflare's Content-Signal, and noai / noimageai directives. The verdict follows the project's AI policy: when it allows AI crawlers, the check passes with no blocking signals and warns when any are present; when it blocks them, it passes with a restriction in place and warns without one.
- Author attribution (E-E-A-T)
Applies only to pages that look like articles. Passes when the page names its author in any machine-readable way: an author in its JSON-LD, a <meta name="author"> tag, a rel="author" link or an itemprop="author" element. Warns when it has none of them.
- Content freshness (last-updated signal)
Applies only to pages that look like articles. Looks for a date the page was published or last updated: a dateModified or datePublished in JSON-LD, the article:modified_time or article:published_time meta tags, or a <time datetime> element. Passes when it finds one and warns when it finds none.
- Machine-readable pricing for AI agents
Fetches /pricing.md and /pricing.txt and checks whether either answers 200 with a non-empty body. When the project allows AI crawlers, it passes if one is published, warns if the homepage links to a pricing or plans page but no such file exists, and is skipped when there is no pricing page. When the project blocks AI crawlers, publishing the file is what warns.
- llms.txt file for LLM guidance
Fetches /llms.txt and /llms-full.txt from the site and checks that they answer 200 with a non-empty body. When the project allows AI crawlers, it passes if either file is published and warns if neither is; when the project blocks AI crawlers, it is the other way round. If a file could not be fetched at all, the result is inconclusive instead of a verdict.
Redirects
- HTTP to HTTPS redirects
Requests the http:// version of the site root. Passes when it answers with a 3xx redirect whose Location starts with https://, fails when it serves the page over plain HTTP (200) or redirects somewhere that is not HTTPS, and warns on any other status.
- No broken links (internal and external)
Collects the http and https links on the page (every internal link, and the first 25 distinct external links) and requests each one. Passes when all of them respond successfully, fails when any internal or external link returns a 4xx or 5xx status, and warns when internal links only redirect to another URL.
- Page URL resolves without redirecting
Requests each audited page URL without following redirects, retrying with GET when the server rejects HEAD. Passes on a 2xx response, fails when the URL redirects (3xx), returns 404 or 410, or returns a 5xx server error, and warns on any other status.
- Path preservation in redirects
Requests a made-up path (/test-audit-path-12345) on the http:// version of the host and on its www or non-www counterpart. For each one that redirects, it passes when the Location keeps the path and fails when the redirect drops it, typically by sending everything to the homepage; it skips when neither version redirects.
- WWW to non-WWW (or vice versa) redirects
Requests the same URL on the other host form: www when the site is on the bare domain, or the bare domain when it is on www. Passes when that host redirects to the audited host or does not serve the page, and warns when both versions answer 200, when the redirect points to another host, or when the other form cannot be reached.
DNS
- CAA records (certificate authority authorization)
Looks up the CAA records on the domain. Passes when at least one issue or issuewild record names the certificate authorities allowed to issue certificates for it, warns when there are none (so any CA may issue one), and fails when the domain does not exist.
- DNS configuration audit
Resolves the domain's A, AAAA, MX and NS records and reads the A-record TTL from the authoritative nameservers. Warns when there is no A record, no IPv6 (AAAA) record, fewer than two nameservers, or an A-record TTL under 60 seconds, and fails when the domain does not exist. MX records are reported for information only.
- DNS propagation and routing checks
Queries the domain's A records on the Google (8.8.8.8), Cloudflare (1.1.1.1) and Quad9 (9.9.9.9) public resolvers. Passes when all three return the same set of addresses, and warns when one of them fails to resolve the domain or they disagree.
- DNSSEC enabled
Asks Google Public DNS for the domain's DNSKEY records. Passes when DNSKEY records are published, and fails when there are none, when the domain does not exist, or when the resolver answers SERVFAIL, which usually means DNSSEC is broken (for example a DS record at the registrar that no longer matches the zone's keys).
- Domain expiry date monitoring
Reads the domain's registration expiry date from WHOIS, falling back to RDAP. Passes when it is 60 or more days away, warns between 30 and 59 days or when the date cannot be determined, and fails when the domain expires in under 30 days or has already expired.
- Nameserver response time
Sends three non-recursive A-record queries to each of the domain's authoritative nameservers and times them. Passes when every nameserver averages 200 ms or less, warns when one averages over 200 ms (and over 500 ms is reported as slow), and fails when a nameserver answers none of the queries or no nameservers are found.
- Private WHOIS registration
Looks up the domain's public registration data through WHOIS, falling back to RDAP, and searches it for privacy or redaction markers such as a privacy proxy, 'redacted for privacy' or WhoisGuard. Passes when the registrant's details are hidden, and warns when they appear to be public or the lookup fails.
- Subdomain security scan
Resolves a list of common subdomains (www, api, mail, ftp, admin, staging, dev, test, vpn, remote, portal, blog, shop and store) and sends an HTTPS request to each one that has an A record. Passes when every resolving subdomain answers, and warns when one still has a DNS record but no longer responds.
- WWW CNAME properly configured
Resolves the www counterpart of the audited host, or the bare domain when the site is served on www. Passes when it resolves through a CNAME or an A record, and warns when it has no record or does not exist.
- BIMI implementation
Skips domains without an MX record. Otherwise looks for a BIMI TXT record at default._bimi.<domain>: passes when the record names a Verified Mark Certificate (a= tag), warns when it only gives a logo (l=) without a certificate, and fails when there is no BIMI record.
- DKIM authentication
Probes about forty common DKIM selectors (google, selector1 and selector2 for Microsoft 365, k1 for Mailchimp, s1, s2, mandrill, zoho, protonmail and others) at <selector>._domainkey.<domain>, ignoring revoked keys with an empty p= value. Passes when a published key is found, or when none is found but DMARC is enforced with quarantine or reject; otherwise warns, since the domain may use a selector outside the list.
- DMARC policy
Reads the TXT record at _dmarc.<domain>. Passes when a single DMARC record sets p=quarantine or p=reject, warns when the policy is p=none (monitor only) or cannot be read, or when more than one DMARC record is published, and fails when there is no DMARC record.
- MTA-STS and TLS-RPT (email transport security)
Skips domains without an MX record. Otherwise looks for the _mta-sts TXT record, the policy file at https://mta-sts.<domain>/.well-known/mta-sts.txt and the _smtp._tls TLS-RPT record. Passes when all three are present, warns when only some are, fails when none are, and reports inconclusive when the TXT record exists but the policy host cannot be reached.
- SPF records configured
Reads the TXT records on the domain. Passes when exactly one starts with v=spf1, warns when several SPF records are published (receivers then treat SPF as a permanent error), and fails when there is none or the domain does not exist.
Mobile
- Favicons and app icons (all platforms)
Looks at the homepage for every kind of site icon: link rel="icon" favicons, /favicon.ico, an apple-touch-icon, the msapplication-TileImage meta tag, and the icons in the web app manifest, including the 192x192 and 512x512 sizes PWAs need. No icon at all fails; a favicon plus an Apple touch icon or both PWA sizes passes; anything less is a warning.
- Mobile-responsive design
Looks for signs of a responsive layout: a viewport meta tag, CSS media queries based on width, a responsive framework (Bootstrap, Tailwind, Foundation, Bulma), responsive images and flexbox or grid layouts. It flags a missing viewport tag and fixed widths of 1000px or more. Two or more signs with no issues pass; signs with issues or only one sign is a warning; no sign at all fails.
- PWA service worker
Looks for a service worker on the site: registration code in the homepage HTML (navigator.serviceWorker.register, Workbox, a sw.js reference), a file at /sw.js, /service-worker.js or /serviceworker.js, or a Service-Worker-Allowed header. Any of these passes; none is a warning.
- Viewport meta tag
Reads the viewport meta tag on the homepage. It passes when the tag sets both width= and initial-scale=, warns when one of them is missing, and fails when the tag is missing or empty.
- Web App Manifest (manifest.json)
Looks for a link rel="manifest" on the homepage, fetches the file and parses it. A missing link, a manifest that does not answer 200, or one that is not valid JSON fails; a manifest without name or icons is a warning; a valid manifest with both passes.
Accessibility & HTML
- ARIA attributes validation
Scans the rendered page for ARIA mistakes: aria-labelledby, aria-describedby and aria-controls pointing at IDs that do not exist, empty aria-label values, focusable elements inside an aria-hidden container, role values that are not valid WAI-ARIA roles, and dialogs without an accessible name. A page with no ARIA at all, or with ARIA and none of these mistakes, passes; any mistake is a warning.
- Color contrast ratios
Reads Lighthouse's color-contrast audit for the page and lists every text element whose contrast ratio is below the WCAG AA minimum (4.5:1 for normal text, 3:1 for large text). The check passes when no element falls short, fails when Lighthouse scores the audit 0, and warns when only some elements fall short. It runs on the pages the project measures with Lighthouse.
- Critical resource hints (preload, prefetch)
Collects the page's resource hints (preload, prefetch, preconnect, dns-prefetch, modulepreload). It flags preloads without an as attribute, and more than 2 third-party origins serving scripts or stylesheets without a preconnect. It passes with hints and no issues, warns when hints come with issues, and warns when there are no hints at all.
- Custom 404 error page
Requests a URL that cannot exist on the site and inspects the answer. It passes when the server responds with a 404 status and a real page (a title or substantial content, not the bare nginx or Apache default); it warns when the error page is a generic server page, or when the missing URL does not answer 404 at all (a soft 404).
- Form accessibility
Inspects every form on the page. Each visible input, select and textarea needs a label (a label for=, a wrapping label, aria-label, aria-labelledby or title; a placeholder alone does not count), groups of two or more radios or checkboxes need a fieldset with a legend, and email and password fields need an autocomplete attribute. More than 30% of fields without a label fails; any other issue is a warning; pages without forms pass.
- HTML best practices
Looks for buttons and links (with an href) that have no accessible name, more than 5 inline event handlers (onclick, onmouseover, onchange and the like), and a missing charset declaration. Any of these is a warning; a page with none passes.
- Keyboard navigation support
Checks the page for keyboard support: a skip link to the main content, no elements with a positive tabindex, no :focus rule that removes the outline without a box-shadow, border or background replacement, no buttons without accessible text, and no focus traps in Lighthouse's audit. A removed focus outline or a focus trap fails; any other issue is a warning.
- Lazy loading implementation
Counts the images and iframes on the page (ignoring those in the header, nav and footer) and how many load lazily with loading="lazy" or a data-src style JavaScript loader. It passes when there is nothing to lazy load, or when native lazy loading is used and covers at least half of the media; partial coverage, or JavaScript-only lazy loading, is a warning, and so is none at all.
- Modern image elements (picture, srcset)
Looks at how the page serves images: srcset and sizes, picture elements, WebP or AVIF files, and explicit width and height. On pages with more than 3 images it flags responsive images (srcset or picture) on fewer than 20% of them, and no WebP or AVIF at all. It passes with at least two good practices and no issues, and warns otherwise; pages without images pass.
- No browser console errors
Loads the page in headless Chrome and collects everything written to the browser console. Any uncaught JavaScript exception, or more than 3 console errors, is a failure; 1 to 3 errors, or any warnings, is a warning; a clean console passes.
- Semantic HTML usage
Checks the page structure for the four primary landmarks (main, header, nav, footer, or their ARIA roles), a single main element, duplicated landmarks of the same kind that are not told apart with aria-label, and interactive markup misuse: clickable div or span elements, links without an href, and href="#" or javascript: links. Missing 3 or more landmarks, or 10 or more misused elements, fails; any other issue is a warning.
- Skip links and visible focus indicators
Looks for a skip link (an in-page link labelled "skip to content" or similar) and for CSS that removes the focus outline (outline: none or 0 on :focus or *) without any :focus-visible rule to replace it. Either problem is a warning; a page with neither passes.
- Valid HTML markup
Runs the rendered HTML through the Nu HTML Checker (the W3C validator), ignoring a few known false positives for modern CSS such as @container, @property and color(display-p3 ...) colors. No errors or warnings passes; warnings only, or 1 to 2 errors, is a warning; 3 or more errors fails.
Analytics
- Application Performance Monitoring (APM)
Looks at the rendered homepage for a real-user performance monitoring (RUM/APM) script: New Relic, Datadog RUM, Dynatrace, AppDynamics, Elastic APM, Splunk RUM, SpeedCurve, Pingdom RUM, Raygun RUM or Sentry Performance. It passes when one of them, or the web-vitals library, is found; it warns when the page only calls the browser Performance API by hand, or when nothing is found.
- Error reporting tool present
Looks at the rendered homepage for a JavaScript error-reporting service: Sentry, Bugsnag, Rollbar, Airbrake, LogRocket, TrackJS, Raygun, Honeybadger, AppSignal or New Relic Browser. It passes when one is found; it warns when the page only has its own window.onerror or error-boundary handling, or when there is no error reporting at all.
- Web analytics installed
Looks at the rendered homepage for a known web analytics service: Google Analytics or Tag Manager, Plausible, Fathom, Matomo, Mixpanel, Segment, Amplitude, Heap, Chartbeat, comScore, Adobe Analytics, Piano, Permutive, PostHog, Simple Analytics, Clicky or Umami. It passes when one is found, and notes whether a consent manager is present; it warns when none is detected.
Extras
- App and cross-platform integration
Looks for signs that the site connects to a mobile app: an Apple Smart App Banner, al:ios / al:android App Links meta tags, App Store or Google Play links, an apple-app-site-association or assetlinks.json file under /.well-known/, and deep links. It passes when any are found, and reports three or more as strong integration; a site with none is skipped, since not every site has an app.
- CORS configuration
Requests the page with an Origin header from an unrelated site (https://example.com) and reads the Access-Control-* headers that come back. No CORS headers passes, as it should for an ordinary website. It fails when Access-Control-Allow-Origin is * together with Access-Control-Allow-Credentials: true, and warns when the server echoes back whatever origin it is sent.
- Dark mode implementation based on system setting
Looks at the rendered page for dark mode support: a prefers-color-scheme media query in an inline style block, or a color-scheme meta tag that includes dark. It passes when either is found; it warns when the page only has dark-mode classes or a data-theme attribute, with no way to follow the visitor's system setting; a page with no dark mode at all is skipped.
- Social media presence and verification
Looks at the page's links for profiles on social platforms (Facebook, X/Twitter, Instagram, LinkedIn, YouTube, TikTok, GitHub, Mastodon, Bluesky and others), and also notes rel="me" links, sharing widgets and platform verification meta tags. It passes when at least one profile link or verification tag is found, reporting three or more platforms as a strong presence, and warns when there are none.
- Third-party scripts audit
Lists the scripts the page loads from other domains and sorts them into analytics, advertising, social, CDN and other. It warns when more than three of them load without async or defer (render-blocking) or when there are more than ten in total, and passes otherwise.