301 🌐 HTTP

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

  1. Inspect the chain: curl -sIL https://example.com/page
  2. Redirect straight to the final URL — one hop, not several
  3. Behind Cloudflare/a load balancer, base HTTPS redirects on X-Forwarded-Proto to avoid loops
  4. Use 308 instead of 301 for API endpoints that receive POST/PUT
  5. Clear browser cache or test in a private window after changing redirects

Detailed fix by platform

Nginx

  1. 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

  1. .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

  1. "Too many redirects" with SSL mode Flexible + an origin HTTPS redirect is a loop — set SSL/TLS mode to Full (strict).

WordPress

  1. Check siteurl and home in 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

  1. Chain — curl -sIL — how many hops, and where do they go?
  2. Loop — Does any URL appear twice? (ERR_TOO_MANY_REDIRECTS)
  3. Layer — Which layer issues each redirect: CDN, web server, app?
  4. Method — Is a POST being redirected? Use 307/308
  5. 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.