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
- Enter any URL from your Wagtail site — homepage, blog post, product page, or landing page.
- 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.
- 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.
- 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.