Skip to main content
Version: next

SEO & analytics audit rules

Audit rules covering technical SEO, meta tags, structured data, and tracking reliability in a Front-Commerce project.

An e-commerce storefront lives or dies by its organic traffic and by the reliability of its conversion data. These rules check the technical SEO foundations (crawlability, canonical URLs, structured data) and the analytics setup, with emphasis on failure modes observed in real Front-Commerce projects: missing Open Graph tags, sitemaps left on the legacy backend, and homegrown tracking that bypasses the Front-Commerce analytics module.

AUDIT-SEO-01 — Serve environment-appropriate robots.txt

Severity: critical — Detection: runtime

Rule: The production robots.txt does not disallow the whole site, and non-production environments (staging, preprod) do disallow all crawlers or are protected by HTTP authentication.

Why: A global Disallow: / shipped to production deindexes the entire catalog — a direct, major revenue loss that can take weeks to recover from. The inverse mistake (an open staging environment) creates duplicate content that competes with production pages.

How to check: Fetch the file on every deployed environment:

curl -s https://www.example.com/robots.txt
curl -s https://staging.example.com/robots.txt

A violation is Disallow: / (or an empty/404 response combined with an open staging site) on production, or a staging environment that neither disallows all user agents nor sits behind authentication. Also verify the robots.txt references the sitemap URL (Sitemap: line) and that this URL responds (see AUDIT-SEO-02). Statically, locate how the file is produced — grep -rn "robots" app/ extensions/ public/ — and confirm the content is driven by the environment rather than hardcoded.

AUDIT-SEO-02 — Serve the sitemap from the storefront or proxy it cleanly

Severity: important — Detection: runtime

Rule: https://<shop>/sitemap.xml responds with 200 and XML content listing storefront URLs; if the sitemap is generated elsewhere (for example by Magento), the storefront URL proxies it or redirects to it with a single 301.

Why: Crawlers discover deep catalog pages through the sitemap. A real audit found the sitemap still served only by the Magento backend on its own domain, with no redirection from the storefront: search engines crawling the declared location got a 404, and the sitemap URLs pointed at the wrong domain.

How to check:

curl -sI https://www.example.com/sitemap.xml
curl -s https://www.example.com/sitemap.xml | head -20

A violation is a 404, a redirect chain (more than one hop), or <loc> entries pointing at a different host than the storefront. Statically, verify the project uses the Front-Commerce sitemap service: look for getSitemapEntries or sitemapFetcher usage in extensions (grep -rn "getSitemapEntries\|sitemapFetcher" app/ extensions/) and confirm that custom dynamic routes (CMS pages, landing pages) register a fetcher so they appear in the sitemap.

AUDIT-SEO-03 — Keep canonical URLs and robots directives consistent

Severity: important — Detection: runtime

Rule: Every indexable page declares exactly one canonical URL pointing at its preferred variant, and no indexable page carries a noindex directive (via meta tag or x-robots-tag header).

Why: Product pages are typically reachable through several paths (category path, search, direct URL) and with query parameters (filters, pagination, tracking). Without a canonical, search engines split ranking signals across duplicates. A stray x-robots-tag: noindex header — often a leftover from a staging configuration — silently deindexes pages while the HTML looks correct.

How to check: For one URL per page type (home, category, product, CMS):

curl -sI https://www.example.com/some-product.html | grep -i "x-robots-tag"
curl -s https://www.example.com/some-product.html | grep -io '<link rel="canonical"[^>]*>'

A violation is a missing canonical on an indexable page, a canonical pointing at a different page than the one served, contradictory signals (canonical + noindex on the same page), or an x-robots-tag header on production pages that should be indexed. Also fetch a filtered category URL (?color=blue&page=2) and verify its canonical points at the unfiltered page or that it is marked noindex, per the project's SEO strategy.

AUDIT-SEO-04 — Enforce one URL per resource with single-hop 301 redirects

Severity: important — Detection: runtime

Rule: Trailing slash handling is consistent (one variant serves 200, the other permanently redirects to it), legacy URLs redirect with a 301, and no redirect requires more than one hop.

Why: When both /category and /category/ serve 200, every page exists twice for crawlers. Redirect chains (http → https → non-slash → final) waste crawl budget and leak PageRank at each hop; 302 responses on permanent moves prevent signal transfer entirely.

How to check:

curl -sIL -o /dev/null -w "%{http_code} %{url_effective}\n" https://www.example.com/some-category/
curl -sIL -o /dev/null -w "redirects: %{num_redirects}\n" http://example.com/some-category/

Run the pair for a category, a product, and a CMS page, testing both slash variants. A violation is: both variants returning 200, any 302 on a permanent move, or num_redirects greater than 1 from any commonly linked entry URL. If the project migrated from another platform, sample a handful of old URLs from the redirect mapping and confirm they return a single 301 to the new location. Statically, review custom redirects registered in extensions (grep -rn "redirect" extensions/ app/routes/ on loader code).

AUDIT-SEO-05 — Provide meta title, description, and Open Graph tags per page type

Severity: important — Detection: runtime

Rule: Every page type (home, category, product, CMS) renders a unique <title>, a meta description, and Open Graph tags (og:title, og:type, og:image, og:url) reflecting that page's content.

Why: Title and description drive click-through from search results. Open Graph tags control how shared links render on social networks and messaging apps; a real audit found a storefront with no OG tags at all, so every shared product link displayed as a bare URL with no image.

How to check: With the application running:

for url in "/" "/some-category" "/some-product.html" "/some-cms-page"; do
curl -s "https://www.example.com$url" | grep -io '<title>[^<]*\|<meta[^>]*\(description\|og:\)[^>]*>'
done

A violation is a missing or duplicated <title> across page types, a missing description, or absent og: tags — product pages must at minimum carry og:title, og:image (the product image), and og:url. Statically, verify each custom route exports a meta function (grep -rln "MetaFunction\|export const meta" app/routes/) and that overridden page components did not drop the base theme's meta logic.

AUDIT-SEO-06 — Expose structured data as JSON-LD

Severity: important — Detection: runtime

Rule: Product pages emit valid Product JSON-LD (name, image, price, availability), all pages with a breadcrumb emit BreadcrumbList, and the site emits an Organization (or WebSite) block.

Why: Structured data powers rich results — price, availability, and review stars directly in search listings — which measurably raise click-through on product pages. Invalid or missing JSON-LD forfeits this space to competitors.

How to check:

curl -s https://www.example.com/some-product.html | grep -o '<script type="application/ld+json">[^<]*' | head -5

Parse each block and validate it with the Rich Results Test. A violation is a product page without a Product block, a Product block missing offers.price or offers.availability, or JSON that fails to parse. Statically, locate where structured data is produced (grep -rn "ld+json\|application/ld" app/ extensions/) and check that overridden product page components still render it.

Severity: important — Detection: static

Rule: All internal navigation uses the Front-Commerce Link (or TrackingLink) component; HTML content injected from a CMS is rendered through the Wysiwyg pipeline so its <a> elements become client-side navigations.

Why: A raw <a href> to an internal URL triggers a full page reload: the SPA state, the preloaded data, and the navigation performance are lost, and analytics page-view tracking may double-fire. This is a real, recurring finding with CMS-driven content: HTML from Strapi rendered with dangerouslySetInnerHTML contained internal <a> links, and every click on them reloaded the whole application.

How to check:

grep -rn "<a href" app/ extensions/ --include="*.tsx" --include="*.jsx" | grep -v "http\|mailto:\|tel:\|target="
grep -rn "dangerouslySetInnerHTML" app/ extensions/ --include="*.tsx"

A violation is a raw <a> pointing at an internal path, or CMS HTML injected with dangerouslySetInnerHTML instead of the Front-Commerce Wysiwyg component (whose transforms rewrite embedded links to client-side navigations). Confirm at runtime by clicking a link inside CMS-managed content with the network panel open: a full document reload on an internal link is a violation.

AUDIT-SEO-08 — Track and meet Core Web Vitals

Severity: important — Detection: runtime

Rule: LCP, CLS, and INP are within the "good" thresholds (LCP under 2.5 s, CLS under 0.1, INP under 200 ms) on the home, category, and product pages, and the team monitors them with field data.

Why: Core Web Vitals are a search ranking factor and correlate directly with conversion. A storefront that passes only in the lab regresses unnoticed without field monitoring.

How to check:

npx lighthouse https://www.example.com/some-product.html --only-categories=performance --output=json --quiet

Check field data through the PageSpeed Insights CrUX panel when the site has enough traffic. A violation is any of the three metrics outside "good" on a key page type, or the absence of any field monitoring (CrUX, RUM plugin, or equivalent). For remediation levers specific to Front-Commerce — priority images, font loading, one query per route — see Improve your Core Web Vitals.

Severity: critical — Detection: static

Rule: If the project uses Google Analytics or Google Tag Manager, the consent configuration declares the four Consent Mode V2 signals (ad_storage, ad_user_data, ad_personalization, analytics_storage) and the plugins are gated behind needConsent: true.

Why: Since March 2024, Google requires Consent Mode V2 for measurement in the EEA; without it, conversion data degrades and the site processes personal data without a valid legal basis — a GDPR exposure, not just a data-quality problem.

How to check:

grep -rn "google-analytics\|google-tag-manager\|gtag\|gtm" app/config/analytics.ts app/ extensions/
grep -rn "ad_storage\|ad_user_data\|ad_personalization\|analytics_storage" app/config/
grep -rn "needConsent" app/config/analytics.ts

If Google plugins are present, a violation is: missing consentOptions with the four V2 signals in app/config/cookiesServices.js (or .ts), a Google plugin with needConsent: false, or a custom plugin lacking an updateConsent method to propagate granular consent changes. Confirm at runtime that no Google request fires before consent is given (network panel, fresh session).

AUDIT-SEO-10 — Enable server-side tracking with a running worker for order events

Severity: important — Detection: runtime

Rule: Server-side events are configured (app/config/serverEvents.ts with a Redis config and at least one integration), the front-commerce worker process runs in production, and the OrderPlaced event is dispatched on order confirmation.

Why: Client-side order tracking loses 10–30% of conversions to ad blockers, consent refusals, and users closing the confirmation page early. The server-side OrderPlaced event is the only reliable source for revenue data — but it silently does nothing if the worker process is not deployed alongside the web process.

How to check: Statically, confirm the configuration exists:

cat app/config/serverEvents.ts
grep -rn "front-commerce worker" package.json Procfile* docker-compose* .platform* 2>/dev/null

A violation is a missing integration for the analytics destination, or no deployment artifact starting the worker (front-commerce worker absent from process definitions). At runtime, place a test order with the ConsoleIntegration (or worker debug integration) enabled and verify the OrderPlaced event is logged by the worker process. An event configured but never consumed (worker not running, Redis unreachable) is a violation.

AUDIT-SEO-11 — Route all tracking through the Front-Commerce analytics module

Severity: important — Detection: static

Rule: All tracking services are registered as plugins in app/config/analytics.ts; no tracking script is loaded through a hand-rolled <script> tag, a custom hook, or a parallel tag manager setup outside the analytics module.

Why: The Front-Commerce analytics module provides consent gating, e-commerce event mapping (product views, add-to-cart, checkout steps), and page-view tracking on client-side navigation. A real audit found a homegrown GTM implementation pasted into the document head: none of the Front-Commerce tracking features worked — no e-commerce events, no consent integration, and page views missed on every SPA navigation.

How to check:

cat app/config/analytics.ts
grep -rn "googletagmanager\|gtag(\|fbq(\|dataLayer" app/ extensions/ --include="*.tsx" --include="*.ts" | grep -v "config/analytics"
grep -rn "createElement(.script.)\|<script" app/root.tsx app/entry.client.tsx 2>/dev/null

A violation is any tracking snippet (GTM container, gtag bootstrap, Meta pixel) injected outside the analytics plugin list, or an analytics.enable set to false while tracking works "anyway" through a parallel implementation. The fix is to move each service to a plugin with needConsent set appropriately, so consent, e-commerce events, and SPA page views work uniformly.