HTTP 301 Moved Permanently
The resource has a new permanent URL; clients and search engines should use the address in the Location header from now on.
Meaning
A 301 is not an error by itself — it is the correct way to move a page. Problems start when redirects loop, chain through several hops, point to the wrong place, or turn a POST into a GET.
Search engines transfer ranking signals to the new URL, and browsers cache 301s aggressively, so a wrong 301 can stick around in users’ browsers even after you fix the server.
Common causes
- Site moved to HTTPS or to www / non-www
- Page slug or URL structure changed
- Redirect loop: http→https and https→http rules fighting (common behind proxies/CDNs)
- Trailing-slash redirects added by the framework or web server
- Old cached 301 in the browser after the rule was removed
- POST requests become GET after a 301 (use 308 to keep the method)
⚡ Quick fix
- Inspect the chain:
curl -sIL https://example.com/page - Redirect straight to the final URL — one hop, not several
- Behind Cloudflare/a load balancer, base HTTPS redirects on
X-Forwarded-Prototo avoid loops - Use 308 instead of 301 for API endpoints that receive POST/PUT
- Clear browser cache or test in a private window after changing redirects
Detailed fix by platform
Nginx
- Force HTTPS + non-www in one hop:nginx
server { listen 80; server_name example.com www.example.com; return 301 https://example.com$request_uri; } server { listen 443 ssl; server_name www.example.com; return 301 https://example.com$request_uri; }
Apache
- .htaccess, proxy-aware HTTPS redirect:apache
RewriteEngine On RewriteCond %{HTTPS} off RewriteCond %{HTTP:X-Forwarded-Proto} !https RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Cloudflare
- "Too many redirects" with SSL mode Flexible + an origin HTTPS redirect is a loop — set SSL/TLS mode to Full (strict).
WordPress
- Check
siteurlandhomein Settings → General (or wp_options) match the canonical https URL.
Code examples
Trace a redirect chain
bash
curl -sIL http://example.com/blog | grep -iE '^(HTTP|location)'
# HTTP/1.1 301 → https://example.com/blog
# HTTP/2 301 → https://example.com/blog/
# HTTP/2 200 ← two hops: make the first redirect point straight to /blog/How to diagnose
- Chain — curl -sIL — how many hops, and where do they go?
- Loop — Does any URL appear twice? (ERR_TOO_MANY_REDIRECTS)
- Layer — Which layer issues each redirect: CDN, web server, app?
- Method — Is a POST being redirected? Use 307/308
- Cache — Retest in a private window — 301s are cached
🧠 Still stuck? Analyze your error
Paste the full message, response headers or stack trace — we'll detect the platform and point to the most likely cause.
Was this page helpful?
Report a correction or suggest an improvement
Last updated 2 Oct 2026