Vercel

Redirects, Rewrites, Headers, And Clean URLs

Routing configuration controls what happens when a request reaches your Vercel project. A user asks for a path, and the platform decides whether to serve a file, run a function, redirect the browser, rewrite the request internally, attach headers, or return a 404. Small routing rules can have large effects, so they deserve careful review.

This lesson expands on the vercel.json overview. Redirects, rewrites, headers, clean URLs, SPA fallbacks, and 404 behavior are all related, but they solve different problems. A redirect tells the browser to use a different URL. A rewrite serves a different resource without necessarily changing the visible URL. Headers add metadata to responses. Clean URLs make static paths nicer. A 404 tells the user the requested resource does not exist.

The mistake to avoid is treating every routing problem as the same kind of rule. Use the smallest tool that expresses the behavior you actually need.

Redirects Change The URL

A redirect sends the browser to a different address. The address bar changes. Redirects are right when the canonical location of a resource has changed.

Use redirects for:

old page path -> new page path
http domain -> https domain
apex domain -> www domain, or the reverse
old campaign URL -> current landing page
renamed documentation section -> new section

A permanent redirect is a long-term promise. Browsers and search engines may remember it. Use it when you are confident the move is durable. A temporary redirect is safer for experiments, short campaigns, maintenance windows, and uncertain migrations.

A conceptual redirect looks like:

{
  "redirects": [
    {
      "source": "/old-guide",
      "destination": "/guides/new-guide",
      "permanent": true
    }
  ]
}

After adding a redirect, test both the old and new paths. The old path should move to the intended destination. The new path should load successfully. A redirect to a missing page is not a completed migration.

Rewrites Keep The URL

A rewrite serves content from a different destination while the browser can keep the original visible URL. Rewrites are useful when the public URL and internal implementation are intentionally different.

Use rewrites for:

single-page app route fallback
internal route reshaping
proxying to an API endpoint when appropriate
serving a framework route behind a cleaner URL
migration where visible URLs must stay stable

For example, a single-page app may need /settings to return the app shell instead of a static 404. The browser URL remains /settings, and client-side JavaScript handles the route after loading.

Rewrites can also create confusion. A user sees one URL while the platform serves another resource. If debugging is hard, document the rule. If security depends on hiding the destination, the design is weak. Rewrites are routing tools, not access controls.

Redirect Or Rewrite

Choose based on what the user should observe.

Use a redirect when the user should end up at a new canonical URL:

/products-old -> /products

Use a rewrite when the public URL should stay stable while implementation changes behind it:

/docs/latest -> /docs/v3/index.html

Ask these questions:

Should the address bar change?
Should search engines learn a new canonical URL?
Should bookmarks update over time?
Is this hiding implementation or moving the resource?
Will logs and analytics be easier to understand with a redirect?

If the answer is about a permanent public move, use a redirect. If the answer is about internal serving behavior, consider a rewrite.

Headers Add Response Metadata

Headers are not routes, but they shape how browsers, caches, and clients handle responses. Vercel supports configuring headers for matching paths.

Headers can be used for:

security behavior
cache behavior
content type protections
cross-origin policy
custom metadata for APIs

A small security header example:

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Content-Type-Options", "value": "nosniff" }
      ]
    }
  ]
}

Be careful with broad headers. A strict Content Security Policy can break scripts, styles, images, analytics, and third-party widgets. Aggressive cache headers can keep old files alive. Cross-origin headers can break API clients or browser features. Add headers deliberately and test the pages they affect.

Cache Headers

Cache headers deserve special attention. Static assets with fingerprinted names can often be cached for a long time. HTML documents and unversioned JSON usually need shorter caching because users expect them to change.

A fingerprinted asset might be:

/app.8f3a91.js
/styles.14a2c0.css

Those filenames change when content changes. Long caching is safer because a new build creates new URLs.

An HTML file such as /index.html usually should not be cached as aggressively. If a browser caches old HTML, users may not see a new release or may request outdated asset URLs.

Before adding cache headers, identify the file type and update strategy. Performance improvements that cause stale production behavior are not wins.

Security Headers

Security headers can reduce browser-side risk, but they are not substitutes for secure application design. For example, a Content Security Policy can limit which scripts run, but it does not fix an API that exposes private data. X-Content-Type-Options can reduce MIME sniffing risk, but it does not fix serving the wrong file.

Add security headers in stages. Start with well-understood protections, test in Preview, and inspect browser console warnings. For a strict Content Security Policy, consider report-only modes and a careful rollout for production apps.

A security header change should be reviewed by someone who understands the app's scripts, styles, fonts, images, analytics, embeds, and API calls. A copied header block can silently block important behavior.

Clean URLs

Clean URLs make static paths easier to read. Instead of /about.html, users can visit /about. This is often desirable for static sites and documentation.

Clean URLs are a presentation choice and a routing behavior. They affect links, redirects, canonical URLs, analytics, and SEO. Choose a style and apply it consistently.

A small static site may prefer:

/about
/contact
/pricing

Instead of:

/about.html
/contact.html
/pricing.html

If a site has already launched with .html URLs, add redirects before removing or changing them. Users and search engines may still request the old paths.

SPA Fallback Routes

Single-page apps often use client-side routing. A user may open /settings, but the server only has one built HTML shell. Without a fallback, refreshing /settings can return a 404 even though clicking to Settings inside the app works.

A fallback rewrite can send app routes to the HTML shell. The exact rule depends on the app and framework. Be careful not to rewrite API routes, asset paths, or real 404s unintentionally.

A good SPA fallback plan identifies:

Which paths belong to the SPA?
Which paths are static assets?
Which paths are API routes?
Which missing paths should return 404?
Which route should serve the app shell?

Do not use a catch-all rewrite that hides every error. Real missing files should still be diagnosable.

404 Handling

A 404 is not always a failure. It is the correct response when a resource does not exist. Custom 404 pages can help users recover, but routing rules should not turn every unknown path into a success.

For content sites, a missing page should usually return a real 404 so search engines and monitoring understand the page is absent. For an SPA, some app routes may need a fallback, but missing assets and invalid API paths should still fail clearly.

Test 404 behavior intentionally:

Known page loads.
Old page redirects.
SPA route refresh works.
Missing asset returns an error.
Missing content page shows 404.
Missing API route does not return the homepage.

This catches over-broad rewrites early.

Testing Routing Rules

Routing changes need URL-by-URL tests. Keep a small table in the pull request:

/old-guide -> redirects to /guides/new-guide
/guides/new-guide -> loads 200
/app/settings -> loads SPA shell
/missing-page -> returns 404
/assets/app.js -> loads asset
/api/contact -> reaches API handler

Open the Preview Deployment and test each path. Check browser DevTools for status codes when needed. For production releases, test the production domain after merge because preview and production domains may differ.

Plan Route Changes Like Migrations

Treat routing work as a small migration, not as a cosmetic cleanup. URLs are part of your application's public interface. People bookmark them, search engines index them, clients paste them into project plans, emails point to them, and analytics reports group traffic by them. Changing a URL without a migration plan can break more than navigation.

Start by listing the current public paths. For a small site, this can be a handwritten checklist. For a larger site, export the top pages from analytics or crawl the current production domain. Mark each path as keep, redirect, rewrite, remove, or intentionally 404. That simple inventory prevents the common mistake of only testing the pages the developer remembers.

Next, decide the expected status code. A moved page should usually redirect. A removed page should usually return 404 or 410, depending on the app's policy. An app route inside a single-page app may return the shell with a 200 response. An API route should return an API-shaped response, not an HTML fallback. These choices should be explicit before the rule is written.

Preview Deployments are useful for this review because they let you test routing before the production domain changes. Share the preview with another developer or stakeholder and ask them to open the old links, the new links, and at least a few intentionally missing links. The important question is not only "does the happy path load?" It is also "do old paths fail or move in the way we expect?"

After release, keep the migration rules long enough for real users and search engines to catch up. Do not delete redirects immediately because the new page works on your machine. Old links can live in invoices, email campaigns, documentation, support tickets, and browser history for a long time. Removing redirects too soon creates avoidable support work.

For client or business projects, route changes should be reviewed with the same care as schema changes or payment changes. They can affect search visibility, ad campaigns, customer onboarding, legal pages, and support documentation. A small vercel.json diff can still be a production behavior change.

Common Mistakes

The first mistake is using a rewrite when a redirect is the correct public move.

The second mistake is creating redirect loops. Always test the destination.

The third mistake is applying cache headers too broadly.

The fourth mistake is using a catch-all rewrite that hides missing pages, assets, or API errors.

The fifth mistake is copying security headers without testing scripts, fonts, images, analytics, and embeds.

The sixth mistake is changing URL style without migration redirects.

Review Questions

  • What is the visible difference between a redirect and a rewrite?
  • When should a redirect be permanent?
  • Why can broad cache headers break releases?
  • What problem does an SPA fallback solve?
  • Why should missing API routes not return the homepage?
  • What URL tests should accompany a routing change?

Official References