DEVESSENTIALS

HTTP 301 Moved Permanently

A 301 says the old URL is gone for good and clients should use the new one from now on. Browsers follow the Location header automatically. Because 301 is cacheable by default, they may remember the redirect and skip the old URL next time, possibly for a very long time if there's no Cache-Control.

3xx · RedirectionRFC 9110 §15.4.2

301 Moved Permanently

The resource has a new permanent URL, given in the Location header.

Who fixes it: Server config. Clients just follow it. Site owners decide where it points, whether it's permanent, and how long it stays in place.

What 301 means

RFC 9110 notes that, for historical reasons, user agents may change the method from POST to GET when following a 301, and browsers do exactly that. If a redirected request must keep its method and body (an API POST), use 308.

It's the standard redirect for domain moves, HTTP to HTTPS, www normalisation and renamed pages.

Source: RFC 9110 §15.4.2.

When to use a 301

  • A page or route was renamed: /blog/old-slug to /blog/new-slug.
  • HTTP to HTTPS, and www to the apex domain or the reverse. Pick one canonical host.
  • The site moved to a new domain.
  • Trailing slash normalisation (/docs/ to /docs).
  • Not for temporary maintenance, A/B tests, geo or login redirects. Those are 302 or 307, because the original URL is still the real one.

How to set up and check a 301

nginx

return 301 is cheaper and clearer than rewrite. $request_uri keeps the path and query string.

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

# Inside the HTTPS server block: one renamed page
location = /old-page {
    return 301 /new-page;
}

Express

res.redirect() defaults to 302, so pass 301 explicitly.

app.get('/old-page', (req, res) => res.redirect(301, '/new-page'));

Next.js and Vercel

In next.config, permanent: true produces a 308, not a 301. Search engines treat them the same, but if you specifically need a 301 (for very old clients), use statusCode instead of permanent.

// next.config.ts
const nextConfig = {
  async redirects() {
    return [
      { source: '/old-page', destination: '/new-page', permanent: true }, // 308
      { source: '/legacy', destination: '/new', statusCode: 301 },        // explicit 301
    ];
  },
};
export default nextConfig;

Find redirect chains with curl

-L follows redirects and -I prints each hop's headers. Two hops like this should be collapsed into one redirect straight to the final URL.

curl -sIL http://example.com/old-page | grep -iE '^(HTTP|location)'

HTTP/1.1 301 Moved Permanently
Location: https://example.com/old-page
HTTP/2 301
location: https://example.com/new-page
HTTP/2 200

Headers that matter with 301

Location
Where to go. Absolute or relative URLs are both valid.
Cache-Control
Browsers can cache a 301 indefinitely. While you're testing, send Cache-Control: max-age=3600 so a mistake doesn't stay stuck in visitors' browsers.

301 vs similar status codes

  • 308 Permanent Redirect: Same meaning, but the method and body must be kept. Prefer 308 for APIs; either is fine for page moves.
  • 302 Found: Temporary. Search engines keep the old URL indexed and browsers don't cache the redirect by default.
  • 307 Temporary Redirect: Temporary and keeps the method. The temporary counterpart of 308.

SEO impact of 301

A 301 is a strong signal to Google that the target should be the canonical URL, and ranking signals are consolidated on it. Google's site-move guidance is to keep redirects for at least a year. Redirect each old URL to its closest equivalent, not the homepage (mass redirects to the homepage are often treated as soft 404s). Avoid chains, and update internal links and the sitemap to point at the final URL.

Related status codes

Looking for another code? Search the full HTTP status codes list, or read which code your API should return in the REST API status codes guide.

Learn more

Browse all developer guides →

Frequently Asked Questions

How long do browsers cache a 301 redirect?▾

Without Cache-Control or Expires headers, browsers may cache a 301 indefinitely, until the cache is cleared. If you might change the redirect later, send an explicit Cache-Control max-age. To clear it on your own machine, clear the browser cache or test in a private window.

301 or 308: which should I use?▾

Both are permanent and search engines treat them the same. The difference is that 308 forbids changing the method, so a POST stays a POST. Use 308 for API endpoints and either one for ordinary page moves. Next.js permanent redirects use 308 by default.

Does a 301 redirect lose SEO value?▾

Not in any meaningful way. Google has said 3xx redirects don't lose PageRank. What hurts is redirecting to an irrelevant page, long redirect chains, or removing the redirect too early.