Fixing SEO in Headless Ecommerce
A headless shop separates the storefront, built in a framework such as Shopify Hydrogen, Next.js or BigCommerce Catalyst, from the commerce platform that holds products, prices and orders. That gives full control over speed and design, but it also removes everything a traditional platform handled for you: titles, canonicals, sitemaps, redirects, status codes and structured data all become your code.
Most headless SEO problems are things nobody rebuilt. This guide shows how to choose a rendering mode for each page type, then restore each missing piece and check it the way a crawler would.
What a traditional platform did that you now own
On a standard Shopify or WooCommerce shop, the platform and theme quietly produce dozens of things search engines rely on. On a headless build each is an explicit task, and each is easy to miss because the storefront looks perfect in a browser.
| Job | Traditional platform | Headless storefront |
|---|---|---|
| Rendering HTML | Server always sends full HTML | Depends on the framework mode you choose per route |
| Title, description, canonical | Theme and SEO settings | Your route code must set them from product data |
| 404 for missing products | Automatic | Often a 200 page that says "not found" unless you set the status |
| Redirects for old URLs | Admin redirect table | Must be read from the platform or configured at the edge |
| Product and breadcrumb markup | Theme or app | Must be generated in the storefront |
| Sitemap and robots.txt | Generated for you | Built from the catalogue API |
| hreflang for markets | Platform feature | Must be emitted per route |
Starter templates such as Hydrogen's demo store or the Next.js commerce examples include some of these, but only the basics, and they are frequently removed or broken during customisation. Treat every row as something to verify, not assume.
Choosing static, server or client rendering for each page
Modern frameworks let each route render in a different way. The right choice depends on how often the content changes and how many pages there are.
| Page type | Suggested mode | Why |
|---|---|---|
| Product pages | Server rendering with short caching, or static with frequent revalidation | Price and stock change; thousands of pages make full rebuilds slow |
| Category and collection pages | Server rendering with caching, or static with revalidation | Product lists change as stock moves |
| Content, buying guides, about pages | Static generation | Rarely change, fastest to serve |
| Search results, basket, account | Client or server, not indexed | Personal or endless; keep out of the index |
| Filtered category views | Server-rendered for shoppers, canonical to the clean category | Avoid thousands of indexable combinations |
The one mode to avoid for indexable pages is pure client-side rendering, where the server sends an empty shell and the browser fetches the product from an API. Google can render JavaScript, but rendering may lag behind crawling and any failure in the API call leaves an empty page. Most other search engines and AI assistants that fetch pages read only the server HTML. The general case is covered in Fixing JavaScript crawler rendering.
Hydration and responsiveness
Server rendering fixes what crawlers see, but the browser still has to download and run the JavaScript that makes the page interactive. A product page that ships a large bundle can show quickly and then respond slowly when the shopper picks a size, which harms Interaction to Next Paint. Keep interactive islands small and avoid sending code the page does not use.
Seeing your storefront the way a crawler does
Because the site looks right in a browser, you need tools that show the raw response.
# Status code and headers for a product
curl -sI https://www.example.co.uk/products/fernfold-trail-tent
# Status for a product that does not exist (should be 404)
curl -sI https://www.example.co.uk/products/no-such-product
# Is the product name in the server HTML?
curl -s https://www.example.co.uk/products/fernfold-trail-tent | grep -i "Trail tent"
# Title, canonical and robots tags in the server HTML
curl -s https://www.example.co.uk/products/fernfold-trail-tent | grep -iE "<title|canonical|robots"
Then compare with URL Inspection in Search Console, which shows the HTML Google rendered and any resources it could not load. Things to look for:
- The product name, price, description and main image present in the first HTML, not only after scripts.
- One
<title>and one canonical, matching the product, not the site default repeated on every route. - A
404or410status for missing products, not200. - Structured data in the server HTML, not injected later.
- No
noindexleft over from a staging configuration.
Soft 404s, redirects and the old platform
Missing products
When a product is deleted or unpublished, the storefront's API call returns nothing. A common fault
is to render a friendly "product not found" page with status 200. Search engines treat these
as soft 404s, and the URL can stay in the index. Every framework has a way to set the status: in Next.js,
calling notFound() in a route returns a 404; in Hydrogen and other React Router based frameworks,
throwing a Response with status 404 from the loader does the same.
Redirects
Shopify and BigCommerce both keep a redirect table in the admin, which merchandisers use when products change. A headless storefront ignores it unless you read it. Hydrogen provides a helper that checks Shopify's redirects when a route is not found; on other stacks you can query the redirects through the platform API, or manage them at your CDN or host. Whichever you use, make sure there is one place where merchandisers can add a redirect without a code release.
Moving from a traditional platform
If the shop used to run on a Shopify theme or another platform, URL patterns often change in the move, and the old theme storefront may still be reachable on another domain. Map every old URL to its new one, and make sure the retired storefront is password-protected or redirected so two versions of the shop are not indexed. The migration itself is covered in Fixing headless migrations.
Structured data and freshness
Product markup has to be built in the storefront from the same product object used to render the page,
so the price in the markup cannot differ from the price on screen. Output it as a JSON-LD block during
server rendering, created with JSON.stringify rather than string joining. Breadcrumb and
organisation markup belong in shared layout components. For the field rules, see Fixing
Product schema errors; for debugging it on this kind of stack, see Debugging
schema on headless sites.
Caching without stale prices
Headless storefronts are fast partly because they cache aggressively, at the CDN, in static pages or in data caches. The risk is a page and its markup showing yesterday's price or stock. Options include short cache lifetimes for product routes, revalidating a product's page when the commerce platform sends a webhook for a price or stock change, and fetching the live price for the basket separately while keeping the cached page close to current. If you send a feed to Google Merchant Center, stale prices in pages can also cause mismatches there.
The fix, step by step
Step 1
Check what the server actually sends
For one product, one category, one content page and one non-existent product URL, fetch
the page with curl or a browser with JavaScript turned off. Record the status code, the
title, the canonical, any robots meta tag, whether the product name and price appear, and whether JSON-LD
is present. Compare with URL Inspection in Search Console. This short list of gaps becomes the plan for
the rest of the work.
Step 2
Choose a rendering mode for each page type
Make every indexable route render its main content on the server, either on request with caching or statically with revalidation. Use static generation for guides and fixed content, server rendering or frequent revalidation for products and categories, and keep search, basket and account pages out of the index. Move any product or category data that is currently fetched in the browser into the route's server loader so it arrives in the first HTML.
Step 3
Rebuild metadata and canonicals per route
In each route, set the title, meta description, canonical and Open Graph tags from the product or category data, using the SEO title and description fields from the commerce platform where merchandisers fill them in. Point canonicals at the clean URL without variant, filter or tracking parameters. Add hreflang for each market and language if you sell internationally, and make sure the site-wide default title never appears on product routes.
Step 4
Return real status codes and redirects
When
the product or category lookup returns nothing, send a 404 status, or 410 for
items removed for good, with a helpful page that links back to the shop. Connect the storefront to the
platform's redirect table or manage redirects at the edge, so old and changed URLs return a single permanent
redirect to their new address. Test a deleted product, an old URL and a URL with a trailing slash difference.
Step 5
Generate structured data from the same data
Add a server-rendered JSON-LD component to product routes that builds the Product block,
including offers, brand and identifiers, from the same product object the page displays, and serialise
it with JSON.stringify. Add breadcrumb markup to product and category routes and organisation
markup to the root layout. Test a sample in the Rich Results Test and confirm the price and availability
match what the page shows.
Step 6
Build sitemaps and robots rules from the catalogue
Generate XML sitemaps from the commerce API, listing published products, categories
and content pages, split into several files if the catalogue is large, with a sitemap index at a stable
address. Exclude unpublished products, filtered views and account pages. Serve a robots.txt
that points to the sitemap index and keeps crawlers out of search, basket and endless parameter URLs,
then submit the index in Search Console.
Step 7
Keep prices and stock fresh under caching
Review how long product pages and data are cached at every layer: CDN, framework cache and any static output. Set product routes to revalidate often, and where the platform supports webhooks, trigger revalidation for a product when its price, stock or content changes. Check a product after a price change to confirm the page, the JSON-LD and any Merchant Center feed all show the new price within an acceptable time.
Step 8
Lock down preview and duplicate hosts
List every hostname that serves the storefront: preview deployments, staging, the host's default domain and any retired theme storefront on the commerce platform. Make sure only the production domain is indexable, using a noindex header or password protection on the others, and check that some hosts already add a noindex header to previews. Search Google for a distinctive product name to catch copies of the shop indexed under the wrong host.
Notes for common stacks
Shopify Hydrogen
Hydrogen is Shopify's React framework for headless storefronts, built on React Router (formerly Remix)
and usually hosted on Shopify's Oxygen. It includes SEO utilities for meta tags, sitemaps and redirects,
but custom builds often bypass them. Shopify's product URLs use a variant query parameter
for preselected variants; canonicalise these to the product URL.
Next.js storefronts
With the App Router, set metadata per route with generateMetadata, return 404s with notFound(),
and generate sitemap and robots through the framework's metadata files. Mixing
static generation and server rendering is straightforward, but check which routes are actually static
in the build output.
BigCommerce Catalyst and other composable stacks
Catalyst is BigCommerce's Next.js-based storefront and follows the same rules. For commercetools, Saleor, Medusa and similar platforms, the storefront is entirely your code, so every row of the table at the top of this guide is your responsibility.
1. Is headless commerce bad for SEO?
No, but it moves responsibility from the platform to your development team. A well-built headless shop can be faster and cleaner than a theme-based one, with full control over markup and rendering. The problems come when features a traditional platform provided, such as 404s, redirects, canonicals, sitemaps and Product markup, are not rebuilt or are broken during customisation. Plan and test those explicitly and headless has no inherent SEO disadvantage.
2. Does Google index client-side rendered product pages?
Google can render JavaScript and often does index client-rendered pages, but rendering may happen some time after crawling, API failures leave empty pages, and links that appear only after interaction can be missed. Many other search engines and AI assistants read only the HTML the server sends, so they see nothing. For product and category pages, server rendering or static generation removes that uncertainty entirely, which is why it is the recommended approach.
3. Should product pages be static or server-rendered?
Either can work, as long as the content is in the first HTML. Static generation with frequent revalidation is very fast but needs care to keep prices and stock current and can make full builds slow for large catalogues. Server rendering with short caching keeps data fresh at the cost of a little more server work. Many shops use server rendering for products and categories and static generation for guides and fixed content pages.
4. Why are deleted products still showing in Google?
The most likely cause is a soft 404: the storefront shows a not found message but returns status 200, so search engines treat the URL as a live page. Check with curl or the Network panel. Change the route to return a real 404 or 410 when the product lookup fails, or redirect to a close replacement where one exists. After that, the URLs should drop out as Google recrawls them.
5. How do I keep the Shopify admin redirects working in a headless storefront?
A headless storefront does not use the theme, so Shopify redirects are ignored unless the storefront checks them. Hydrogen includes a helper that looks up Shopify redirects when a route is not found, and other stacks can query redirects through the Admin or Storefront API or manage them at the CDN. Keep one source that merchandisers can edit without a code release, so a product rename does not wait for a developer.
6. Can preview deployments get indexed?
Yes, if they are publicly reachable and nothing tells search engines to stay away. Some hosts, including Vercel, add a noindex header to preview deployments by default, but custom domains for staging, the host default domain for production and old theme storefronts may not be covered. Check the response headers on each hostname, and use noindex headers or password protection on anything that is not the production shop, and recheck whenever you add a new environment.
7. Do I still need a sitemap if my storefront links everything?
A sitemap is strongly recommended for any shop of reasonable size, and on a headless build it will not exist unless you generate it. It helps search engines discover new products quickly, find items that sit deep in pagination, and understand which URLs you consider canonical. Build it from the commerce API so it reflects published products automatically, include last modified dates only when they are accurate, and submit the index in Search Console.
The Ecommerce Audit checks your most important store pages for product page completeness, category depth, Product and review schema, faceted navigation, product images, internal links and product page speed.
Open Ecommerce Audit Fixes โ