Overview

E-commerce SEO has three core pages: product, category, and search. Each one needs different schema, different intent matching, and different crawl rules. The two recurring failure modes are faceted-navigation crawl traps that burn the crawl budget and variant explosions that split ranking signal across hundreds of near-duplicate URLs. Solve those two and the rest of the playbook follows.

Emit Product JSON-LD with offers, brand, sku, and aggregateRating

Every product page ships a single Product schema block with the four fields that decide rich-result eligibility: offers, brand, sku, and aggregateRating. Missing any of these and the product loses the price snippet, the star rating, or both.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Acme Trail Runner 3",
  "sku": "TR3-BLK-10",
  "brand": {"@type": "Brand", "name": "Acme"},
  "image": ["https://example.com/img/tr3-black.jpg"],
  "description": "Lightweight trail runner with a 4mm drop and Vibram outsole.",
  "offers": {
    "@type": "Offer",
    "price": "129.00",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "url": "https://example.com/products/trail-runner-3?color=black"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.6",
    "reviewCount": "247"
  }
}
</script>

See schema-markup-deep for the full Product field reference and the rules for Offer vs AggregateOffer when prices vary by variant.

Keep the page on temporary out-of-stock; return 410 only for permanently gone

Out-of-stock handling is the most common e-commerce SEO mistake. The right answer depends on whether the product comes back.

  • Temporary out-of-stock: keep the URL, set availability: "OutOfStock" in the Offer, and surface a “notify me” form on the page. The ranking and the indexed page persist; conversion resumes on restock.
  • Discontinued, no replacement: return 410 (Gone). Google drops the URL within a few crawls. Faster than 404 and signals intent.
  • Discontinued, has a replacement product: 301 to the replacement. Signal transfers; the old URL retires.
  • Discontinued with no replacement but inbound links worth keeping: 301 to the parent category. Less ideal than a replacement but better than a 410 if the equity matters.

See redirects for the status-code reference.

Variants live on one canonical URL with options, not separate URLs

Color, size, and material variants are options on a single canonical product page. Splitting each variant onto its own URL forces a canonical war that the crawler resolves badly.

  • One Product schema block per canonical page; variants live in the hasVariant field or in JS-rendered swatches.
  • The canonical URL is the parent product. Variant query strings (?color=black) point at the canonical via <link rel="canonical">.
  • Indexable variants only when the variant is a distinct purchase decision with distinct demand (one-off colorways with their own marketing, for example).
  • Schema’s Offer array lists each in-stock variant with its own price and SKU; the canonical absorbs the ranking signal.

This pattern keeps the ranking weight on one URL and lets the page rank for the broad query and the variant-specific query at once.

Reviews schema with real reviewer entities, never invented

Star-rating snippets multiply CTR; faked reviews invite a manual action. The rule is simple: only mark up reviews that came from real customers and reference the reviewer by name (or verified-buyer flag).

  • Use Review entries with an author Person, a reviewBody, and a datePublished.
  • Cap aggregateRating at the actual review count; do not pad it.
  • Verified-buyer reviews carry more weight than open reviews; surface that signal where possible.
  • Never reuse a generic “5 stars, great product” snippet across an entire catalog. Google detects the pattern.

Control faceted-nav crawling with noindex, follow and parameter rules

Faceted navigation produces an exponential URL explosion: brand x color x size x price-band quickly creates thousands of low-value combinations.

  • <meta name="robots" content="noindex, follow"> on facet combinations that have no search demand.
  • Allow indexing only on facet pages with proven query demand (size-12-trail-runners ranks; size-12-trail-runners-blue-129-to-149-usd does not).
  • Block crawl on infinite facet combinations via robots.txt only as a last resort; the noindex route lets link equity still flow through.
  • Set parameter handling in Search Console for sort, view, and pagination parameters that do not change content.

See pagination-and-facets for the parameter taxonomy and the rel=next/prev decision.

Category pages target the informational and transactional split

Category pages compete for both informational queries (“best trail runners”) and transactional queries (“buy trail runners”). The page that wins both does the work to serve both.

  • Top of page: product grid for transactional intent.
  • Below the grid: 200 to 400 words of editorial content that answers the informational query. Buying guide, feature comparison, sizing notes.
  • One H1 matching the category name; H2s on the editorial sections.
  • Internal links from the category page out to subcategory pages and back to the parent. See internal-linking.

Common errors

  • Variant URL explosion. Every color on its own URL with ?variant=... and no canonical. Splits ranking across dozens of URLs.
  • 404 on every discontinued product. Better to 301 to the replacement or the parent category.
  • AggregateRating without Review entries. Google flags as suspect; the rich result disappears.
  • Facet combinations crawled and indexed without query demand. Burns the crawl budget on URLs that never rank.
  • Category page is the product grid with no editorial content. Loses to category pages that answer the buying-guide query.
  • Out-of-stock product set to noindex during a temporary stockout. The URL drops from the index and takes weeks to recover after restock. Use availability: "OutOfStock" and keep the page indexable.