Skip to content

Redirects on Vercel and Netlify

On Vercel and Netlify you don't touch a web server config. Redirects live in a file in your repository or in the dashboard, and the platform's edge network sends them. Here is how both work and where they differ.

At a glance

VercelNetlify
Config filevercel.json_redirects or netlify.toml
Default status308 (permanent defaults to true)301
Parameters:slug, :path*, regex:placeholder, * with :splat
Existing filesRedirects are checked before the file systemExisting files win unless you force with !
Domain redirectsProject Settings → DomainsAutomatic www/apex redirect, rules for aliases

Vercel: redirects in vercel.json

Place vercel.json in the root of your project. Each entry needs a source and a destination:

{
  "redirects": [
    { "source": "/about-us", "destination": "/about" },
    { "source": "/blog/:slug", "destination": "/news/:slug", "permanent": true },
    { "source": "/docs/:path*", "destination": "https://docs.example.com/:path*" },
    { "source": "/sale", "destination": "/offers", "permanent": false },
    { "source": "/legacy", "destination": "/", "statusCode": 301 }
  ]
}
  • permanent is optional and defaults to true, which sends 308. false sends 307.
  • With statusCode you pick the code explicitly (301, 302, 303, 307 or 308). Use it instead of permanent.
  • :slug matches one path segment, :path* any number of segments. Regular expressions work too, for example "/post/:id(\\d+)".
  • With has and missing you can match on the host, headers, cookies or query parameters.

Redirecting a host inside vercel.json looks like this:

{
  "redirects": [
    {
      "source": "/:path*",
      "has": [{ "type": "host", "value": "old.example.com" }],
      "destination": "https://example.com/:path*",
      "permanent": true
    }
  ]
}

If you deploy a Next.js app, you can also define the same rules in redirects() in next.config.js; see the Next.js guide. Keep them in one place. Vercel limits the number of routes in vercel.json; for very large lists, look at the bulk redirect options in the Vercel documentation or use middleware with a lookup table.

Implicit redirects on Vercel

Two options in vercel.json create redirects without an explicit rule:

{
  "cleanUrls": true,
  "trailingSlash": false
}

cleanUrls: true serves /about.html as /about and redirects the .html version with a 308. trailingSlash decides whether /about/ redirects to /about (false) or the other way round (true). If your own rules point to the other form, you get an extra hop.

Vercel: domain redirects in the dashboard

For www/non-www or an old domain you don't need any code. Add both domains under Project → Settings → Domains, then edit the domain that should redirect and choose Redirect to the main domain. You can select the status code; use 301 or 308 for a permanent move. The path and query string are kept, so www.example.com/pricing?plan=pro ends up at example.com/pricing?plan=pro. HTTP to HTTPS is handled automatically by Vercel with a 308.

Netlify: the _redirects file

The _redirects file must end up in the publish directory after the build (for many frameworks you put it in public/ or static/ so it is copied). Each line has the form from to [status][!]:

# Single page, 301 is the default
/about-us            /about

# Explicit status codes
/sale                /offers             302
/old-feed            /feed.xml           301

# Placeholders
/news/:year/:month/:slug   /blog/:slug   301

# Splat: everything below /blog/ keeps its rest path
/blog/*              /articles/:splat    301

# Query parameters
/store id=:id        /products/:id       301

# Old domain to new domain, forced
https://old.example.com/*   https://example.com/:splat   301!

# Rewrite (URL stays) and custom 404
/api/*               https://api.example.com/:splat   200
/*                   /404.html           404

Netlify reads the rules top to bottom and uses the first match. Put specific rules first and catch-all rules like /* at the end. The status 200 is not a redirect but a rewrite: the visitor stays on the original URL.

Shadowing and forcing with "!"

By default, a rule is ignored if a file exists at the requested path. This is called shadowing. If /about-us/index.html is still in your build, the rule /about-us /about never fires. There are two fixes: delete the old file, or add an exclamation mark to force the rule:

/about-us   /about   301!

Use forcing deliberately. A forced /* /index.html 200! for a single-page app would also rewrite your CSS and JavaScript files.

Netlify: redirects in netlify.toml

The same rules can live in netlify.toml in the repository root. The syntax is more verbose, but you can keep build settings and redirects together:

[[redirects]]
  from = "/blog/*"
  to = "/articles/:splat"
  status = 301

[[redirects]]
  from = "/about-us"
  to = "/about"
  status = 301
  force = true

[[redirects]]
  from = "/shop/*"
  to = "/de/shop/:splat"
  status = 302
  conditions = {Country = ["de", "at"]}

If you use both files, Netlify processes the rules from _redirects first, then those from netlify.toml. force = true corresponds to the !. The conditions block lets you redirect by country, language, role or cookie presence; use such rules sparingly for SEO, because Googlebot mostly crawls from the US and would only ever see one variant.

Domains on Netlify

When you set a primary domain, Netlify automatically redirects its www or apex counterpart to it, and HTTP to HTTPS once a certificate is active. Additional domain aliases, however, serve the same content by default. To redirect an alias such as an old domain, add a forced rule with the full URL, as in the https://old.example.com/* example above.

Which status code should you use?

  • Moved for good: 301 or 308. Both pass PageRank and are a strong signal for Google to index the destination.
  • Temporary: 302 or 307, e.g. for a campaign page or maintenance.
  • Removed without replacement: no redirect, but a 404 or 410. Redirecting everything to the home page is treated as a soft 404 by Google.

The details are in 301 vs. 302 and 307 vs. 308.

Testing after deploy

Both platforms apply redirects at the edge, so your local dev server may not show them. After a deploy, check a few URLs with the redirect checker: look for a single hop with the status code you expect, and watch out for an extra hop caused by trailing slashes or the www redirect. For long lists of old URLs, the bulk checker saves time. If you are moving a whole domain, follow the domain migration checklist.

Frequently asked questions

Why does Vercel send 308 instead of 301?

In vercel.json, permanent defaults to true, which means 308. It is the permanent redirect that keeps the request method; Google treats it like a 301. Set "statusCode": 301 if you need a 301.

Why is my Netlify redirect ignored?

Usually a file still exists at the old path (shadowing), or the _redirects file is not in the publish directory after the build. Remove the old file or force the rule with 301!. Also check that no earlier rule matches first.

What does :splat mean in Netlify?

:splat inserts whatever the * in the source matched. /blog/* /articles/:splat 301 sends /blog/2024/post to /articles/2024/post.

Is a 200 rule in Netlify a redirect?

No, it is a rewrite (proxy). The URL in the browser stays the same and the content comes from the destination. Search engines index the original URL.

  • .htaccess redirects on Apache

    Everything you need to redirect pages and whole domains with .htaccess on Apache: mod_alias vs. mod_rewrite, copy-ready rules for the common cases, and how to avoid loops and chains.

  • nginx redirects

    Copy-ready nginx configuration for 301 redirects: single pages, patterns, whole domains, HTTPS and www in a single hop, and hundreds of URLs with map.

  • Redirects on Cloudflare

    Cloudflare can answer redirects at the edge before a request ever reaches your server. Here is how Single Redirects, Bulk Redirects and Always Use HTTPS work, and how to keep them from fighting with your origin.

  • Redirects in WordPress

    WordPress gives you several ways to redirect a URL: a plugin, a rule in .htaccess or a few lines of PHP. This guide shows when to use which, and how to avoid the typical pitfalls with caching and HTTPS.

  • Redirects in Next.js

    Next.js has four places where you can redirect: the config file, middleware, server code in the App Router and the trailingSlash option. Each one sends different status codes by default, so it pays to know which is which.

  • Redirects on Microsoft IIS

    IIS gives you two ways to redirect: the built-in HTTP Redirect feature and the URL Rewrite module. Here is when to use which, with web.config examples you can copy.