301 Moved Permanently
All future requests should use the new URL provided in the Location header.
The resource has been permanently moved to a new URL.
What HTTP 301 Moved Permanently Means
The resource has been permanently moved to a new URL.
All future requests should use the new URL provided in the Location header.
Common Causes
- Website migration
- URL structure change
- Permanent content relocation
What Visitors Should Do
- Update bookmarks to new URL
- Browser should automatically redirect
Notes for Developers
- Update internal links to new URL
- Use for permanent URL changes
- Set Location header
301 vs 302 vs 307 vs 308
| Code | Permanent? | Method on redirect | Typical use |
|---|---|---|---|
| 301 | Yes | May change POST to GET | Moved pages, domain or HTTPS migrations |
| 302 | No | May change POST to GET | Short-term moves, login redirects |
| 303 | No | Always GET | Redirect after a form POST |
| 307 | No | Must keep method and body | Temporary moves of APIs or forms |
| 308 | Yes | Must keep method and body | Permanent moves of APIs or forms |
RFC 9110 allows clients to switch a POST to GET on a 301 or 302 for historical reasons, which is why 307 and 308 exist.
How to Set Up a 301 Redirect
# Nginx: redirect one path permanently
location = /old-page { return 301 /new-page; }
# Nginx: redirect a whole domain, keeping the path
server {
server_name old.example.com;
return 301 https://example.com$request_uri;
}
# Apache (.htaccess)
Redirect 301 /old-page /new-pageRedirect each old URL to its closest equivalent, update internal links to point to the final URL, and avoid redirect chains (A to B to C). Browsers cache 301s aggressively, so test redirects in a private window.
Returning a 301 Moved Permanently (Code Examples)
If you build APIs or web apps, here is how to send an HTTP 301 response and how to test for it:
Node.js (Express)
app.get('/resource', (req, res) => {
res.status(301).json({ error: 'Moved Permanently' });
});
Python (Flask)
@app.route('/resource')
def resource():
return jsonify(error='Moved Permanently'), 301
PHP
<?php
http_response_code(301);
header('Content-Type: application/json');
echo json_encode(['error' => 'Moved Permanently']);
Check the status with curl
curl -I https://example.com/resource
# Look for: HTTP/1.1 301 Moved Permanently
Frequently Asked Questions
What does HTTP 301 Moved Permanently mean?
All future requests should use the new URL provided in the Location header. In short: The resource has been permanently moved to a new URL.
How should I handle an HTTP 301 Moved Permanently response?
Update internal links to new URL. Use for permanent URL changes. Set Location header.
Official Specification
The 301 Moved Permanently status code is defined in RFC 9110 Section 15.4.2.