Production Readiness Checklist for Modern Web Sites
102 Checks Across SEO, Social Sharing, Security, and Technical Quality
About This Document
This checklist was developed from hard-won experience debugging a Next.js speaker website after several issues were discovered post-deployment — broken social share previews, URL encoding bugs, oversized OG images, and missing metadata on listing pages. The items here are organised into ten categories. Running all 102 checks before every production deploy will catch the majority of common issues before your users or search engines do.
This checklist applies broadly to any server-side or statically generated site (Next.js, Nuxt, Gatsby, Astro, SvelteKit, etc.), though some items are most relevant to sites using the App Router pattern.
Need a printable version for deployment reviews? Get the gated PDF companion: /help/resources/production-readiness-checklist
Category 1 — Essential Static Files (5 checks)
These files are read by search engines, AI assistants, and RSS readers. They must exist and return HTTP 200 — a missing file silently breaks discoverability.
-
1.
robots.txtreturns HTTP 200 Every crawler checks this first. A 404 here can cause unexpected indexing decisions. -
2.
sitemap.xmlreturns HTTP 200 Required for Google Search Console and Bing Webmaster Tools submission. -
3. RSS feed returns HTTP 200 If you have a blog or news section, an RSS feed at
/feed.xmlor/rss.xmlenables newsletter tools, podcast apps, and RSS readers to follow your content automatically. -
4.
llms.txtreturns HTTP 200 An emerging standard (similar torobots.txtbut for AI language models) that lets you provide a plain-text description of your site and its purpose for AI training and retrieval contexts. Place at the domain root. -
5. IndexNow key file returns HTTP 200 If you use IndexNow for instant search engine URL submission, the key file at
/{your-key}.txtmust be publicly accessible.
Category 2 — robots.txt Content (3 checks)
Getting robots.txt wrong blocks your entire site from being indexed without any obvious error.
-
6.
robots.txtallows indexing (check thatDisallow: /is not present unless intentional) A bareDisallow: /on the User-agent line blocks all crawlers — a common mistake when copy-pasting from a staging configuration. -
7.
robots.txtreferences your sitemap AddSitemap: https://www.yourdomain.com/sitemap.xmlso crawlers can discover all your pages automatically. -
8. Sitemap URL in
robots.txtuses your canonical domain If your canonical domain iswww.yourdomain.com, the sitemap URL must match. A mismatch (e.g. referencing the non-www version) creates a signal conflict for search engines.
Category 3 — Sitemap Quality (4 checks)
-
9.
sitemap.xmlis valid XML Malformed XML causes crawlers to skip the sitemap entirely. Validate with Google Search Console or an XML linter. -
10. All URLs in sitemap use the canonical domain form Every URL must use your chosen canonical form consistently (
wwwor non-www,httpsnothttp). Mixed forms cause duplicate content issues. -
11. No URL-encoded newlines (
%0A) in sitemap URLs A stray newline in an environment variable or string template can produce URLs likehttps://www.yourdomain.com%0A/blog/post— valid enough to write to file, but broken for every tool that reads it. -
12. Sitemap contains the expected number of URLs Count the
<loc>entries programmatically and compare against your known page count. Pages that 404 or are excluded by accident won't appear.
Category 4 — RSS Feed Quality (3 checks)
-
13. RSS feed is valid XML (contains
<rss>or<feed>root element) An invalid RSS feed breaks all subscribers silently. Test with https://validator.w3.org/feed/ -
14. RSS feed uses the canonical domain Feed URLs embedded in items must match the canonical domain — RSS readers store these as unique identifiers for deduplication.
-
15. RSS feed contains published entries An empty RSS feed (
<channel>with no<item>elements) is worse than no feed — it signals to readers that the site is inactive.
Category 5 — Open Graph Tags on All Pages (35 checks, 5 per page × 7 pages)
Run these checks on every page with a distinct URL: homepage, about, speaking, events, blog listing, media, contact. Missing OG tags mean your links show as bare URLs when shared on LinkedIn, WhatsApp, X (Twitter), Slack, and iMessage.
For each page, verify:
-
og:titleis present and non-empty This becomes the bold headline in the link preview. Without it, platforms fall back to the<title>tag (sometimes truncated) or show nothing. -
og:descriptionis present and non-empty The preview subtext. Aim for 120–155 characters — enough to describe the page value without truncation. -
og:imageis present and non-empty The preview image. Without this, most platforms show a text-only card or no card at all. Must be an absolute URL (not a relative path). -
Canonical URL is set (
<link rel="canonical" href="...">) Tells search engines which URL is authoritative when the same content is accessible via multiple paths. -
og:imageURL uses the canonical domain If your images reference a non-canonical domain variant (e.g. non-www when you're a www site), crawlers may follow a redirect to fetch them — or fail silently.
The 7 pages to check: /, /about, /speaking (or equivalent), /events, /blog (listing), /media, /contact.
Common mistake: When a page defines an explicit
openGraphmetadata block that overrides the layout default, it loses theimagesproperty unless explicitly re-added. The blog listing page is a frequent offender — it gets a custom title and description, but the developer forgets to include the image.
Category 6 — Blog Post Open Graph (25 checks, 5 per post × 5 posts sampled)
For a representative sample of blog post URLs:
-
Page loads and returns HTTP 200 A post that 404s after deployment means its slug was changed, the static generation failed, or a data source was modified.
-
og:imageis present Per-post OG images (dynamically generated from the post title) dramatically increase click-through when sharing. Tools like Next.js'sopengraph-image.tsxor Vercel's OG image service can generate these automatically at build time. -
og:imageURL uses the canonical domain The image URL must match the domain serving the page. A mismatch forces a redirect, which some social crawlers do not follow. -
og:imageURL contains no URL-encoded newlines (%0A) A%0Ain a URL means a literal newline ended up in a string somewhere upstream — typically from a shellechoor heredoc adding a trailing newline to an environment variable. The fix: useprintf(notecho) when setting env vars from scripts, and add.trim()when reading env vars in code. -
Canonical URL contains no
%0ASame source, same fix.
Category 7 — OG Image Endpoints (5 checks)
If you generate OG images dynamically (server-side or at build time):
- Image endpoint returns HTTP 200
-
Content-Typeheader isimage/pngorimage/jpeg(nottext/htmlortext/plain, which indicates a render error) - Image is reachable without authentication — preview crawlers never authenticate
- No redirect loop — the image URL in the meta tag should point directly to the image, not through multiple hops
- File size is within platform limits — Facebook's limit is 8MB, but WhatsApp and some others are lower. Target under 1MB. A 1200×630 PNG should be around 100–500KB when optimised. Anything over 2MB risks being silently dropped.
Common mistake: Generating OG images at full display resolution (e.g. 2752×1536) from a Retina screen capture and never resizing. Always specify exact target dimensions (1200×630 for standard OG) and optimise with a tool like
sharp,imagemin, or PIL/Pillow.
Category 8 — Social Share Button URLs (6 checks)
If your pages include social sharing buttons (as opposed to leaving sharing entirely to the browser):
-
LinkedIn share link is present Use
https://www.linkedin.com/sharing/share-offsite/?url=ENCODED_URL -
X (Twitter) share link is present Use
https://twitter.com/intent/tweet?url=ENCODED_URL&text=ENCODED_TITLE -
WhatsApp share link is present Use
https://api.whatsapp.com/send?text=ENCODED_TITLE%20ENCODED_URL -
LinkedIn share URL contains no
%0A -
X share URL contains no
%0A -
WhatsApp share URL contains no
%0A
Why
%0Aappears in share URLs: Social share buttons work by passing the page URL as a query parameter. If the page URL itself contains a newline (see Category 5–6),encodeURIComponent()faithfully encodes it as%0A. The result is a share URL that opens LinkedIn/Twitter with a two-line broken URL in the draft. The fix is upstream — clean the URL at the source, not in the share button component.
Category 9 — HTTP Security Headers (5 checks)
These headers protect users from common web attacks. They should be set for every response from your server, not just HTML pages.
-
X-Content-Type-Options: nosniffPrevents browsers from MIME-type sniffing. Stops some types of XSS attacks where an attacker tricks a browser into treating a non-script file as JavaScript. -
X-Frame-Options: SAMEORIGINPrevents your pages from being embedded in<iframe>tags on other domains, protecting against clickjacking attacks. -
Strict-Transport-Security(HSTS) Tells browsers to only connect to your site via HTTPS, even if the user typeshttp://. Set at minimummax-age=31536000(1 year). -
Referrer-PolicyControls how much referrer information is sent to third-party links.strict-origin-when-cross-originis a sensible default that protects user privacy without breaking analytics. -
Permissions-PolicyRestricts access to browser APIs your site doesn't use (camera, microphone, geolocation, payment). Reduces the attack surface if a third-party script is compromised.
Category 10 — IndexNow Verification (1 check)
- IndexNow key file content matches the filename
The file at
/{key}.txtmust contain exactly the key string as its content. If the content doesn't match the filename, IndexNow submission will be rejected with a 403.
Bonus: Checks Worth Running Periodically (not in the 102)
These are harder to automate but worth doing manually on a schedule:
- Google Search Console — check for crawl errors, manual actions, and Core Web Vitals after each significant deployment
- LinkedIn Post Inspector (
linkedin.com/post-inspector/inspect/) — force LinkedIn to re-scrape any URL whose preview is stale after a metadata update - W3C Validator (
validator.w3.org) — check HTML validity after template changes - PageSpeed Insights (
pagespeed.web.dev) — check LCP, CLS and FID/INP after layout changes - ahrefs or Screaming Frog — full crawl to check for broken internal links after adding or removing pages
- SSL Labs (
ssllabs.com/ssltest/) — verify TLS configuration and certificate health
Implementation Note: Environment Variables and URLs
The most common source of %0A bugs is shell input handling:
# ❌ Adds a trailing newline (shell heredoc behaviour)
npx vercel env add MY_URL <<< "https://www.yourdomain.com"
# ✅ No trailing newline
printf "https://www.yourdomain.com" | npx vercel env add MY_URL
# ✅ Also safe in code — strip whitespace defensively
const url = (process.env.SITE_URL || "https://fallback.com").trim().replace(/\/$/, "")
Any environment variable that becomes part of a URL should be sanitised at the point of use, regardless of how cleanly it was set — this makes your code robust against future tooling changes, copy-paste mistakes, and different operating system line-ending conventions (Windows uses \r\n, macOS/Linux use \n).
Automation
All 102 checks above can be scripted in Python (using urllib), Node.js (using fetch), or integrated into a CI/CD pipeline as a post-deployment smoke test. The checks intentionally use only the public HTTP interface — no database access, no framework internals — which means they work regardless of your stack and will catch real-world issues as external crawlers and users would experience them.
Recommended workflow: run the full audit script as the last step of your deploy pipeline, after the new version is live but before marking the deployment as complete. Fail the pipeline if any check fails.
Compiled from production experience — March 2026
Apply This with Maya AI
If you want this checklist operationalized into weekly execution workflows, templates, and guided playbooks, start with the companion asset and the Help Center.