DEV Community

Cover image for Tech SEO with Headless
Nebojsa Radakovic
Nebojsa Radakovic

Posted on

Tech SEO with Headless

A headless architecture empowers eCommerce businesses to deliver lightning‑fast, highly interactive storefronts without sacrificing search engine visibility. By decoupling content and presentation layers, you gain full control over crawlable sitemaps, semantic URLs, and metadata management—all critical for ranking in today’s AI‑driven search landscape.

So, what (and how) can you control via a framework or platform of your choice in a headless setup for SEO? Fortunately, as a headless commerce platform, Crystallize offers a rather unique perspective.

(Everything else ecommerce SEO can be found in the linked article)

Implementing Sitemaps & robots.txt & LLM.txt in Your Headless Frontend

Next.js

  • robots.txt: Simply create a robots.txt file in your project’s /public directory.
  • sitemap.xml: Either hand‑craft a static sitemap.xml under /public or generate it at runtime via getServerSideProps, returning XML with your product and category URL.

Astro

  • Use the astro-sitemap integration to crawl static routes and output sitemap.xml.
  • Leverage astro-robots-txt to auto‑generate robots.txt based on your published page.

SvelteKit

  • Define a +server.js endpoint that returns XML for your sitemap.xml, mapping over your route manifest.
  • Create a separate +server.js or static asset for robots.txt, listing disallowed paths (e.g., /admin/*, /search/*).

While robots.txt guides crawlers on URL access, llms.txt, an experimental best practice, helps LLMs prioritize, fetch, and order content. It should not be considered a guaranteed method for driving traffic or indexing. Implementation-wise, you can upload it to your project’s public directory.

Alternatively, you can rely on a platform you are using (like Crystallize) to define LLM text per page that would be included in the file at build time. Basically, you’d model out the fields you need from scratch.

Custom 4XX Error Pages

Delivering branded, helpful error pages improves UX and ensures crawlers don’t get stuck on dead ends.

  • Next.js: Drop a pages/404.js file—Next will statically generate it at build time.
  • Astro: Add 404.astro (or 404.md) in /src/pages; Astro will serve it for any missing route.
  • SvelteKit: Use a Svelte component at +error.svelte to customize both 404 and other 4XX responses.

For complete control, you can also configure your hosting platform’s error‐page behavior (e.g., Netlify’s _redirects file or a Vercel error edge function).

Managing 301 Redirects

Permanent redirects preserve link equity when URLs change. In headless setups, you can choose framework‑level or hosting‑level redirects.
Next.js: Define a redirects array in next.config.js:

module.exports = {
  async redirects() {
    return [
      { source: '/old-path', destination: '/new-path', permanent: true },
    ];
  },
};
Enter fullscreen mode Exit fullscreen mode

Astro: In astro.config.mjs, add:

export default {
  redirects: [
    { from: '/old-url', to: '/new-url', status: 301 },
  ],
};
Enter fullscreen mode Exit fullscreen mode

SvelteKit: Use the redirect helper in your +server.js:

import { redirect } from '@sveltejs/kit';
export function GET() {
  throw redirect(301, '/new-location');
}
Enter fullscreen mode Exit fullscreen mode

Hosting‑Level (Netlify/Vercel/Cloudflare):
Netlify: _redirects file in publish folder:

/old-path  /new-path  301
Enter fullscreen mode Exit fullscreen mode

Vercel: vercel.json with a redirects array.
Cloudflare Pages: Plain‑text _redirects in your output directory.

Rendering Canonicals in a Decoupled Frontend

To prevent duplicate‑content issues, emit a self‑referencing or preferred canonical URL tag in your headless pages.
Next.js: Use <Head> (or next-seo) in your page component:

import Head from 'next/head';
// ...
<Head>
  <link rel="canonical" href={`https://example.com${router.asPath}`} />
</Head>
Enter fullscreen mode Exit fullscreen mode

Astro: Build the canonical using Astro.url in your

:
---
const canonical = Astro.url.pathname;
<link rel="canonical" href={`https://example.com${canonical}`} />
Enter fullscreen mode Exit fullscreen mode

SvelteKit: In your +layout.svelte or page component:

<svelte:head>
  <link rel="canonical" href="https://example.com{$page.url.pathname}" />
</svelte:head>
Enter fullscreen mode Exit fullscreen mode

Embedding these tags at build or render time ensures crawlers always see the correct URL.

3rd Party Scripts

Astro components support interactive elements through standard HTML <script> tags. For Next.js, next/script enhances the HTML <script> element to optimize the fetching and execution of additional scripts.

Structured Data

In a Next.js project, you can manage structured data (JSON‑LD) just as you would any other page head content—by injecting a <script type="application/ld+json"> block via the built‑in

component or a helper library.

Here are a couple of ways you can do it:

Assemble your schema object. In your page’s data‑fetching function (e.g. getStaticProps or getServerSideProps), build the JSON‑LD payload.

// pages/products/[slug].js
export async function getStaticProps({ params }) {
  const product = await fetchProduct(params.slug);
  const schema = {
    "@context": "https://schema.org/",
    "@type": "Product",
    name: product.name,
    image: product.images,
    description: product.description,
    sku: product.sku,
    brand: { "@type": "Brand", name: product.brand },
    offers: {
      "@type": "Offer",
      priceCurrency: "USD",
      price: product.price,
      availability: "https://schema.org/InStock",
    },
  };
  return { props: { product, schema } };
}
Enter fullscreen mode Exit fullscreen mode

Inject via <Head>. In your page component, serialize and insert the JSON‑LD inside the <Head> tag so it’s rendered on the client and server.

import Head from 'next/head';
export default function ProductPage({ product, schema }) {
  return (
    <>
      <Head>
        <title>{product.name} -- My Shop</title>
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
        />
      </Head>
      {/* ...rest of your page... */}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Use a helper library (optional). For larger sites, next-seo simplifies SEO—including structured data—by letting you define your schema in a centralized config.

After deployment, test each page in Google’s Rich Results Test or Schema Validator to ensure your JSON‑LD is correct and being picked up by crawlers.

If you have repeated schema across many pages (e.g., site­wide Organization or Breadcrumb data), consider adding a custom _document.js or a top‑level layout component that injects those elements globally.

As for Astro, you treat structured data much the same way you would in any static‐site setup, including the above one with Next.js.

Your CMS or e-commerce platform defines structured data. Crystallize, for example, utilizes content modeling to describe all the necessary product information for your product page (including structured data, LLM summary, etc.). Your chosen frontend can then request and display this information via the API, including in Schema.org format.

Images Handling

Optimizing images can be achieved through various methods. Many frontend frameworks offer built-in solutions:

  • Next.js: The <Image/> component handles rendering and optimizes images for different viewports by incorporating lazy loading, responsiveness, resizing, and optimized file size and format by default.
  • Svelte: Vite's built-in image handling is often the most effective approach.
  • Astro: The <Image /> Astro component optimizes both local and configured remote images. Alternatively, consider using a third-party service, such as Cloudflare Image Optimization.

Finally, some CMS and e-commerce platforms like Crystallize provide image optimization as part of their service. For instance, Crystallize automatically compresses and converts uploaded images to AVIF and WebP formats, resizing them into multiple resolutions (e.g., 100px, 300px, up to the original image's maximum width). The @crystallize/react-image npm package then assists with rendering responsive and fast images.

Conclusion

By surfacing robots.txt, sitemap.xml, custom error pages, 301 redirects, and canonical tags directly in your headless frontend or hosting layer, you maintain tight control over SEO without sacrificing the flexibility of modern frameworks.

Continue with the original and complete version of the guide at the Crystallize blog 👉eCommerce SEO Guide: How To Drive Organic Traffic In 2025?

Top comments (0)