The Next.js SEO checklist we actually use
Rendering strategy, metadata, canonicals, hreflang, structured data, sitemaps and Core Web Vitals — the concrete list we run before a Next.js site goes live.
Next.js is good for SEO and routinely used badly for it. This is the checklist we run before launch, in the order we run it.
Rendering: does the HTML contain the content?
The first test costs nothing: fetch the page with curl and read the response. If the main content is not in the HTML, no amount of metadata work will help. In the App Router, server components render by default — the problem is almost always a use client boundary placed too high, or data fetched in an effect rather than on the server.
Decide the caching strategy per route rather than globally. Marketing pages suit static generation with revalidation. Anything genuinely per-user should be dynamic and is usually not something you want indexed anyway.
Metadata: unique, generated, and correct per locale
Every indexable route needs its own title and description from the content, not a template with the site name repeated. Use generateMetadata so they follow the data. Titles under about sixty characters, descriptions under about one hundred and sixty, both written for a human deciding whether to click.
Set metadataBase once so relative Open Graph URLs resolve absolutely — this is the most common cause of broken social previews.
Canonicals: one address per page
Self-referencing canonical on every page, absolute, matching the URL you actually want indexed. Pay attention to trailing slashes, uppercase paths and tracking parameters — pick one form, redirect the rest, and make sure the canonical agrees with the redirect target rather than fighting it.
hreflang: the part most sites get wrong
For a multilingual site, every page needs alternate links to each locale version and to itself, plus x-default. They must be reciprocal: if the English page points at the Romanian one, the Romanian page must point back. Broken reciprocity means Google ignores the whole cluster.
This is much easier when the CMS stores the locales as one document, because the alternate URL is then a property of the content rather than something a developer maintains by hand. It is exactly how the page you are reading resolves its Romanian counterpart.
Structured data: describe what the page is
Organization and WebSite on the home page, BreadcrumbList on anything nested, Service or Product on the relevant templates, Article on posts, and FAQPage where a real question-and-answer section exists on the page. Validate it — invalid JSON-LD is ignored silently, which looks identical to not having any.
Sitemaps and robots
Generate sitemap.xml from the same data source as the routes, so it cannot drift. Include locale alternates. Exclude anything noindex. Keep robots.txt honest — blocking a path in robots does not remove it from the index, it just stops Google reading the noindex you put there.
Core Web Vitals
Use next/image with explicit dimensions to eliminate layout shift. Use next/font so fonts are self-hosted and preloaded rather than fetched from a third party. Set priority on the hero image only. Audit your JavaScript bundle honestly — an analytics tag, a chat widget and a consent banner can easily outweigh the entire application.
Then measure with field data in Search Console rather than a local Lighthouse run on a fast laptop. Lab numbers are for debugging; field numbers are what ranks.
Before launch
Crawl the staging site with a real crawler and compare its URL list to your sitemap. Check that 404s return 404 and not 200 with a friendly message. Confirm redirects are single-hop. And if you are migrating, map every old URL to a new one before the switch, not after the traffic drops.