STORYBLOK SEO AUDIT · HEADLESS CMS · VISUAL EDITOR · NEXT.JS
Free Storyblok SEO Audit
Storyblok SEO requires the SEO Meta Fields plugin for structured content fields, frontend generateMetadata() for meta tag output, JSON-LD built in page components from Storyblok delivery API data, and custom sitemap generation. DeepSEOAnalysis audits your live Storyblok-powered frontend — validating meta tag output, structured data, sitemap coverage, real CrUX Core Web Vitals, and all five AI visibility signals — with Storyblok schema configuration and Next.js component code for every finding.
WHAT WE CHECK
6 Storyblok-specific SEO checks
Storyblok SEO auditing covers SEO component field configuration and frontend meta tag output, story slug quality and catch-all route generation, JSON-LD in frontend components from Storyblok delivery API data, custom sitemap via app/sitemap.ts, real CrUX Core Web Vitals with Storyblok image CDN optimisation, and AI visibility — with Storyblok schema paths and Next.js component code for each finding.
Storyblok SEO component configuration and frontend meta tag output
Storyblok SEO depends on two layers: the SEO fields defined in the Storyblok content type schema, and the frontend reading those fields to output the correct HTML `head` elements. The audit validates what the frontend actually renders. The audit checks: title tag presence and uniqueness across crawled pages (each Storyblok story\'s `seo.seo_title` should map to a unique `title` tag — pages without the SEO Meta Fields plugin configured or with empty seo_title fields fall back to the story\'s default title, which may be too generic for SEO ranking); meta description presence and length (the `seo.seo_description` field in Storyblok should be populated for every published story — empty descriptions result in Google auto-generating from page content); canonical URL format (the `seo.canonical_url` override field or the frontend-generated canonical from the story slug must output a valid absolute URL — relative canonicals are invalid); Open Graph tags (og:title, og:description, og:image — the OG image must be an absolute URL from Storyblok\'s asset CDN returning a 2xx response; `img.storyblok.com` CDN images with size transforms like `/m/1200x630` should be tested for availability); noindex implementation (a `noindex` field in the Storyblok SEO component or a story-level flag should translate to `meta name="robots" content="noindex"` in the frontend rendering — verify the frontend component reads and applies this field); and robots.txt (Storyblok-connected frontends must manage their own robots.txt — in Next.js via `app/robots.ts`, in Nuxt via a plugin or static file).
URL structure and routing in headless Storyblok frontends
Storyblok\'s URL structure is determined by the story\'s `full_slug` field (the folder path + story slug in the Storyblok space) and how the frontend maps this to public URLs. The audit checks: slug quality (Storyblok story slugs set in the story\'s URL field — the default slug is auto-generated from the story name, which may produce verbose or non-descriptive slugs; the frontend should use the story\'s `full_slug` for URL routing, and the Storyblok slug should be reviewed for keyword relevance before publishing); catch-all route configuration (in Next.js App Router, a `[...slug]/page.tsx` catch-all route handles all Storyblok story paths — verify the route generates paths for all published stories via `generateStaticParams()` fetching from the Storyblok delivery API with `per_page: 1000` and filtering for published stories only); draft vs published routing (Storyblok\'s preview token vs public token — the published URL should use the public token, not the preview token; the audit crawls using a standard HTTP client without the Storyblok preview token, matching how Googlebot crawls); translated slugs for multi-language sites (`translated_slugs` in Storyblok story data maps language codes to slugs — the frontend\'s routing must use these to serve language-specific URLs); and redirect handling (when Storyblok story slugs change, the old URL needs a 301 redirect in the frontend\'s `next.config.js` `redirects` function or via a redirect table stored in Storyblok).
JSON-LD structured data in Storyblok frontend components
Storyblok is a headless CMS — all JSON-LD must be implemented in the frontend framework using data from the Storyblok delivery API. The audit checks JSON-LD presence and correctness in server-rendered HTML. For blog/article stories: `Article` JSON-LD from story fields — `headline` from `story.content.title`, `datePublished` from `story.first_published_at`, `dateModified` from `story.published_at`, `author` from a populated author relationship field (Storyblok supports single and multi-option relationship fields), `image` from `story.content.featured_image.filename` (Storyblok asset URL). In Next.js: inject via `script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }} /` in the story page component. For FAQPage: add a `faq` blocks field to the Storyblok content type using a blocks field with a `faq_item` nested component (containing `question` text field and `answer` textarea). In the frontend, iterate `story.content.faq_blocks` to build the FAQPage JSON-LD: `story.content.faq_blocks?.map(item => ({ "@type": "Question", "name": item.question, "acceptedAnswer": { "@type": "Answer", "text": item.answer } }))`. For `BreadcrumbList`: construct from the story\'s `full_slug` path segments. For local business stories: `LocalBusiness` JSON-LD from address, phone, and geo coordinate fields in the Storyblok content type. Critical: all JSON-LD must be in the server-rendered HTML response (from Next.js SSR or `generateStaticParams()` SSG), not injected via a React `useEffect()` or dynamically after hydration.
XML sitemap for headless Storyblok with Next.js frontend
Storyblok has no built-in sitemap generation — the frontend must implement it. For Next.js App Router: create `app/sitemap.ts` that fetches all published stories from the Storyblok delivery API and returns a `MetadataRoute.Sitemap` array. Example: `const { data } = await storyblokApi.get("cdn/stories", { version: "published", per_page: 1000, excluding_slugs: "global/*,config" }); return data.stories.map(story => ({ url: \`https://yourdomain.com/\${story.full_slug}\`, lastModified: story.published_at, changeFrequency: "monthly", priority: story.full_slug === "" ? 1.0 : 0.7 }))`. The audit checks: sitemap accessibility at `/sitemap.xml` (Next.js App Router serves `sitemap.ts` at this path automatically); URL coverage (all published Storyblok stories should appear — verify the API call uses `version: "published"` not `"draft"` and that `per_page` is large enough to capture all stories, or paginate through results for large spaces); `lastModified` from `story.published_at` (or `story.first_published_at` if `published_at` isn\'t updated on content changes — test which reflects the actual last content change); exclusion of config/global stories (Storyblok sites typically have global configuration stories — header, footer, settings — that should be excluded from the sitemap via `excluding_slugs`); and the `Sitemap:` directive in `robots.txt` (add `sitemap: "https://yourdomain.com/sitemap.xml"` to the `app/robots.ts` return object).
Core Web Vitals for Storyblok + Next.js sites
Storyblok-powered sites most commonly use Next.js, Nuxt, or Astro as the frontend — the Core Web Vitals profile depends heavily on the frontend framework\'s rendering approach. The audit uses real CrUX field data. **LCP** — for Storyblok sites, the hero image is typically served from Storyblok\'s asset CDN (`img.storyblok.com`). Storyblok\'s image service supports WebP transformation via URL parameters: `story.content.hero_image.filename + "/m/1200x630/filters:format(webp)"`. In the Next.js `Image` component, use a custom loader for Storyblok images: `const storyblokLoader = ({ src, width, quality }) => \`\${src}/m/\${width}x0/filters:quality(\${quality ?? 75})\``; then `Image loader={storyblokLoader} src={heroImage.filename} alt={heroImage.alt} width={1200} height={630} priority /`. The `priority` prop adds `fetchpriority="high"` and preloads the image — critical for LCP. **INP** — Storyblok\'s React components and the Storyblok bridge JavaScript (loaded for Visual Editor preview) should only be loaded in preview mode, not on the production frontend. The bridge script adds significant JavaScript weight — verify it\'s conditionally loaded: `if (process.env.NEXT_PUBLIC_STORYBLOK_ENV === "preview")`. **CLS** — Storyblok image components should always include explicit `width` and `height` props to prevent layout shift. **SSG for TTFB** — use Next.js `generateStaticParams()` with Storyblok to pre-render all story pages at build time; use on-demand ISR revalidation via Storyblok webhooks (Story Published webhook → revalidate the path) to refresh pages when content changes without full rebuilds.
AI visibility for Storyblok sites
AI visibility configuration for Storyblok sites is implemented in the frontend framework, since Storyblok is a headless CMS with no native support for AI-specific files or signals. For `llms.txt`: place `llms.txt` in the Next.js `public/` directory (`public/llms.txt`) — Next.js serves static files from `public/` at the root, making the file accessible at `/llms.txt` automatically. The file should list the site\'s main content types, key URLs, and primary topics. For `robots.txt`: use Next.js `app/robots.ts` to generate the robots.txt programmatically — return a `MetadataRoute.Robots` object that explicitly does not disallow GPTBot, ClaudeBot, or PerplexityBot. If using a static `public/robots.txt`, verify AI crawlers aren\'t blocked. For `FAQPage` JSON-LD: add a Storyblok `faq_blocks` blocks field to story content types, and implement FAQPage JSON-LD output in the frontend page component using the field data. For question-heading ratio: Storyblok\'s Rich Text field (rendered via `renderRichText()` from `@storyblok/richtext`) supports heading blocks — encourage editors to phrase H2/H3 headings as questions in FAQ-style content sections. A `faq_section` Storyblok component with explicit question and answer fields (rather than free-form rich text headings) is more reliable for maintaining the question-format ratio. For content chunkability: Storyblok\'s blocks/sections structure naturally segments content — each blok in a page\'s `body` array represents a distinct section. Keep section bloks focused on single subtopics with concise content blocks (≤400 words per section) rather than combining multiple topics in one rich text blok. The audit checks all five GEO signals with Next.js component code and Storyblok field configuration for each fix.
HOW IT WORKS
Audit your Storyblok site in 60 seconds
- Enter your published Storyblok site URL. Use the live production domain — not the Storyblok Visual Editor preview URL (app.storyblok.com/...) or the local dev server. No Storyblok space access, API tokens, or space ID needed. Works with all Storyblok plans and all frontend frameworks (Next.js, Nuxt, Astro, SvelteKit).
- We crawl and analyse. The engine validates the frontend's meta tag output from
generateMetadata()across crawled pages, checks JSON-LD in server-rendered HTML, reviews the custom sitemap for coverage and format, measures CrUX Core Web Vitals from real Chrome field data, and evaluates all five AI visibility signals. - You get a prioritised report. Storyblok-specific findings first — missing SEO Meta Fields plugin configuration, empty seo_title/seo_description fields rendering generic output, missing FAQPage JSON-LD from faq_blocks fields, Storyblok bridge script loading on production, large unoptimised images from Storyblok's asset CDN — then general SEO, each with the Storyblok schema path and Next.js code for the fix.
FAQ
Questions about the Storyblok SEO audit
Does Storyblok have built-in SEO features?
Storyblok (storyblok.com) is a headless CMS with a visual editor — it stores content and exposes it via a REST or GraphQL API, but SEO meta tag output and structured data are the responsibility of the frontend framework consuming the API. Storyblok\'s built-in SEO capabilities: (1) The official SEO Meta Fields plugin (available free in the Storyblok app directory) adds a reusable SEO component (a schema/nestable component) to any Storyblok content type. The SEO component includes fields for meta title, meta description, OG title, OG description, OG image, and canonical URL override. (2) Storyblok supports defining the `_uid`, `component`, and custom fields per story type (page, article, product) — the URL slug field on each story maps to the published URL. (3) The Storyblok bridge (for preview) and the delivery API (for production) both return the same story data — the frontend uses the story\'s SEO fields to populate meta tags and structured data. What Storyblok does not include: automatically generated XML sitemaps (must be implemented in the frontend), hreflang for multi-language spaces (multi-language in Storyblok uses separate folders or spaces; hreflang must be implemented in the frontend), and JSON-LD structured data (must be implemented in frontend components from Storyblok story data).
How do I add meta tags to a Storyblok site?
Adding meta tags to a Storyblok-powered site requires (1) configuring the SEO fields in Storyblok and (2) reading those fields in the frontend to output `title`, `meta`, and `link` tags. In Storyblok: install the SEO Meta Fields plugin from the Storyblok app directory, then add the SEO component as a nestable in your Page or Article content type schema. The SEO component adds fields: `seo_title`, `seo_description`, `og_title`, `og_description`, `og_image`, `canonical_url`. In the frontend (Next.js App Router): fetch the story via `@storyblok/react` and access the SEO fields: `const seo = story.content.seo` → return `{ title: seo?.seo_title ?? story.content.title, description: seo?.seo_description, alternates: { canonical: seo?.canonical_url ?? \`https://yourdomain.com/\${story.slug}\` } }` from `generateMetadata()`. For Open Graph: `{ openGraph: { title: seo?.og_title ?? seo?.seo_title, description: seo?.og_description ?? seo?.seo_description, images: seo?.og_image?.filename ? [{ url: seo.og_image.filename }] : [] } }`. Without the SEO Meta Fields plugin, SEO fields must be manually added to each content type schema via Storyblok\'s Schema Editor. The audit validates the rendered HTML output — checking that meta tags are present and correctly formatted in the HTTP response the frontend returns.
How does Storyblok handle multi-language and international SEO?
Storyblok supports multi-language content through two patterns: (1) **Field-level translation** — within a single space, individual fields on each story can be translated by enabling field-level translation in the field definition. The translated content is stored under locale codes (e.g., `content.title` for default, `translated_slugs` array for alternate slugs). The frontend fetches the story with a language parameter: `useStoryblokApi().get("cdn/stories/home", { language: "de" })` and renders the appropriate language version. (2) **Folder-based language routing** — each language lives in a separate folder within Storyblok (e.g., /en/, /de/, /fr/ folders in the Storyblok space), with the frontend routing based on the folder structure. For hreflang: Storyblok does not automatically generate hreflang tags. The frontend must output hreflang link elements in the `head` based on the available language variants. In Next.js App Router: use the `alternates.languages` object in `generateMetadata()` — `alternates: { canonical: \`/\${lang}/\${story.slug}\`, languages: { en: \`/en/\${story.slug}\`, de: \`/de/\${story.slug}\` } }`. The audit checks hreflang tag correctness: absolute URLs, valid language codes, bidirectional alternates (every variant must reference all other variants including itself), and x-default for homepage.
Is the Storyblok SEO audit free?
Yes. The complete audit is free with no signup and no email gate. Enter the published URL of your Storyblok-powered site — not the Storyblok Visual Editor preview URL (app.storyblok.com/...). The audit crawls the frontend output that Googlebot sees, regardless of which Storyblok plan the space is on (Community, Growth, or Enterprise). Paid DeepSEOAnalysis plans add saved report history, scheduled monitoring, larger crawls (up to 1,000 pages), and agency reporting workflows.