301 Moved Permanently
When a resource has permanently moved to a new URL, the server responds with 301 Moved Permanently.
The response is cacheable by default. To override this behavior, include appropriate Cache-Control headers.
Usage
When the 301 Moved Permanently status code is received, clients are expected to update stored links to the new URI. The new URI is specified by the Location header and is used by the client for automatic redirection.
This response is common when servers migrate to a new domain or reorganize their internal file structure with no plan to revert to the former URI.
Keep 301 redirects active for at least one year, ideally indefinitely. Google recognizes 301s quickly but full signal transfer takes weeks. Browsers cache 301 responses aggressively based on Cache-Control directives. Without explicit cache headers, the redirect persists in browser cache indefinitely. Removing a 301 server-side does not clear cached redirects in browsers.
301 vs 302
The pair separates a permanent statement from a temporary one, and the consequences run deeper than the wording suggests.
301 Moved Permanently declares the old address finished. Search engines transfer ranking signals to the target and replace the old address in the index, and clients cache the response by default, so a browser holding a stored 301 stops requesting the original address entirely. Undoing an incorrect 301 means waiting out caches nobody controls.
302 declares a detour. The original address stays the real home, keeps its ranking signals, and remains the address in the index. Responses are not cacheable by default, so a change takes effect on the next request.
The wrong choice fails in both directions. Marking a genuine site move as 302 leaves signals stranded on addresses no longer serving content, and the migration stalls. Marking a maintenance page or a short campaign as 301 hands the target the original address's standing and caches the decision in browsers.
The test is whether the original address is expected back in service. Anything returning is 302. Anything retired is 301.
301 vs 308
Both codes announce a permanent move, and the difference is what happens to the request method.
301 Moved Permanently leaves method handling loose in the HTTP specification, while the Fetch Standard requires browsers to convert a redirected POST to GET and drop the body. Non-browser clients vary, which is the unreliability 308 exists to remove: 308 forbids the conversion, so a POST stays a POST with the body intact.
For ordinary page moves the distinction rarely matters, since the traffic being redirected is GET traffic and both codes transfer ranking signals identically. 301 remains the common choice there, with the longest history of consistent handling across clients, crawlers, and intermediaries.
For API endpoints the distinction decides whether the request survives. Redirecting a POST endpoint with 301 risks the body disappearing on the way to the new address, and 308 is the code carrying the request through unchanged.
Example
The client requests an HTML resource. The server responds with 301 Moved Permanently and includes the new URI in the Location header. A message body suggests updating bookmarks, though modern browsers redirect immediately.
Request
GET /news.html HTTP/1.1
Host: www.example.re
Response
HTTP/1.1 301 Moved Permanently
Location: https://www.example.re/feeds/news.html
Content-Type: text/html; charset=UTF-8
Content-Length: 149
<h1>The Newsfeed has moved</h1>
<body>
The newsfeed has moved permanently to
<a href=/feeds/news.html>here</a>.
Please update your bookmarks.
</body>
Code references
.NET
HttpStatusCode.MovedPermanently
Rust
http::StatusCode::MOVED_PERMANENTLY
Rails
:moved_permanently
Go
http.StatusMovedPermanently
Symfony
Response::HTTP_MOVED_PERMANENTLY
Python3.5+
http.HTTPStatus.MOVED_PERMANENTLY
Java
java.net.HttpURLConnection.HTTP_MOVED_PERM
Apache HttpComponents Core
org.apache.hc.core5.http.HttpStatus.SC_MOVED_PERMANENTLY
Angular
@angular/common/http/HttpStatusCode.MovedPermanently
See also
- RFC 9110: HTTP Semantics
- WHATWG Fetch Standard: HTTP-redirect fetch
- Google: HTTP status codes and network errors
- Soft 404
- 308
- 302
- 307
- Redirects
- HTTP status codes