47
Breaks without it
28
Expected in 2026
11
Context-dependent
4
Causes harm
Master Prompt
Copy this prompt, paste it into ChatGPT / Claude / Gemini with your site URL, and get a full audit with fixes.
You are auditing a website for compliance with modern web standards. I will give you a URL. For each item below, report PASS, FAIL (with the exact fix), or N/A.
My website URL: [PASTE YOUR URL]
My tech stack: [Next.js / Astro / plain HTML / etc.]
## Check these items:
### HTML Foundations
- [ ] <!doctype html> as first line
- [ ] <html lang="..."> with valid BCP 47 tag
- [ ] <meta charset="utf-8"> in first 1024 bytes
- [ ] <meta name="viewport" content="width=device-width, initial-scale=1">
- [ ] Unique, descriptive <title> under 60 chars
- [ ] <meta name="description"> under 160 chars, unique per page
- [ ] <link rel="canonical"> with absolute URL
- [ ] Favicon SVG + ICO fallback + apple-touch-icon
- [ ] <meta name="color-scheme" content="light dark">
- [ ] Open Graph tags: og:title, og:description, og:image (1200x630), og:url, og:type
### SEO
- [ ] /robots.txt exists and references sitemap
- [ ] /sitemap.xml exists and is valid XML
- [ ] URLs are lowercase, hyphenated, descriptive
- [ ] 404 page returns HTTP 404 (not 200)
- [ ] One h1 per page, heading hierarchy never skips levels
- [ ] JSON-LD structured data (Article, FAQPage, BreadcrumbList, etc.)
- [ ] Internal links use descriptive anchor text
### Accessibility
- [ ] Color contrast 4.5:1 for normal text, 3:1 for large text
- [ ] Every <img> has alt text (decorative images: alt="")
- [ ] Every form input has a <label>
- [ ] All interactive elements keyboard-accessible (Tab, Enter, Escape)
- [ ] Visible focus indicators on keyboard focus
- [ ] Skip link as first focusable element
- [ ] Semantic landmarks: <header>, <nav>, <main>, <footer>
- [ ] No links or buttons without accessible names
- [ ] @media (prefers-reduced-motion: reduce) disables animations
- [ ] Touch targets at least 24x24 CSS px
### Security Headers (check with: curl -sI [URL])
- [ ] Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
- [ ] Content-Security-Policy (not just Report-Only)
- [ ] X-Content-Type-Options: nosniff
- [ ] Referrer-Policy: strict-origin-when-cross-origin
- [ ] Permissions-Policy disabling unused APIs
- [ ] frame-ancestors 'none' or 'self' in CSP
### Agent Readiness
- [ ] /llms.txt exists with structured content index
- [ ] robots.txt has rules for GPTBot, ClaudeBot, PerplexityBot
- [ ] JSON-LD structured data on every content page
- [ ] Stable URLs (no breaking changes without 301 redirects)
### Performance
- [ ] LCP < 2.5s, INP < 200ms, CLS < 0.1
- [ ] Images in WebP/AVIF with width and height attributes
- [ ] loading="lazy" on off-screen images (NOT on LCP image)
- [ ] Cache-Control: immutable on hashed assets, no-cache on HTML
- [ ] Brotli compression on text responses
- [ ] Scripts use defer, async, or type="module" (no bare <script> in <head>)
- [ ] HTTP/2 or HTTP/3
### Privacy
- [ ] Privacy policy linked from footer
- [ ] Cookie consent before non-essential cookies (EU/UK)
- [ ] Global Privacy Control signal respected
- [ ] No unused third-party scripts
### Resilience
- [ ] Custom 404 page returning HTTP 404
- [ ] Custom 500 page returning HTTP 500
After the audit, provide:
1. A prioritized list of failures, grouped by severity
2. The exact code or config to fix each failure
3. Verification commands to confirm each fixQuick Verification
Run against your live site. Replace example.com with your domain.
# Security headers
curl -sI https://example.com | grep -iE "strict-transport|content-security|x-content-type|referrer-policy|permissions-policy"
# Soft 404 check (should return 404, not 200)
curl -sI https://example.com/this-page-does-not-exist | head -1
# robots.txt and llms.txt
curl -s https://example.com/robots.txt | head -10
curl -sI https://example.com/llms.txt | grep "200"
# HTTP version (should show HTTP/2 or HTTP/3)
curl -sI --http2 https://example.com | head -1
# Compression (should show br or gzip)
curl -sI -H "Accept-Encoding: br,gzip" https://example.com | grep -i content-encoding
# TLS version
echo | openssl s_client -connect example.com:443 2>/dev/null | grep "Protocol"
# DNS CAA records
dig CAA example.com +short
# Structured data validation
# https://search.google.com/test/rich-results?url=https://example.com
# Full security header audit
# https://securityheaders.com/?q=example.comFull Specification
9 categories. Click any item to see implementation details.
Foundations
Required<!doctype html>First line of every HTML document. Triggers standards mode.
First line of every HTML document. Triggers standards mode.
Without it, browsers enter quirks mode: box models break, CSS behaves differently, layouts fail silently.
Official specification ↗Required<html lang="...">BCP 47 language tag on the root element.
BCP 47 language tag on the root element.
Screen readers use this to pick the pronunciation engine. Google uses it for language detection. Translators use it to decide what to offer. Use lang="en", lang="fr", lang="ja", not lang="english".
Official specification ↗Required<meta charset="utf-8">Character encoding declaration, must appear in the first 1024 bytes.
Character encoding declaration, must appear in the first 1024 bytes.
Place immediately after <head>. Without it, non-ASCII characters (accents, CJK, emoji) may render as garbled text. UTF-8 covers every writing system.
Official specification ↗Required<meta name="viewport">Tells mobile browsers to use the device width.
Tells mobile browsers to use the device width.
content="width=device-width, initial-scale=1". Without this, phones render pages at 980px and zoom out. Never set maximum-scale=1 or user-scalable=no - that breaks pinch-to-zoom for low-vision users.
Official specification ↗Required<title>Exactly one non-empty title per page.
Exactly one non-empty title per page.
Format: "Page Name - Site Name" or "Page Name | Site Name". Keep under 60 characters. This shows in browser tabs, search results, social shares, and screen reader announcements. Each page needs a unique title.
Recommended<meta name="description">Page summary for search engine snippets.
Page summary for search engine snippets.
150-160 characters. Unique per page. Google may rewrite it, but a specific, accurate description gets rewritten less often. Include the primary keyword naturally.
Official specification ↗Recommendedrel="canonical"Declares the preferred URL for duplicate/similar pages.
Declares the preferred URL for duplicate/similar pages.
<link rel="canonical" href="https://example.com/page">. Use the full absolute URL. Points to the version you want indexed when the same content lives at multiple URLs (www vs non-www, HTTP vs HTTPS, trailing slash variants).
Official specification ↗RecommendedFaviconsSVG favicon + ICO fallback + apple-touch-icon + PWA icon.
SVG favicon + ICO fallback + apple-touch-icon + PWA icon.
<link rel="icon" href="/favicon.svg" type="image/svg+xml"> for modern browsers. /favicon.ico at 32x32 for legacy. <link rel="apple-touch-icon" href="/apple-touch-icon.png"> at 180x180. Maskable 512x512 PNG for PWA manifest.
Recommended<meta name="color-scheme">Prevents white flash before CSS loads for dark-mode users.
Prevents white flash before CSS loads for dark-mode users.
<meta name="color-scheme" content="light dark">. Tells the browser to pre-paint the page background in the user's preferred color scheme. Add matching theme-color meta tags for browser chrome tinting.
Official specification ↗RecommendedOpen Graph tagsControls how pages appear when shared on social platforms.
Controls how pages appear when shared on social platforms.
og:title, og:description, og:image (1200x630px), og:url, og:type. Test with the Facebook Sharing Debugger and Twitter Card Validator. Without og:image, social shares look plain and get lower engagement.
Official specification ↗OptionalFeed discoveryAnnounce RSS/Atom/JSON feeds in <head>.
Announce RSS/Atom/JSON feeds in <head>.
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml">. Feed readers and agents auto-discover this. If you publish content regularly, ship a feed.
OptionalPopover APINative browser primitive for modals, tooltips, menus.
Native browser primitive for modals, tooltips, menus.
<div popover>Content</div> <button popovertarget="my-popover">Open</button>. Browser handles opening, closing, light-dismiss, focus trapping, and Escape key. Replaces hundreds of lines of modal JavaScript.
Official specification ↗SEO
Requiredrobots.txtPlain-text file at domain root controlling crawler access.
Plain-text file at domain root controlling crawler access.
User-agent: *\nAllow: /\nSitemap: https://example.com/sitemap.xml. Place at /robots.txt. Standardized in RFC 9309. Disallow blocks crawling but not indexing - use noindex for that.
Official specification ↗RecommendedXML sitemapLists canonical URLs with lastmod dates.
Lists canonical URLs with lastmod dates.
Max 50,000 URLs per file, max 50MB uncompressed. Reference it from robots.txt. Submit to Google Search Console. Most frameworks generate this automatically - verify the output is correct.
Official specification ↗RequiredURL structureLowercase, hyphenated, descriptive, shallow.
Lowercase, hyphenated, descriptive, shallow.
Good: /web3-101/smart-contracts. Bad: /page?id=47&cat=3. URLs are permanent public contracts. Changing them breaks bookmarks, citations, and agent caches. Plan them before launch.
RequiredRedirects301/308 for permanent moves, 302/307 for temporary.
301/308 for permanent moves, 302/307 for temporary.
A 301 tells search engines to transfer all ranking signals to the new URL. A 302 keeps them on the old URL. Never chain more than 2 redirects. Audit old redirects annually.
AvoidSoft 404sPages returning 200 OK for "not found" content.
Pages returning 200 OK for "not found" content.
Google Search Console reports these as errors. They dilute crawl budget and site quality. Your 404 page must return HTTP 404. Test: curl -sI https://yoursite.com/nonexistent | head -1 should show 404.
RequiredMeta robotsIndexing policy per page.
Indexing policy per page.
Public pages: default index,follow (no tag needed). Staging, admin, thin content: <meta name="robots" content="noindex,nofollow">. HTTP alternative: X-Robots-Tag: noindex header.
RequiredHeading hierarchyOne h1 per page, nested h2/h3/h4 outline.
One h1 per page, nested h2/h3/h4 outline.
h1 = page title. h2 = major sections. h3 = subsections. Never skip levels (h1 then h3). Never use headings for visual styling - use CSS classes instead.
RequiredInternal linkingLinks between your own pages.
Links between your own pages.
The strongest ranking signal you control. Every important page should be reachable within 3 clicks from the homepage. Use descriptive anchor text, not "click here".
RecommendedJSON-LD structured dataMachine-readable page annotations using schema.org.
Machine-readable page annotations using schema.org.
<script type="application/ld+json">{...}</script>. Use Article for blog posts, FAQPage for FAQs, Product for products, BreadcrumbList for navigation, Person/Organization for identity. Validate at search.google.com/test/rich-results.
Official specification ↗RecommendedBreadcrumbsNavigation trail showing page position in site hierarchy.
Navigation trail showing page position in site hierarchy.
Visible in UI + marked up as BreadcrumbList JSON-LD. Google displays them in search results as clickable path segments. One of the highest-ROI structured data types.
OptionalIndexNowPush protocol for instant re-crawl notification.
Push protocol for instant re-crawl notification.
One POST request to api.indexnow.org when a URL changes. Bing, Yandex, Naver, Seznam support it. Google ignores it (they rely on their own crawling). Free, no registration.
Official specification ↗Accessibility
RequiredColor contrast4.5:1 ratio for normal text, 3:1 for large text (18px bold / 24px regular).
4.5:1 ratio for normal text, 3:1 for large text (18px bold / 24px regular).
Check with Chrome DevTools (inspect element > contrast ratio) or webaim.org/resources/contrastchecker. Common failure: light gray text on white backgrounds. WCAG AA is the minimum; AAA requires 7:1.
Official specification ↗RequiredImage alt textEvery <img> gets an alt attribute.
Every <img> gets an alt attribute.
Descriptive images: describe what you see ("Bar chart showing 40% growth in Q3"). Decorative images: alt="" (empty, not missing). Functional images (icons in buttons): describe the action ("Close", "Search").
Official specification ↗RequiredForm labelsEvery input has a programmatically associated <label>.
Every input has a programmatically associated <label>.
<label for="email">Email</label> <input id="email" type="email">. Or wrap: <label>Email <input type="email"></label>. Placeholders disappear on input and are not labels. Screen readers announce the label when the field gets focus.
RequiredKeyboard navigationAll interactive elements operable via keyboard alone.
All interactive elements operable via keyboard alone.
Test: unplug your mouse, use Tab/Shift+Tab to move, Enter to activate, Escape to close. Focus must follow a logical order. No focus traps (except modals, which trap intentionally and release on close).
Official specification ↗RequiredFocus indicatorsVisible outline on keyboard-focused elements.
Visible outline on keyboard-focused elements.
Never use *:focus { outline: none } without a replacement. Safe default: :focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }. :focus-visible fires only on keyboard focus, not mouse clicks.
Official specification ↗RequiredSkip link"Skip to main content" as the first focusable element.
"Skip to main content" as the first focusable element.
<a href="#main" class="sr-only focus:not-sr-only">Skip to main content</a> before the <header>. Visually hidden until Tab is pressed. Saves keyboard users from tabbing through 30 nav links on every page.
RequiredSemantic landmarksUse <header>, <nav>, <main>, <footer>, <aside>.
Use <header>, <nav>, <main>, <footer>, <aside>.
One <main> per page. Screen readers let users jump between landmarks (Rotor on VoiceOver, Landmarks on NVDA). <div> has no semantic meaning - it tells assistive tech nothing.
RecommendedARIA usageUse native HTML elements before reaching for ARIA.
Use native HTML elements before reaching for ARIA.
<button> beats <div role="button" tabindex="0" onclick="...">. The native element gives you keyboard support, focus management, and click events for free. ARIA only when no native element fits (custom widgets, live regions).
Official specification ↗RequiredLink textEvery link describes its destination.
Every link describes its destination.
Good: "Read the WCAG 2.2 color contrast guidelines". Bad: "Click here". Screen reader users navigate by pulling up a list of all links on the page - "click here" x20 is useless.
AvoidEmpty controlsLinks and buttons with no accessible name.
Links and buttons with no accessible name.
Icon-only buttons need aria-label="Close" or visually hidden text inside: <button><svg .../><span class="sr-only">Close</span></button>. Without it, screen readers announce "button" with no context.
RequiredForm error handlingErrors announced to assistive tech with field association.
Errors announced to assistive tech with field association.
On submit failure: move focus to the first error, associate error text with the field via aria-describedby, announce with role="alert" or aria-live="polite". Color alone is not enough - use text + icon.
RequiredReduced motionRespect prefers-reduced-motion OS setting.
Respect prefers-reduced-motion OS setting.
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } } Parallax, autoplay video, and decorative animation can trigger vestibular distress.
Official specification ↗AvoidAccessibility overlaysThird-party widgets claiming one-line WCAG compliance.
Third-party widgets claiming one-line WCAG compliance.
AccessiBe, UserWay, AudioEye. They break screen readers, add legal liability, and the National Federation of the Blind has publicly opposed them. The only fix is fixing the actual code.
Official specification ↗RequiredCaptionsSynchronized captions on video, transcripts on audio.
Synchronized captions on video, transcripts on audio.
YouTube auto-captions have 10-15% error rates. WCAG requires human-reviewed captions. Upload .vtt or .srt files. For audio-only content (podcasts), provide a text transcript.
Official specification ↗RequiredTouch targets24x24 CSS px minimum, 44x44 recommended.
24x24 CSS px minimum, 44x44 recommended.
WCAG 2.2 Level AA. Common failure: inline text links in dense paragraphs, close-together icon buttons in toolbars. Add padding to increase the tap area without changing visual size.
Official specification ↗RequiredNative elementsUse <button>, <a>, <details>, <dialog> over <div onClick>.
Use <button>, <a>, <details>, <dialog> over <div onClick>.
<button> gives you keyboard support (Enter + Space), focus management, and screen reader announcement for free. A <div> with a click handler gives you none of that without manual ARIA + tabindex + keydown handlers.
Security
RequiredHTTPS + TLSTLS 1.2+ on every page, HTTP redirected to HTTPS.
TLS 1.2+ on every page, HTTP redirected to HTTPS.
Free via Let's Encrypt. All major hosts (Vercel, Netlify, Cloudflare, Firebase) handle this automatically. Test: openssl s_client -connect example.com:443 should show TLSv1.2 or TLSv1.3.
Official specification ↗RequiredHSTSStrict-Transport-Security header.
Strict-Transport-Security header.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload. Once set with preload, browsers will never attempt HTTP for your domain. This is irreversible via hstspreload.org. Start with max-age=300 to test.
Official specification ↗RequiredCSPContent-Security-Policy header.
Content-Security-Policy header.
Start with Content-Security-Policy-Report-Only: default-src 'self' to find what breaks. Then enforce. A strict CSP blocks most XSS attacks. In Next.js: configure in next.config.mjs headers(). Common pain point: inline scripts need nonces.
Official specification ↗RequiredX-Content-Type-OptionsPrevent MIME type sniffing.
Prevent MIME type sniffing.
X-Content-Type-Options: nosniff. One header, one value, set-and-forget. Without it, browsers may interpret a .txt file as JavaScript.
RequiredFrame protectionPrevent clickjacking via iframe embedding.
Prevent clickjacking via iframe embedding.
CSP: frame-ancestors 'none' (no one embeds you) or frame-ancestors 'self' (only your own domain). Legacy fallback: X-Frame-Options: DENY.
RecommendedReferrer-PolicyControl URL info leaked on navigation.
Control URL info leaked on navigation.
Referrer-Policy: strict-origin-when-cross-origin. Sends origin (https://example.com) to external sites, full URL only to same-origin. Protects paths with tokens or sensitive query strings.
RecommendedPermissions-PolicyDisable unused browser APIs.
Disable unused browser APIs.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(). Prevents injected scripts from accessing these APIs. List only the permissions you actually use.
RecommendedSRISubresource Integrity hashes on external scripts/styles.
Subresource Integrity hashes on external scripts/styles.
<script src="https://cdn.example.com/lib.js" integrity="sha384-..." crossorigin="anonymous">. Browser refuses to execute if the file hash changes. Generate: openssl dgst -sha384 -binary file.js | openssl base64 -A
Official specification ↗Recommendedsecurity.txtVulnerability disclosure contact at /.well-known/security.txt.
Vulnerability disclosure contact at /.well-known/security.txt.
Contact: mailto:security@example.com\nExpires: 2027-01-01T00:00:00.000Z\nPreferred-Languages: en. Per RFC 9116. Security researchers check this before going public with a vulnerability.
Official specification ↗RequiredCookie hardeningSecure + HttpOnly + SameSite on every cookie.
Secure + HttpOnly + SameSite on every cookie.
Set-Cookie: __Host-session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/. The __Host- prefix requires Secure, no Domain, Path=/. HttpOnly prevents JavaScript access. SameSite prevents CSRF.
RecommendedDNS CAARestrict which CAs can issue certs for your domain.
Restrict which CAs can issue certs for your domain.
DNS record: example.com. CAA 0 issue "letsencrypt.org". Set via your DNS provider. Prevents a compromised CA from issuing unauthorized certificates for your domain.
Agent Readiness
Recommended/llms.txtMarkdown index of your most important pages for LLMs.
Markdown index of your most important pages for LLMs.
# Site Name\n> One-line description.\n\n## Section\n- [Page](URL): Description. Keep under 100 entries. Place at /llms.txt. Emerging convention via llmstxt.org, not a ratified standard. See our AI Discovery Standards page for full details.
Official specification ↗Optional/llms-full.txtFull Markdown content of key pages concatenated into one file.
Full Markdown content of key pages concatenated into one file.
Practical for sites under ~500 pages. Gives LLMs complete context in a single request. Costs bandwidth but eliminates multi-page crawling. Regenerate on every deploy.
RecommendedAI crawler rulesExplicit robots.txt rules per AI user-agent.
Explicit robots.txt rules per AI user-agent.
GPTBot (OpenAI), ClaudeBot (Anthropic), PerplexityBot, Google-Extended (Gemini training), CCBot (Common Crawl), Bytespider (ByteDance). Allow bots whose search products you want to appear in. Block training-only crawlers if you choose.
RequiredStable URLsURLs are permanent contracts. Breaking them breaks everything.
URLs are permanent contracts. Breaking them breaks everything.
When you must move a URL: set a 301 redirect and keep it running indefinitely. Agent caches, bookmarks, citations, and backlinks all reference the original URL. Cool URIs don't change (W3C, 1998).
RecommendedStructured data for agentsJSON-LD gives agents typed facts instead of guessing from prose.
JSON-LD gives agents typed facts instead of guessing from prose.
Same schema.org markup search engines use. Article, Person, Organization, FAQPage, HowTo, Product. Agents extract structured fields (author, date, price) directly. No scraping needed.
RecommendedMachine-readable formatsJSON, RSS, or Markdown endpoints alongside HTML.
JSON, RSS, or Markdown endpoints alongside HTML.
An /api/posts.json endpoint is more useful to an agent than scraping your blog listing page. Content negotiation (Accept: text/markdown) or .md URL suffixes both work.
OptionalMCP serverModel Context Protocol - queryable tools for agents over JSON-RPC.
Model Context Protocol - queryable tools for agents over JSON-RPC.
Publish a server card at /.well-known/mcp/server-card.json. Agents discover your tools and call them programmatically. Relevant when your content has structure worth filtering (product catalog, documentation, dataset).
Official specification ↗OptionalA2A agent cardsAgent-to-Agent protocol for autonomous agent delegation.
Agent-to-Agent protocol for autonomous agent delegation.
/.well-known/agent-card.json describes your agent's capabilities. Other agents find it and delegate tasks via JSON-RPC. Google-led specification. Relevant for sites that expose agentic behavior.
OptionalAgent SkillsScoped instruction files that teach AI agents how to use your site.
Scoped instruction files that teach AI agents how to use your site.
/.well-known/agent-skills/index.json lists SKILL.md files. Each Skill tells an agent when and how to query your content. Cloudflare-led proposal.
OptionalHTTP Link headersAdvertise resources in HTTP response headers.
Advertise resources in HTTP response headers.
Link: </llms.txt>; rel="alternate"; type="text/markdown", </sitemap.xml>; rel="sitemap". Agents that skip HTML parsing can still discover your machine-readable resources.
Performance
RequiredCore Web VitalsLCP < 2.5s, INP < 200ms, CLS < 0.1 at p75 of real users.
LCP < 2.5s, INP < 200ms, CLS < 0.1 at p75 of real users.
Measure at pagespeed.web.dev (lab) and Chrome UX Report (field data). Most sites fail on LCP (unoptimized hero images, render-blocking CSS) and CLS (images without width/height, late-loading web fonts).
Official specification ↗RequiredImage optimizationWebP/AVIF, responsive sizes, explicit dimensions.
WebP/AVIF, responsive sizes, explicit dimensions.
<img src="hero.webp" width="1200" height="630" alt="..." loading="eager"> for LCP image. Use <picture> with AVIF + WebP + JPEG fallback. Always set width and height to prevent layout shift.
RecommendedLazy loadingloading="lazy" on off-screen images and iframes.
loading="lazy" on off-screen images and iframes.
Never on the LCP element (hero image, first visible image). The browser delays fetching until the element is near the viewport. For the LCP image, use loading="eager" and add <link rel="preload">.
RecommendedResource hintspreload, preconnect, prefetch for critical resources.
preload, preconnect, prefetch for critical resources.
<link rel="preload" href="/hero.webp" as="image"> for LCP image. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> for third-party fonts. Over-preloading is worse than not preloading - limit to 2-3 resources.
RequiredCache-ControlLong cache for hashed assets, short/no-cache for HTML.
Long cache for hashed assets, short/no-cache for HTML.
Hashed files (app.a1b2c3.js): Cache-Control: public, max-age=31536000, immutable. HTML pages: Cache-Control: no-cache. Never cache HTML for a year - users will see stale content.
RequiredCompressionBrotli for text, gzip as fallback.
Brotli for text, gzip as fallback.
Content-Encoding: br for HTML, CSS, JS, JSON, SVG. Do not compress JPEG, PNG, WOFF2, or video (already compressed). Most CDNs and hosts handle this automatically - verify with: curl -sI -H "Accept-Encoding: br" https://example.com | grep content-encoding
RecommendedFont loadingSelf-hosted WOFF2, subsetted, font-display: swap.
Self-hosted WOFF2, subsetted, font-display: swap.
Download from Google Fonts, self-host. Subset to Latin (or your needed character set) with pyftsubset. font-display: swap shows fallback text immediately. Preload only the one font used above the fold.
RequiredScript loadingdefer for app code, async for third-party, type=module for ESM.
defer for app code, async for third-party, type=module for ESM.
Bare <script src="..."> in <head> blocks rendering. Always use defer (executes after parsing) or async (executes when ready). type="module" scripts are deferred by default.
RequiredHTTP/2+Multiplexed connections, QUIC if available.
Multiplexed connections, QUIC if available.
HTTP/2 at minimum (all major hosts support it). HTTP/3 (QUIC) eliminates TCP head-of-line blocking. Check: curl -sI --http2 https://example.com | head -1 should show HTTP/2.
OptionalSpeculation RulesPrerender likely next pages for instant navigation.
Prerender likely next pages for instant navigation.
<script type="speculationrules">{"prerender":[{"where":{"href_matches":"/*"},"eagerness":"moderate"}]}</script>. "moderate" = on hover, "conservative" = on click. Chromium only, progressive enhancement.
Official specification ↗RecommendedBFCacheBack/forward cache for instant history navigation.
Back/forward cache for instant history navigation.
Avoid: unload listeners, Cache-Control: no-store on HTML, unclosed WebSockets. Test in Chrome DevTools > Application > Back/forward cache. Pages restored from BFCache load in 0ms.
Privacy
RequiredPrivacy policyWhat you collect, why, who gets it, how long you keep it, user rights.
What you collect, why, who gets it, how long you keep it, user rights.
Required by law in the EU (GDPR), California (CCPA/CPRA), and most jurisdictions. Must be written in plain language. Update when your data practices change. Link from the footer of every page.
RequiredCookie consentOpt-in before setting non-essential cookies (EU/UK).
Opt-in before setting non-essential cookies (EU/UK).
Pre-checked boxes do not count as consent. Rejecting must be as easy as accepting (no dark patterns). Essential cookies (session, CSRF) do not need consent. Analytics cookies do.
RecommendedGlobal Privacy ControlBrowser signal for "do not sell/share my data."
Browser signal for "do not sell/share my data."
Check: navigator.globalPrivacyControl === true or the Sec-GPC: 1 request header. California (CCPA) and Colorado (CPA) legally require honoring this signal.
Official specification ↗RequiredThird-party script auditEvery external script can read cookies and exfiltrate data.
Every external script can read cookies and exfiltrate data.
Audit every <script src="https://..."> quarterly. Remove scripts you no longer use. Lock remaining ones with CSP and SRI. A single compromised CDN script can steal every user's session.
RecommendedCookieless analyticsTraffic data without personal data collection.
Traffic data without personal data collection.
Plausible, Fathom, Umami, or Cloudflare Web Analytics. No cookies, no personal data, no GDPR consent banner needed. Google Analytics requires a consent mechanism in the EU.
RequiredData minimizationCollect only what you need, keep only as long as needed.
Collect only what you need, keep only as long as needed.
Do not log full IP addresses if you only need country-level geo. Set retention periods and enforce them. Redact PII from error logs. GDPR Article 5(1)(c) mandates this.
Resilience
RequiredCustom error pages404 returns 404, 500 returns 500, both are helpful.
404 returns 404, 500 returns 500, both are helpful.
Explain the error in plain language. Link to homepage and search. Never return 200 OK for error pages (soft 404). Test: curl -sI https://yoursite.com/nonexistent should show 404, not 200.
Recommended503 + Retry-AfterPlanned maintenance response.
Planned maintenance response.
HTTP 503 with Retry-After: 3600 (seconds). Tells search engines the downtime is temporary - do not deindex. Without Retry-After, crawlers may return and still get 503, wasting crawl budget.
OptionalOffline fallbackService worker caches a fallback page.
Service worker caches a fallback page.
Register a service worker that returns a cached "You're offline" page when fetch fails. Workbox makes this a 10-line setup. Users see a branded page instead of the browser's dinosaur.
OptionalWeb app manifestJSON file for installable PWA behavior.
JSON file for installable PWA behavior.
manifest.webmanifest with name, short_name, icons (192px + 512px), start_url, display: "standalone", theme_color, background_color. Link from <head>: <link rel="manifest" href="/manifest.webmanifest">.
Official specification ↗RecommendedExternal monitoringUptime checks from outside your infrastructure.
Uptime checks from outside your infrastructure.
UptimeRobot (free tier), Pingdom, Better Uptime. Combine synthetic checks (is the site up?) with Real User Monitoring (how fast is it for actual users?). Put the status page on a separate host.
Internationalisation
RequiredURL patternPick one: subdirectory (/fr/), subdomain (fr.example.com), or ccTLD (example.fr).
Pick one: subdirectory (/fr/), subdomain (fr.example.com), or ccTLD (example.fr).
Subdirectories are simplest and recommended by Google. Do not mix patterns. Each language version needs its own URL - do not use cookies or JS to switch content on the same URL.
RequiredhreflangTells search engines which language version to serve which users.
Tells search engines which language version to serve which users.
<link rel="alternate" hreflang="fr" href="https://example.com/fr/page">. Must be reciprocal: EN points to FR, FR points back to EN. Include hreflang="x-default" for the fallback version.
Official specification ↗RequiredLocalized metadataTranslate title, description, OG tags, JSON-LD - not just the body.
Translate title, description, OG tags, JSON-LD - not just the body.
A French page with English <title> and og:description is a half-translation. Search engines and social platforms pull from metadata, not the body. Translate everything in <head>.
AvoidNo auto geo-redirectsIP-based language redirects break crawlers and VPN users.
IP-based language redirects break crawlers and VPN users.
Googlebot crawls from the US. A US-based redirect sends it to /en/ and it never sees /fr/. Users on VPNs get the wrong language. Show a banner suggesting the local version instead of forcing a redirect.
RequiredInline lang attributesMark foreign phrases so screen readers switch pronunciation.
Mark foreign phrases so screen readers switch pronunciation.
<span lang="ja">東京</span>. Without this, a screen reader using English pronunciation will try to read Japanese characters as English, producing nonsense.
Official specification ↗RecommendedLanguage switcherList languages in their own names, never with flags.
List languages in their own names, never with flags.
"Deutsch", "日本語", "العربية". Not "German 🇩🇪". Flags are countries, not languages. Which flag for Spanish - Spain, Mexico, Argentina? Which for English - US, UK, Australia?
RequiredRTL supportdir="rtl" on <html> for Arabic, Hebrew, Persian, Urdu.
dir="rtl" on <html> for Arabic, Hebrew, Persian, Urdu.
Use CSS logical properties: margin-inline-start instead of margin-left, padding-block-end instead of padding-bottom. Layouts mirror automatically. Do not hardcode left/right in CSS.
RecommendedIntl APIsFormat dates, numbers, currency per locale.
Format dates, numbers, currency per locale.
new Intl.NumberFormat("de-DE").format(1234.56) returns "1.234,56". new Intl.DateTimeFormat("ja-JP").format(date) returns "2026/5/31". Never hardcode date/number formats.
Related
Sources
Last updated May 2026.