Moving from WordPress, Squarespace, or Webflow to a headless architecture is one of the most performance-positive decisions a development team can make. Next.js with Contentful or Sanity on the backend produces pages that score 90+ on Core Web Vitals out of the box. But it also removes the guardrails that traditional CMS platforms provide for SEO — and teams who aren't proactive about this discover the gap the hard way, after launch.
The Headless SEO Checklist
Before we dive into platform-specific guidance, here are the core SEO concerns in every headless implementation:
Metadata
- Title tags per page
- Meta descriptions
- Open Graph tags
- Twitter Cards
Canonical URLs
- Self-referencing canonicals
- Cross-domain handling
- Pagination canonicals
- Preview URL prevention
Structured Data
- Article / BlogPosting schema
- Breadcrumb schema
- Product schema (e-com)
- FAQPage schema
Rendering
- SSR or SSG (not CSR)
- Crawlable on first response
- No JS required for content
- Robots.txt / Sitemap
Metadata Architecture in Next.js + Headless CMS
In Next.js App Router, metadata is defined via the generateMetadata function, which can fetch data from your CMS:
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
const post = await getPost(params.slug); // fetch from Contentful/Sanity
return {
title: post.seoTitle || post.title,
description: post.seoDescription,
alternates: { canonical: `https://yoursite.com/blog/${params.slug}` },
openGraph: {
title: post.ogTitle || post.title,
description: post.ogDescription,
images: [{ url: post.ogImage?.url }],
},
};
}CMS Content Model for SEO Fields
Add these fields to every content type in your CMS:
seoTitle— Short text, max 60 chars, optional override of page titleseoDescription— Short text, max 160 charscanonicalUrl— Short text, optional override for edge casesnoindex— Boolean, for draft pages, tag pages, etc.ogImage— Media/Asset field for social sharing image
Platform-Specific Notes
Contentful
Contentful's content model is schema-based and type-safe. Create a "SEO" content type and include it as a reference field in your Page and Blog Post types. Use the contentful SDK with TypeScript types generated from your schema for reliable data fetching. Note: Contentful's preview API uses different base URLs — always validate that your canonical tags point to your production domain, not Contentful's preview URLs.
Sanity
Sanity's GROQ query language is extremely powerful for fetching structured data. Define a seo object in your schema with all metadata fields, then query it alongside your page content:
// GROQ query
const query = groq`*[_type == "post" && slug.current == $slug][0]{
title,
"seo": seo{ title, description, image{ asset->{ url } } }
}`Storyblok
Storyblok's visual editor is excellent for non-technical teams but requires careful setup for SEO. Use the storyblok-rich-text-renderer for blog content and ensure your renderer outputs proper HTML heading hierarchy (H1 once, H2/H3 for structure). The bridge.js Storyblok provides for live preview can conflict with Google's rendering — ensure it only loads in preview mode, never in production.
Validating Your Headless SEO Implementation
After launch, validate every key page type using our free comparison tools. Check that your meta tags match your design with the Meta Tag Comparison tool, validate structured data with the Schema Validator, and confirm redirects are correct with the Redirect Checker. Run all pages through the Bulk URL Checker to catch any 404s before they impact rankings.