[ Checklist ]

The SEO checklist for developers

An SEO checklist for developers is a different document from the ones written for marketers: no keyword research, no content calendar. These are the 25 technical checks that decide whether the code you deploy can rank at all — and almost every one is verifiable from a terminal.

Run it before a deploy that touches public pages, or once against your whole site. Checked items persist in your browser.

0 / 25 verified
01 · Indexability

Can Google index the page at all?

  • The noindex that protects staging, shipped to production inside the same template, is the classic way an entire site vanishes from Google. Check the meta tag and the X-Robots-Tag response header — both count, and the header is invisible in view-source. Expect no output from either command.

    $ curl -s https://your.site/page | grep -i noindex$ curl -sI https://your.site/page | grep -i x-robots-tag
  • A Disallow rule written for an API path or crawler trap can shadow a real route by prefix. A blocked page can still be indexed URL-only — but Google never reads its content, so it ranks for nothing.

    $ curl -s https://your.site/robots.txt
  • SPAs love answering every URL with 200 and an empty or error shell. Google classifies those as soft 404s and drops them. The status code should tell the truth for both the page that exists and the one that does not.

    $ curl -s -o /dev/null -w '%{http_code}\n' https://your.site/page
  • A canonical baked into a shared HTML template declares every route to be the same page — and only that page will ever earn impressions. Each route must declare itself. This exact bug once capped this site to homepage-only rankings.

    $ curl -s https://your.site/page | grep -o '<link rel="canonical"[^>]*>'
02 · Rendered HTML

What the first crawl actually sees

  • Google indexes in two waves: the raw HTML immediately, the JavaScript render whenever the render queue gets there — hours to weeks later. Content that only exists after JS runs waits with it. curl the page and look for your H1 and body copy; the details are in our React/Vite SPA playbook.

    $ curl -s https://your.site/page | grep -c 'a phrase from your H1'
  • Client-side head managers set tags after hydration; the first wave sees whatever the template shipped. Frameworks fail this quietly — a one-line diff can delete a route's metadata export with no build error, as covered in the Next.js deploy-level regressions post.

    $ curl -s https://your.site/page | grep -o '<title>[^<]*</title>'
  • Googlebot does not click. A route reachable only through a JavaScript handler is unreachable; router links must render an href in the HTML (most router <Link> components do — verify yours did).

  • Heading structure is how the parser outlines the page. Styled <span> soup gives it nothing to outline — semantic headings are free structure, and they are what jump-link sitelinks anchor to.

03 · URLs & redirects

Renames and variants that silently fork your signals

  • A rename without a redirect zeroes the page's accumulated signals — the new URL starts from scratch while the old one 404s away everything it earned. Ship the 301 in the same deploy as the rename.

    $ curl -sI https://your.site/old-slug | grep -iE '^(HTTP|location)'
  • A→B→C loses signal at every hop, and Googlebot abandons long chains entirely. When you redirect a URL that was already a redirect target, re-point the first hop straight at the final destination.

    $ curl -sIL -o /dev/null -w '%{num_redirects}\n' https://your.site/page
  • http vs https, www vs apex, trailing slash vs not — every variant that answers 200 instead of redirecting splits one page into duplicates that compete with each other. Each variant should 301 to the single true form.

    $ curl -sI http://www.your.site/page
  • Everything after # never reaches the server and is ignored by Google — /#/pricing is the homepage as far as the index is concerned. Public routes need real paths.

04 · Discovery

Can Google find the page — and does it look worth fetching?

  • The sitemap is how Google learns a URL exists without having to crawl into it. Adding the route to the app but not the sitemap is the most common miss in this list.

    $ curl -s https://your.site/sitemap.xml | grep new-slug
  • Google calibrates how much it trusts your lastmod. Bumping every URL on every deploy teaches it to ignore the field — then real updates stop earning recrawls. Only bump the URLs whose content actually changed.

  • A sitemap entry signals existence; links from your most-crawled page signal the page matters. This is the lever that moves "Discovered – currently not indexed" — the status every new page on a small site starts in.

  • A sitemap nobody is told about does nothing. Declare it with a Sitemap: line in robots.txt or submit it once in Search Console.

    $ curl -s https://your.site/robots.txt | grep -i sitemap
05 · Structured data

Markup that parses, matches, and still earns anything

  • A JSON syntax error fails silently — no console error, no build failure, just no rich results. And a script injected client-side may never be seen at all. Bake it into the server HTML and paste the URL into Google's Rich Results Test after deploying.

    $ curl -s https://your.site/page | grep -c 'application/ld+json'
  • Markup describing content that is not on the page violates Google's structured-data policy. The fix is architectural: generate the JSON-LD from the same typed object that renders the page, so the two cannot drift — the approach in our JSON-LD shipping guide.

  • Google has been retiring rich results, not adding them — HowTo removed in 2024, FAQ deprecated in 2026. Article/BlogPosting, BreadcrumbList, Organization and Product still earn something; markup for retired types is dead weight.

06 · Performance

Core Web Vitals regressions that ship without a failing test

  • A hero image that waits on a client-side fetch — or lazy-loads above the fold — adds its whole latency to the LCP that real users report to CrUX. Remember the lag: a perf fix takes ~6–8 weeks to reach rankings through the 28-day field-data window, so regressions are expensive to undo.

  • A dashboard dependency leaking into the marketing chunk regresses LCP and INP with no failing test. Compare the build-output chunk sizes for public routes against the previous deploy before shipping.

  • Set explicit dimensions on images, embeds and ad slots, and never insert banners above existing content after load — CLS is measured on real users, where slow connections make late shifts worse than your dev machine ever shows.

07 · After the deploy

The part everyone skips: watching what happened

  • Rankings respond to a code change over days to weeks, not hours — and Search Console reports lag ~2 days on top. Without the deploy timestamp on the chart, whatever happens next is unattributable.

  • Compare matched days-of-week, then separate a ranking regression (position moved) from a demand drop (impressions moved) or a snippet change (CTR moved) — the full method is in how to tell if your last deploy hurt your SEO. Testing a change deliberately? SEO A/B testing works by pages, not users.

  • Search Console's URL Inspection shows the HTML Google actually rendered — the ground truth for every check above. If its screenshot or rendered HTML is missing what you shipped, one of the earlier items failed.

[ After the checklist ]

The checks above prevent regressions. This measures the wins.

Code Results connects your GitHub deploys to Google Search Console with causal attribution — so when a deploy passes this list and rankings move three weeks later, you know which PR did it.

Start for free