Wagtail SEO audit

Wagtail CMS (wagtail.org) is a popular open-source Django-based content management system known for its flexible StreamField content architecture, clean admin interface, and strong developer ecosystem. SEO on Wagtail requires custom implementation: structured data from StreamField blocks, sitemap coverage for all URL patterns, canonical tag templating, and caching configuration for Core Web Vitals performance. DeepSEOAnalysis validates the rendered output — not the Django code — checking exactly what Googlebot sees across all six dimensions.

Run free Wagtail SEO audit →

What the audit checks on Wagtail sites

Wagtail SEO field output and meta tag rendering
Wagtail's built-in SEO tab provides seo_title and search_description fields — but both are optional, so empty fields cause title fallback and missing meta descriptions across the page tree. The audit checks: whether seo_title is populated or falling back to page.title (which often includes site name suffixes that are then double-suffixed in templates); whether search_description is populated on all indexable pages; whether OG title and OG description are set (separate from search metadata in some Wagtail SEO packages); whether the canonical tag is implemented in the base template (Wagtail core does not output canonical tags — requires custom template code or wagtail-seo package); and whether noindex pages are excluded from the sitemap and robots.txt. Sites using wagtail-seo or wagtail-meta-preview packages get additional admin-side meta preview functionality, but the rendered output still requires template validation.
Wagtail page tree URL routing and slug configuration
Wagtail's page tree determines URL structure: each Page has a slug field that forms its URL relative to its parent. Common SEO routing issues: (1) Slugs auto-generated from page titles often include stop words and non-descriptive patterns — validate slug quality for key landing pages. (2) Moving pages in the Wagtail page tree changes their URL; without a redirect configured (Wagtail's contrib.redirects or a manual redirect rule), the old URL returns 404. (3) Root page and site root configuration — Wagtail's Site model maps a hostname to a root page; misconfigured site roots cause canonical URL mismatches. (4) Multilingual sites using wagtail-localize or wagtailmenus require hreflang tag configuration not provided by Wagtail core. (5) Draft/unpublished pages returning 404 is correct — verify no live internal links point to unpublished pages. (6) Pages with show_in_menus=False still appear in the sitemap by default — configure exclude_from_sitemap=True (Wagtail 3.0+) or custom sitemap filtering for admin pages, login pages, and thank-you pages.
JSON-LD structured data from StreamField and custom blocks
Wagtail's StreamField enables rich page layouts but requires explicit structured data implementation. Common patterns and their SEO implications: (1) Article JSON-LD — requires headline (from seo_title, max 110 characters), datePublished (from page.first_published_at in ISO 8601 format), dateModified (from page.last_published_at), author (from a foreign key to an author model or a StreamField author block with Person schema), and image (from search_image or a featured image field — must be absolute URL). (2) FAQPage JSON-LD — if the page contains a FAQ StreamField block (StructBlock with question/answer CharFields or RichTextFields), the structured data should be generated from the block values in get_context() and injected as JSON-LD in the template. Empty answer fields or RichTextField values that include HTML markup (not plain text) in the FAQPage answerText break Rich Results eligibility. (3) BreadcrumbList — generated from the Wagtail page tree ancestry (page.get_ancestors(inclusive=True)). (4) All JSON-LD must be server-rendered in the initial HTML — not GTM-injected — for reliable Googlebot parsing.
Wagtail sitemap coverage and robots.txt configuration
The built-in wagtail.contrib.sitemaps generates a sitemap from the page tree, but several coverage gaps require verification: (1) Custom Django URL patterns not using Wagtail page routing (e.g., function-based views for tools, API endpoints, or legacy URLs) are not included in the Wagtail sitemap — add a custom sitemap class for these URLs. (2) Wagtail image and document URLs (e.g., /images/, /documents/) should be covered by Disallow in robots.txt if they're not intended for indexing. (3) The sitemap should be declared in robots.txt (Sitemap: https://example.com/sitemap.xml). (4) Test sitemap accessibility after deployment — Wagtail's sitemap URL must be added to urlpatterns before Wagtail's catch-all URL pattern (url(r'', include(wagtail_urls))) or it will be handled by Wagtail's 404 handler. (5) For multi-site Wagtail setups, each site needs its own sitemap configuration — the default wagtail.contrib.sitemaps only generates pages for the current Site object.
Core Web Vitals for Wagtail-served pages
Core Web Vitals performance for Wagtail sites varies significantly by hosting environment and caching configuration. Key signals from CrUX field data: (1) LCP — Wagtail's {% image %} tag generates renditions on first request (stored in wagtailimages_renditions); ensure LCP images use fill or max-width renditions appropriate for the viewport rather than serving full original images. Add fetchpriority="high" and loading="eager" to the above-fold hero image. (2) TTFB — Django + Wagtail without frontend caching (Varnish, CDN HTML caching, or Django cache middleware) can produce 200–500ms TTFB. Wagtail's FrontendCache module (wagtail.contrib.frontend_cache) invalidates cached pages on publish — implement for production. (3) CLS — Wagtail image renditions should always specify width and height attributes to prevent layout shift; use the {% image %} tag's width and height output attributes in modern Wagtail versions. (4) INP — watch for Wagtail admin JS (window.wagtailConfig) loading on public-facing pages; verify base.html does not include admin-specific scripts.
AI visibility and GEO score for Wagtail sites
Five AI visibility checks on Wagtail sites: (1) AI crawler access — robots.txt should allow GPTBot, ClaudeBot, PerplexityBot, Google-Extended, and CCBot. Wagtail sites behind Cloudflare or security middleware sometimes have blanket User-agent:* blocks that inadvertently block AI crawlers. (2) llms.txt — add a static llms.txt file at the site root (served via Django's STATICFILES or as a Wagtail document) or implement a /llms.txt/ URL pattern returning the file dynamically from the Wagtail page tree. (3) FAQPage JSON-LD — generate from FAQ StreamField blocks in the initial server-rendered HTML, not via GTM. (4) Question-heading ratio — Wagtail RichTextField content can be analysed for heading structure; content teams should be guided to use question-format H2/H3 headings for informational content (≥20% of headings should be questions). (5) Content chunkability — StreamField architecture naturally supports chunked content via blocks, but blocks without headings create long unmarked sections; enforce heading blocks within body content for sections exceeding 400 words.

How to audit your Wagtail site

  1. Enter any URL from your Wagtail site — homepage, blog post, product page, or landing page.
  2. DeepSEOAnalysis crawls the live site, validating structured data in server-rendered HTML, checking Core Web Vitals from real CrUX field data, and scoring AI visibility across five GEO signals.
  3. Review property-level structured data errors (exact missing fields with corrected markup), Core Web Vitals readings, robots.txt and AI crawler access, and GEO score.
  4. Share the audit URL with your development team — no login required to view results.

Wagtail SEO frequently asked questions

How does Wagtail handle SEO metadata?
Wagtail provides a built-in SEO tab on every page model (via the Page base class) with fields for SEO title (seo_title), search description (search_description), and open graph image. These fields are accessible in templates via page.seo_title, page.search_description, and page.search_image. When seo_title is empty, Wagtail falls back to the page title field. When search_description is empty, pages have no meta description — a common gap on Wagtail sites that ship without explicitly populating description fields. The SEO tab also provides a promote_panels section where developers can add custom SEO fields (canonical URL, noindex toggle, custom OG fields). JSON-LD structured data and canonical tags are not provided by Wagtail core — they require custom template blocks or a Wagtail SEO package (wagtail-seo, wagtail-meta-preview).
How do you add JSON-LD structured data to a Wagtail site?
Wagtail does not generate JSON-LD structured data by default. The three approaches: (1) Custom template block — add a <script type="application/ld+json"> block to the base.html template with a Python dict serialised via |json_script or json.dumps in the view context. Page-specific schema (Article, FAQPage) requires passing the structured data dict from the page\'s get_context() method. (2) Wagtail StreamField — define a FAQBlock or SchemaBlock in the page\'s StreamField, render the structured data in the template from the block\'s value. (3) wagtail-seo package — provides schema configuration fields in the CMS admin for common schema types. JSON-LD must be server-rendered in the initial HTML response — not injected via Google Tag Manager — to be reliably parsed by Googlebot\'s first-pass HTML crawler.
Does Wagtail generate a sitemap automatically?
Wagtail includes a built-in sitemap module (wagtail.contrib.sitemaps) that generates a sitemap.xml covering all live, indexable pages from the Wagtail page tree. The sitemap is enabled by adding wagtail.contrib.sitemaps to INSTALLED_APPS and adding the sitemap URL to urls.py. Key behaviours: the sitemap only includes pages with live=True and show_in_menus or all pages depending on the wagtail_site configuration; it excludes pages where Page.exclude_from_sitemap is True (Wagtail 3.0+); lastmod uses page.last_published_at. Sites using custom page types not inheriting from wagtail.models.Page (e.g., Django views) must add those URLs manually. The sitemap should be declared in robots.txt.
What are common Core Web Vitals issues on Wagtail sites?
Wagtail-specific Core Web Vitals issues: (1) LCP from unoptimised image renditions — Wagtail\'s image API (page.search_image or StructuredValue image fields) generates renditions on first request; if large images are used as hero backgrounds without specifying fill or max-width renditions, LCP suffers. Use {% image page.banner_image fill-1200x630 %} and add loading="eager" + fetchpriority="high" on the LCP image. (2) TTFB from uncached Django views — Wagtail pages served without caching (no Varnish, no CDN, no Django cache middleware) can have 200–500ms TTFB under load, especially on shared hosting. Use Wagtail\'s built-in page cache (WAGTAILFRONTENDCACHE or Varnish integration) or a CDN with HTML caching. (3) CLS from images without explicit dimensions — Wagtail image tags without width and height attributes cause layout shift as images load. Always specify rendition dimensions. (4) INP from Wagtail Editor JS loading on frontend — ensure Wagtail admin JS is not loading on public pages.