Sanity slug changes causing 404s: how to manage redirects in Next.js

By Nayan Kyada · · 6 min read

Part of The Sanity + Next.js Guide

If an editor renamed a page in Sanity and the old link now shows a 404, you have a slug redirect problem. It is one of the quietest ways a Next.js and Sanity site loses traffic: nothing errors in the build, the new page works, and the damage shows up weeks later in Search Console. This post covers the symptoms, what it costs to ignore, and the fix I use.

Symptoms of Sanity slug changes causing 404s

The pattern is almost always the same. Someone tidies up a title and slug for "better SEO", publishes, and the old URL stops resolving. Then you notice one or more of these:

  • Search Console shows a growing list of "Not found (404)" URLs that used to get impressions.
  • Newsletters, social posts and partner backlinks point to pages that now fail.
  • Organic traffic to one section drops a few weeks after a content cleanup, not on the day of the change.
  • Your analytics show 404 landing pages with real referrers.
  • After a migration or redesign, nobody can say which old URLs were supposed to be mapped.

If you recently moved from WordPress or Gatsby, the same problem applies at larger scale. I covered the migration side in WordPress to Next.js migration SEO, and the redesign side in website redesign without losing SEO.

What it costs to ignore

A 404 on a URL with backlinks throws away the authority those links built. Google will keep retrying the old URL for a while, then drop it, and the new URL starts from zero. For a marketing site where a handful of pages carry most of the traffic, one careless slug edit on a top page can matter more than months of content work.

There is also a trust cost. Editors who get blamed for a traffic dip stop touching slugs, and then the content never gets cleaned up. A redirect system fixes the technical problem and lets editors work freely.

Why Sanity does not handle this for you

A Sanity slug is just a field. When it changes, the document keeps its ID but the URL your frontend builds from it changes, and nothing records the old value. Next.js has no memory either. So the old path simply has no route.

Some teams solve this with a slug history array on each document. I avoid that: it clutters every schema and makes querying harder. A single dedicated redirect document type is easier to audit, export and hand over.

Fix step 1: find the URLs that are already broken

Before building anything, collect the damage. Export the 404s from Search Console, check your analytics for 404 landing pages, and crawl the site with any crawler that lists inbound links. Cross-check the list against current slugs in Sanity. If you have a sitemap from before the change, diff it against the current one. Each old URL needs a destination, and some should go to the closest relevant page rather than the homepage. Redirecting everything to the homepage is treated by Google as a soft 404 in many cases.

Fix step 2: add a redirect document type

Give editors a simple type they can use in Studio. Validation matters here, because a redirect loop is worse than a 404.

// sanity/schemaTypes/redirect.ts
import { defineField, defineType } from "sanity";
 
export const redirect = defineType({
  name: "redirect",
  title: "Redirect",
  type: "document",
  fields: [
    defineField({
      name: "source",
      type: "string",
      description: "Old path, for example /blog/old-slug",
      validation: (rule) =>
        rule.required().custom((value) =>
          value?.startsWith("/") ? true : "Must start with /"
        ),
    }),
    defineField({
      name: "destination",
      type: "string",
      description: "New path or full URL",
      validation: (rule) =>
        rule.required().custom((value, context) =>
          value === (context.document as { source?: string })?.source
            ? "Destination cannot equal source"
            : true
        ),
    }),
    defineField({
      name: "permanent",
      type: "boolean",
      initialValue: true,
    }),
  ],
});

This blocks the simplest loop. Longer chains, where A goes to B and B goes back to A, need a periodic check, which I handle with a GROQ query that lists destinations that also appear as sources.

Fix step 3: serve the redirects from Next.js

The simplest reliable route is to read the documents inside next.config.ts and return them from the redirects function. It runs at build time, so redirects are applied at the routing layer before any page renders.

// next.config.ts
import type { NextConfig } from "next";
import { createClient } from "next-sanity";
 
const client = createClient({
  projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
  dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!,
  apiVersion: "2025-01-01",
  useCdn: false,
});
 
type RedirectRow = {
  source: string;
  destination: string;
  permanent: boolean | null;
};
 
const nextConfig: NextConfig = {
  async redirects() {
    const rows = await client.fetch<RedirectRow[]>(
      `*[_type == "redirect" && !(_id in path("drafts.**"))]{source, destination, permanent}`
    );
    return rows.map((row) => ({
      source: row.source,
      destination: row.destination,
      permanent: row.permanent ?? true,
    }));
  },
};
 
export default nextConfig;

Two caveats. First, sources are treated as route patterns, so paths containing characters like parentheses or colons need escaping. Second, because this runs at build time, a new redirect only goes live after a deploy. I connect a Sanity webhook to a Vercel deploy hook for the redirect document type only, so editors get live redirects in a couple of minutes without rebuilding on every blog edit. If you already use webhooks for revalidation, the setup is similar to Sanity webhooks with Next.js ISR revalidation.

If you have thousands of redirects, a config array gets heavy. At that scale I would move the lookup into the request proxy layer backed by a fast key-value store, but most marketing sites never get there.

Fix step 4: stop the problem recurring

Redirects fix the past; process fixes the future.

  • Add slug validation so editors cannot publish clashing paths. I wrote about this in Sanity slug validation.
  • Make the slug field read-only after first publish for high-traffic document types, and require a redirect entry to unlock it.
  • Add a short rule to your editor guide: change a slug only with a redirect.
  • Keep a monthly habit of checking Search Console for new 404s.

If pages are missing from Google rather than 404ing, that is a different diagnosis, covered in Next.js pages not indexed by Google.

How to verify it worked

Request an old URL with curl and check you get a single 308 or 301 straight to the final destination, not a chain. Then re-crawl the list of old URLs and confirm every one resolves with no more than one hop. Finally, in Search Console, use the validate fix option on the 404 report and watch the count fall over the next few weeks.

What to ask a developer

  • How will you map every old URL, and where does that list live afterwards?
  • Can editors add redirects themselves, and what stops a loop?
  • How quickly does a new redirect go live?
  • Will you check for redirect chains after launch?

This is work I do as part of a Performance & Sanity audit, from $800, and it is often the cheapest traffic recovery on a site that has been edited for a year or two. Details are on the services page.

Frequently asked questions

Why do I get 404s after an editor changes a slug in Sanity?

The old URL no longer matches any document, so Next.js returns a 404 and Google eventually drops the page. Sanity does not create redirects on its own, so you have to store and serve them yourself.

Should I use 301 or 302 redirects for changed Sanity slugs?

Use a permanent redirect (301 or 308) when the old URL is never coming back, because that passes ranking signals to the new URL. Use a temporary one only for short-lived moves like a campaign.

Can I manage redirects from Sanity Studio instead of editing next.config?

Yes. Create a redirect document type with source, destination and permanent fields, then have Next.js read those documents and redeploy or revalidate when they change. Editors can then add redirects without a developer.