PAYLOAD CMS SEO AUDIT · HEADLESS · NEXT.JS · TYPESCRIPT
Free Payload CMS SEO Audit
Payload CMS SEO depends on collection field configuration for meta data, the Next.js frontend's generateMetadata() output, JSON-LD built in page components using Payload API data, sitemap generation via Next.js sitemap.ts, and App Router performance. DeepSEOAnalysis audits your live Payload-powered site — validating meta tag output, structured data, sitemap coverage, real CrUX Core Web Vitals, and AI visibility — with Next.js component code and Payload collection field configuration for every finding.
WHAT WE CHECK
6 Payload CMS-specific SEO checks
Payload CMS SEO auditing covers collection SEO field configuration and Next.js generateMetadata() output, URL routing and dynamic route generation, JSON-LD in Next.js page components from Payload API data, sitemap via app/sitemap.ts, Core Web Vitals from real CrUX data, and AI visibility — with Next.js code examples and Payload field configuration for each finding.
Payload collection SEO fields and frontend meta tag output
Payload CMS SEO is split between the collection schema (where SEO data is stored) and the frontend framework (where it\'s rendered). The audit checks what the frontend actually outputs, regardless of the source. The audit validates: title tag presence and quality across crawled pages (the Next.js `generateMetadata()` function must return a `title` for every page — pages where the meta title falls back to a generic default or the document ID indicate that the Payload SEO field isn\'t being fetched or returned); meta description presence and length (50–160 characters, unique per page — a missing description means the `@payloadcms/plugin-seo` field is empty or the frontend `generateMetadata()` function doesn\'t include `description` in its return); canonical URL correctness (`alternates.canonical` in Next.js App Router `generateMetadata()` should return the full absolute URL — relative canonical URLs are invalid and should use the full `https://yourdomain.com/path` format); Open Graph tags (og:title, og:description, og:image — Payload\'s media collection URL for the OG image must be an absolute URL returning a 2xx response); and noindex implementation (pages where the Payload document has `noindex: true` should render `meta name="robots" content="noindex"` in the frontend — verify the frontend component reads and applies this field).
URL structure in headless Payload deployments
Payload is a headless CMS — the URL structure is entirely controlled by the frontend framework, not by Payload itself. For Next.js App Router: URL structure comes from the `app/` directory file structure (static routes like `app/about/page.tsx`, dynamic routes like `app/blog/[slug]/page.tsx`). The audit checks: URL slug quality (Payload collections store a `slug` field — the slug generation logic in the Admin UI or a `beforeChange` hook should produce keyword-descriptive slugs, not auto-generated IDs or timestamp-based slugs); dynamic route configuration (for Payload\'s `generateStaticParams()` in Next.js SSG — verify the function fetches all published documents from Payload and returns their slugs, so all content is pre-built at build time rather than requiring server-side rendering on every request); trailing slash consistency (Next.js has trailing slash configuration in `next.config.js` — `trailingSlash: true` or `false` should be set consistently, with the canonical URL in `generateMetadata()` matching the configured format); multi-language URL structure (Payload supports locale fields — if using `@payloadcms/plugin-i18n` or a custom locale field, each locale\'s frontend route should have a corresponding hreflang tag output in the layout component); and redirect handling (Payload\'s `redirects` plugin or Next.js `redirects` in `next.config.js` for managing 301 redirects when slugs are changed or content is reorganised).
JSON-LD structured data in the Next.js frontend
Since Payload is a headless CMS, all JSON-LD must be implemented in the frontend framework consuming Payload\'s data. The audit checks JSON-LD presence and correctness in the server-rendered HTML response. For `Article`/`BlogPosting` on Payload blog collection pages: `headline` from `post.title`, `datePublished` from `post.publishedAt` (ISO 8601 — Payload stores dates as ISO strings), `dateModified` from `post.updatedAt`, `author` from a populated relationship field (`post.author.name` where author is a Users or Authors collection), `publisher` as site `Organization` with name and logo, `image` from `post.meta?.image?.url` or a `featuredImage` media relationship field. For `FAQPage` using Payload\'s Blocks field: a `faqBlocks` field in the collection with a block type containing `question` (text) and `answer` (richText or textarea) — iterate in the page component: `post.faqBlocks?.map(block => ({ "@type": "Question", "name": block.question, "acceptedAnswer": { "@type": "Answer", "text": block.answer } }))`. For `BreadcrumbList`: construct from the URL path segments and the collection hierarchy. Critical: JSON-LD must be in the server-rendered HTML (from Next.js SSR or `generateStaticParams()` SSG output), not injected via a `useEffect()` hook on the client side — the latter is invisible to Googlebot\'s initial crawl.
XML sitemap for headless Payload with Next.js
Payload CMS has no built-in sitemap. For a Next.js frontend: Next.js App Router provides a `sitemap.ts` file at `app/sitemap.ts` that returns a `MetadataRoute.Sitemap` array — implement this to fetch all published documents from Payload and return their URLs. Example: `const posts = await payload.find({ collection: \'posts\', where: { _status: { equals: \'published\' } }, limit: 0 }); return posts.docs.map(post => ({ url: \`https://yourdomain.com/blog/\${post.slug}\`, lastModified: post.updatedAt, changeFrequency: \'monthly\', priority: 0.7 }))`. The audit checks: sitemap accessibility at `/sitemap.xml` (Next.js App Router\'s `sitemap.ts` automatically serves at this path — verify the sitemap is accessible and not blocked by robots.txt); URL coverage (all published Payload documents across all relevant collections should appear — pages, posts, products — filtered by `_status: equals: published` in the Payload query); `lastModified` population (use Payload\'s `updatedAt` field for `lastModified` — this auto-updates on any document save); draft exclusion (filter by `_status: equals: published` to ensure draft documents don\'t appear in the sitemap); and the `Sitemap:` directive in `robots.txt` (Next.js doesn\'t auto-add this — add it to the `app/robots.ts` file\'s `host` and sitemap URL return).
Core Web Vitals for Payload CMS with Next.js
Payload CMS is most commonly deployed with a Next.js frontend, making Next.js App Router performance characteristics the primary performance concern. The audit uses real CrUX field data for LCP, INP, and CLS. **LCP** — for Payload sites with hero images stored in Payload\'s media collection: the hero image component should use Next.js `Image` with `priority` prop (`Image src={heroImage.url} alt={heroImage.alt} priority width={1200} height={630} /`). The `priority` prop adds `fetchpriority="high"` and preloads the image. Payload\'s media uploads should serve WebP — configure Payload\'s `imageSizes` in the collection to generate WebP variants: `{ name: \'webp\', width: 1200, height: undefined, position: \'centre\', formatOptions: { format: \'webp\' } }`. **INP** — Next.js App Router reduces client-side JavaScript by default with React Server Components. Avoid turning Payload data-display components into Client Components (`\'use client\'`) unnecessarily — every Client Component adds to the client JavaScript bundle. Interactive elements (search, filters, forms) should be Client Components; static content display should remain Server Components. **CLS** — Next.js `Image` with explicit `width` and `height` props prevents layout shift. For Payload Rich Text fields rendered as HTML: ensure rich text image blocks include dimensions. **TTFB** — use Next.js `generateStaticParams()` for Payload content pages to pre-render at build time (fastest possible TTFB); for frequently-updated content, use Incremental Static Regeneration (ISR) with `revalidate: 3600` to cache pages for up to an hour.
AI visibility for Payload CMS sites
Add `llms.txt` to the Next.js frontend\'s `public/` directory (`public/llms.txt`) — Next.js serves static files from `public/` at the root path, making the file accessible at `/llms.txt`. The file should list the site\'s content collections, key pages, and primary topics. For `robots.txt`: use Next.js App Router\'s `app/robots.ts` file to generate `robots.txt` programmatically — return a `MetadataRoute.Robots` object ensuring GPTBot, ClaudeBot, and PerplexityBot are not included in any `disallow` rules. If using a static `public/robots.txt`, verify AI crawlers aren\'t blocked. For `FAQPage` JSON-LD: add a `faqBlocks` Blocks field to Payload collections that contain FAQ content — each block has `question` (text) and `answer` (textarea) fields. In the Next.js page component, check for `post.faqBlocks?.length > 0` and inject `FAQPage` JSON-LD alongside the `Article` JSON-LD. For question-format headings: Payload\'s Lexical or Slate rich text editor allows heading blocks — encourage editors to use H2 headings phrased as questions for FAQ-style content. A Payload block type specifically for "FAQ" content (not just a rich text heading) makes this more systematic. The audit checks all five AI visibility signals — llms.txt, AI crawler access, FAQPage/HowTo JSON-LD in server-rendered HTML, question-heading ratio (≥20%), and content chunkability (avg section ≤400w) — with Next.js component code and Payload collection field configuration for each fix.
HOW IT WORKS
Audit your Payload CMS site in 60 seconds
- Enter your Payload site URL. No Admin UI access, database credentials, or API keys needed — the audit crawls the public HTTP output of the frontend as Googlebot would. Works with Payload v2 and v3, any frontend framework (Next.js, SvelteKit, Astro), and any deployment (Vercel, Railway, self-hosted).
- We crawl and analyse. The engine checks meta tag output from
generateMetadata()across crawled pages, validates JSON-LD in the server-rendered HTML response, checks sitemap coverage and URL format, measures CrUX Core Web Vitals from real Chrome field data, and evaluates all five AI visibility signals. - You get a prioritised report. Payload-specific issues first — missing
generateMetadata()output, collection field gaps, client-side-only JSON-LD injection, sitemap coverage gaps, missingllms.txt— then general SEO, each with the Next.js component code or Payload collection field configuration for the fix.
FAQ
Questions about the Payload CMS SEO audit
Does Payload CMS have built-in SEO features?
Payload CMS (payloadcms.com) is a headless CMS — it provides a flexible content API and admin UI but no built-in frontend rendering or SEO meta tag output. SEO in a Payload setup depends on two layers: (1) The Payload collection schema — defining fields in your collection configurations for meta title, meta description, OG image, canonical URL override, and noindex flag. These fields are stored in the database and exposed via Payload\'s REST or GraphQL API. The official `@payloadcms/plugin-seo` plugin provides a pre-built SEO field group that adds these fields to any collection. (2) The frontend framework — since Payload is headless, the actual `title`, `meta`, and JSON-LD output is the responsibility of the frontend application consuming the Payload API. For Next.js (the most common Payload frontend), this means using `generateMetadata()` in the App Router to fetch and return SEO fields from Payload, or using the `<Head>` component in Pages Router. The audit crawls the rendered HTML output of the frontend, so it validates what the frontend actually serves — regardless of whether the data comes from Payload, a static file, or a default fallback.
How do I add meta tags to a Payload CMS site?
Adding meta tags to a Payload CMS site requires configuring both the Payload collection and the frontend. For the collection: install `@payloadcms/plugin-seo` and add it to your `payload.config.ts` plugins array with `generateTitle`, `generateDescription`, and `generateURL` functions. This adds a "Meta" tab to your collection documents in the Admin UI with title, description, and image fields. For the Next.js App Router frontend: in the page component file (e.g., `app/[slug]/page.tsx`), add a `generateMetadata()` function that fetches the document from Payload and returns the metadata object: `return { title: doc.meta?.title, description: doc.meta?.description, alternates: { canonical: doc.meta?.url ?? \`/\${doc.slug}\` } }`. For the OG image: Payload\'s SEO plugin stores the image in a media collection — use `doc.meta?.image?.url` for the `og:image` URL. For the canonical: return `alternates: { canonical: doc.meta?.canonicalUrl || \`https://yourdomain.com/\${doc.slug}\` }`. The audit checks that these values appear in the rendered HTML `head` of the live page.
How do I add JSON-LD structured data to a Payload CMS site?
JSON-LD structured data in a Payload CMS frontend is added in the Next.js page components that consume Payload data. Since Payload is headless, structured data must be built in the frontend using the data returned by the Payload API. For Article schema on blog post pages: in the page component, destructure the post fields from the Payload API response and inject a `script type="application/ld+json"` block via `dangerouslySetInnerHTML` in the App Router layout, or use the Next.js `<Script>` component with `strategy="beforeInteractive"`. The JSON-LD object: `{ "@context": "https://schema.org", "@type": "Article", "headline": post.title, "datePublished": post.publishedAt, "dateModified": post.updatedAt, "author": { "@type": "Person", "name": post.author.name }, "image": post.meta?.image?.url }`. For FAQPage: if the Payload collection includes a `faq` blocks field (using Payload\'s Blocks field type with a Question block containing `question` and `answer` text fields), iterate the blocks array in the page component to build the FAQPage JSON-LD. The plugin `@payloadcms/plugin-seo` does not automatically generate JSON-LD — structured data is always a frontend implementation task in a headless CMS setup. The audit validates that JSON-LD appears in the server-rendered HTML (from Next.js SSR or SSG) rather than being injected client-side via JavaScript after hydration.
Is the Payload CMS SEO audit free?
Yes. The complete audit is free with no signup and no email gate. Paid plans add saved history, scheduled monitoring, larger crawls (up to 1,000 pages), and agency workflows.