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.
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-slugto/blog/new-slug. - HTTP to HTTPS, and
wwwto 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 200Headers 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=3600so 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
308Permanent Redirect
Permanently moved: repeat this and future requests at the Location URL without changing the method.
302Found
The resource is temporarily at a different URL; keep using the original URL for future requests.
307Temporary Redirect
Temporarily redirected: repeat the same request, with the same method and body, at the Location URL.
404Not Found
The server has no current representation of the target resource, or won't say whether one exists.
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
HTTP Status Codes for REST APIs: 200 vs 201 vs 204, 400 vs 422 vs 429
A practical guide to choosing the right HTTP status code in REST API design. Covers the most confusing pairs: 200 vs 201, 400 vs 422, 401 vs 403, and when to use 503 vs 429.
REST API Design Best Practices: A Practical Guide
The conventions that make REST APIs intuitive and consistent. Covers URL design, HTTP methods, status codes, versioning, pagination, and error responses.
CORS Explained: Why It Happens and How to Fix It
CORS errors happen when a browser blocks a request to a different origin. Learn how the Same-Origin Policy works, what preflight requests are, and how to configure CORS correctly in Express, FastAPI, and Nginx.
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.