Skip to content

checklist

Technical SEO checklist for Next.js sites

Irfan5 min read

What is different about technical SEO on a Next.js site?

Technical SEO on a Next.js site is mostly about configuration, not content. The framework generates canonicals, sitemaps and metadata for you, so most failures are silent: a config flag disables redirects without warning, a page-level metadata export wipes inherited Open Graph tags, and a trailing slash mismatch turns every canonical into a redirect. None of these produce an error.

Most technical SEO advice is framework-agnostic and therefore vague. This is the opposite: a list of things that break specifically on Next.js, all of which we have hit on production sites, and none of which produce an error message.

Work through it in order. The first four are the ones that cost real rankings.

1. Check what your output mode silently disables

This is the most expensive item on the list, so it goes first.

Setting output: 'export' in next.config.mjs produces a static site — and silently discards three things:

DiscardedWhat breaks
redirects()Every 301 you wrote. Old URLs 404 instead of passing equity
rewrites()Any proxying or URL masking
headers()Security headers, cache-control, everything

There is no warning at build time and no error at runtime. The functions stay in your config, look correct in code review, and never run.

How to verify: request a URL you have a redirect for on the deployed site and check the status code. Then check for a header you configured:

curl -sI https://example.com/old-url/ | head -1
curl -sI https://example.com/ | grep -i strict-transport-security

If the redirect returns 200 or 404 rather than 301, and the header is absent, your rules are not running. Either drop output: 'export' or move the rules to your host's configuration. Do not leave them in a file where they read as active.

2. Route every page through one metadata helper

Next merges metadata one level deep. A page that exports this:

export const metadata = {
  openGraph: { title: 'X', description: 'Y' },
};

does not add to the parent's openGraph — it replaces the whole object, silently dropping images, type and siteName. The page still builds. The page still looks fine. It just shares with no image.

The fix is not discipline, it is structure: one helper that reconstructs the full object every time, and no page writing a raw metadata export.

Two related traps in the same area:

  • A title template defined in a layout applies to child segments, not to the segment that defines it. Your homepage title must be written in full. This is correct behaviour and it looks like a bug every single time.
  • keywords still works and still does nothing. Google has ignored the meta keywords tag since 2009. Shipping it signals dated SEO to anyone reading your source.

3. Make trailing slashes agree everywhere

trailingSlash: true is a good default — it is unambiguous and it matches what most hosts do. But it has to be matched by every URL you emit:

  • Canonical tags
  • Sitemap entries
  • JSON-LD @id values and url properties
  • Internal hrefs
  • Any hardcoded URL in copy

A canonical of /services/seo on a site serving /services/seo/ points at a URL that redirects. Search engines treat that as a weaker signal than a canonical pointing at a live URL, and you get no warning that it is happening.

The reliable fix is a single absolute() helper used by everything that constructs a URL, so the rule exists in one place rather than in the memory of whoever writes the next page.

Most teams find the audit straightforward and stall on the remediation — the config changes, the redirect map, the canonical rewrite. That is the bulk of

what our technical SEO work covers.

4. Server-render your navigation

Google renders JavaScript, so client-only navigation is not fatal for Googlebot. It is still worth fixing, because navigation is how internal link equity moves around your site, and several things that read your HTML do not render at all — social preview crawlers, many AI answer-engine crawlers, and most SEO tools.

The specific failure: a dropdown that only mounts on hover or click. In the server-rendered HTML, those links do not exist. Every page behind that dropdown loses its inbound links from every page on the site.

Ship the full menu in the HTML and hide it with CSS. Not conditional rendering — CSS.

opacity-0 invisible  →  group-hover:visible group-focus-within:visible

On this site, that single change took inbound internal links on deep service pages from 1–3 up to 21. It sits alongside the rest of the internal-linking work in the write-up of how that architecture was built, including the generated link graph that replaced hand-written arrays and the flaw in the first version of it.

5. Verify the sitemap against reality

Next's sitemap.ts generates from whatever you tell it to. Two things go wrong:

Routes it cannot see. If content lives outside the app directory — MDX files, a CMS, anything — a directory walk will not find it. Those pages exist, are linked, and are missing from the sitemap.

URLs that do not match canonicals. Usually the trailing slash problem from §3, appearing in a second place.

Check the deployed sitemap and count:

curl -s https://example.com/sitemap.xml | grep -c "<loc>"

Compare that number to the pages you believe you have. A mismatch is either a missing route or an orphan.

6. Decide what your crawl surface actually is

Faceted filters and pagination are where a content site quietly generates thousands of thin URLs.

  • Filters: if a filter changes the URL, each combination is a crawlable page. Five types across eight categories with pagination is several hundred near-duplicate URLs, all competing with each other. Client-side filtering avoids this entirely at the cost of shareable filter links, which nobody shares.
  • Pagination: give each page a self-referencing canonical. Canonicalising page 3 to page 1 tells search engines the content on page 3 does not exist.

7. Check cache headers on HTML, not just assets

Next stamps statically prerendered HTML with s-maxage=31536000 — a one-year shared-cache lifetime. If your CDN honours it, a deploy can leave stale pages served for days while your origin is already correct.

Hashed assets under /_next/static are immutable and should keep a long cache. HTML should not. A short s-maxage with a long stale-while-revalidate keeps the CDN useful while guaranteeing a deploy propagates in minutes.

8. Verify against the deployed site, not the dev server

Everything above passes on localhost. Most of it breaks at the host or CDN layer, which the dev server does not have.

One trap worth knowing: some hosts run a WAF that challenges non-browser clients with a 403 carrying noindex. A naive curl then reports a catastrophically broken site that is in fact fine — and, more importantly, real AI crawlers can hit the same challenge, because they are more likely to look non-browser-like than Googlebot is.

Send browser-like headers when you check, and cache-bust every request. A stale CDN copy will otherwise show you a problem you fixed three deploys ago.

The short version

If you only do four things: check what output disabled, centralise metadata, make trailing slashes agree, and server-render your navigation. Those four cover most of the ranking damage a Next.js site does to itself.

Working through this list?

Most teams get through the audit and stall on the remediation. That is the part we usually take on.

Key takeaways

  • `output: 'export'` silently discards redirects(), rewrites() and headers() — no warning, no error.
  • Next merges metadata one level deep, so a page-level `openGraph` replaces the parent's entirely.
  • `trailingSlash: true` must be matched by every canonical, sitemap entry and internal link, or each one becomes a redirect.
  • A title `template` does not apply to the segment that defines it, so the homepage title must be written in full.
  • Verify against the deployed site, not the dev server — host and CDN behaviour is where most of this actually breaks.
FAQ

Questions about this

Partly. Next.js generates the sitemap, robots.txt and metadata tags for you, and its routing produces clean URLs by default. What it does not do is warn you when a configuration choice disables those features. The most expensive Next.js SEO failures are silent ones, which is why this is a checklist rather than a set of things to install.

Related services

More on SEO

TopicsTechnical SEONext.jsIndexing
Chat with us