301 Moved Permanently

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

Code
301
Reason phrase
Moved Permanently
Class
3xx Redirection
Defined in
RFC 9110 section 15.4.2
Cacheable by default
Yes

What it means

Redirection: the client has to take another step, usually following a Location header, to complete the request.

When a server should send it. When a URL has moved for good: a renamed page, a merged article, http to https, an old domain to a new one. Point straight at the final address, since every extra hop costs a round trip, and keep the redirect in place for at least a year.

Common causes. A site moved to HTTPS or a new domain, or a page was renamed.

What to do. Update links and bookmarks. Search engines pass ranking to the new address. Browsers cache 301s hard, so test redirects with 302 first. Browsers may change a POST to GET when following it; use 308 to keep the method.

How clients and crawlers treat it

Browsers and HTTP clients. Browsers follow it automatically and cache it, often with no expiry, so a mistaken 301 can stick in visitors' browsers long after you remove it. Browsers change a POST into a GET when following a 301, for historical reasons; use 308 when the method must survive.

Google Search. A strong signal that the redirect target should be the canonical URL. Googlebot follows up to 10 redirect hops. Source: Google Search Central, How HTTP status codes, and network and DNS errors affect Google Search.

Caching. Cacheable by default (RFC 9110 section 15.1). With no Cache-Control or Expires header, a cache may still store it and reuse it for a heuristic period, often 10% of the time since Last-Modified, so set Cache-Control explicitly if that is not what you want.

Example response

HTTP/1.1 301 Moved Permanently
Location: https://example.com/new-page

Moved Permanently

How to send 301

nginx
location = /old-page {
    return 301 https://example.com/new-page;
}
Apache (.htaccess)
Redirect 301 /old-page https://example.com/new-page
PHP
header('Location: https://example.com/new-page', true, 301);
exit;
Node.js
res.writeHead(301, { Location: 'https://example.com/new-page' });
res.end();
Python (Flask)
from flask import redirect

return redirect('https://example.com/new-page', code=301)

Change the paths to suit. In nginx, add_header needs always to apply to error responses.

Related codes

All status codes · All 3xx codes

Names and numbers from the IANA HTTP Status Code Registry. Google Search behaviour as documented by Google Search Central.