A redirect is a server saying "not here, go there". The browser follows without you noticing. They are essential whenever addresses change: moving to HTTPS, adding or removing www, renaming a page, merging two sites.
Done well, a redirect is invisible and keeps everything that was attached to the old address: the visitors who bookmarked it, the links other sites have pointed at it for years, and the standing it has with search engines. Done badly, it produces a slow site, a lost ranking or the browser error that says the page "redirected you too many times".
Most of the trouble comes from the same few places: choosing the wrong code, stacking several redirects on top of one another, and having more than one part of the system try to do the job. This article takes each in turn.
What actually happens on the wire
A redirect is an ordinary HTTP response. Instead of the page, the server sends a status code in the 300 range and a Location header naming the new address. The browser reads the header, and immediately makes a second request to that address. The visitor sees the address bar change and the final page load.
$ curl -I http://example.com/old-page
HTTP/1.1 301 Moved Permanently
Location: https://example.com/new-page
Content-Length: 0
That tiny exchange is the whole mechanism. Everything else is about which code to choose and where to put the rule. Note that the redirect happens before any page content is sent, so a visitor never downloads the old page. It also happens per request: the browser must make a new round trip to the server, which costs a small amount of time even when the server is very close.
The codes
A 301 means permanent, and search engines transfer their records to the new address. A 302 means temporary, so they keep the old one. The newer 308 and 307 mean the same as 301 and 302 but promise that the request method will not change, which matters for form submissions. For ordinary page moves, 301 is the right choice. Using 302 for a permanent move is one of the most common mistakes, and it can leave the old address in search results for a long time.
| Code | Meaning | Method preserved? | Typical use |
|---|---|---|---|
| 301 | Moved permanently | Not guaranteed; a POST may turn into a GET | HTTP to HTTPS, renamed pages, merged sites |
| 302 | Found, temporary | Not guaranteed | Short-lived campaigns, maintenance pages, A/B tests |
| 307 | Temporary redirect | Yes | Temporary moves where a form post must stay a post |
| 308 | Permanent redirect | Yes | Permanent moves of API endpoints or form targets |
The method point is easy to skim past. Imagine a checkout form that sends its data by POST to http://example.com/pay. A 301 or 302 to the HTTPS address can legitimately turn the POST into a GET, and the data is lost. A 307 or 308 tells the browser to repeat the same POST with the same body. For links and pages that people visit, you will not see the difference, which is why 301 and 302 remain the everyday pair. Browsers also cache 301 and 308 aggressively, sometimes with no expiry, which brings up the first warning.
A full list of status codes is in the status code reference.
Chains and loops
A chain happens when one redirect leads to another: http://example.com to https://example.com to https://www.example.com to https://www.example.com/home/. Each hop costs a round trip, and long chains can lose search value. Aim for one hop from any old address to the final one. A loop is a chain that goes back to its start, and browsers give up with a "too many redirects" error.
Chains grow by accretion. Someone adds the HTTPS rule in year one, someone else adds the www rule in year three, and a plugin adds a trailing slash rule in year five. Each rule was correct alone. Together they send the plain, non-www address on a four-hop trip, and each hop is a fresh opportunity for something to go wrong. On a slow mobile connection every hop can add a few hundred milliseconds before a byte of the page arrives.
Loops are usually caused by two rules disagreeing. One says "always use www", another (often the CDN or the application) says "always drop www". Each redirect undoes the other. Another classic: a site behind a proxy that terminates HTTPS, whose own web server sees plain HTTP and keeps redirecting to HTTPS, because it does not know the visitor already used it. The fix there is to tell the application to trust the forwarded protocol header.
Where redirects live
In the web server configuration or .htaccess, in the application (WordPress plugins), at the CDN, and at the registrar or DNS host for domain-level forwarding. Problems arise when two of these layers each try to enforce their own rule. Pick one layer to be in charge of each redirect.
As a guide, for a typical shared hosting site:
- HTTP to HTTPS and www to non-www (or the reverse) belong in the web server configuration, because it runs before PHP starts and costs almost nothing.
- Redirects for individual renamed pages suit the web server as well, or a redirect plugin if you edit them often and have no file access.
- Redirects from an old domain to a new one belong at the old domain's host, with a rule that preserves the path, so
old.example/alands onnew.example/a, not on the home page. - Registrar-level forwarding is a convenience for parked names. It is often a 302 and often does not support HTTPS on the source domain, so it is a poor choice for anything with real traffic.
An Apache example, in .htaccess, that sends everything to the HTTPS, non-www form in one hop:
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteRule ^ https://example.com%{REQUEST_URI} [L,R=301]
The same logic in Nginx uses one small server block for the unwanted forms and returns the code directly, which is easier to read:
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
If you would rather not write rules by hand, the redirect generator produces the common cases for Apache and Nginx.
A worked example: tidying a five-year-old site
A village cricket club has had the same website for years. It started on plain HTTP, moved to HTTPS with a plugin, later changed the preferred address to the www form, and recently renamed its fixtures page. A curl -IL http://clubname.example/fixtures shows five lines starting with HTTP/1.1 301 before the final 200.
- List every redirect actually happening. Reading the
Locationheader on each hop shows the plugin handles HTTPS, the server handles www, and a separate rule handles the renamed page. - Choose the final form:
https://www.clubname.example/. - Move the HTTPS and www rules to the server configuration as a single rule, and disable the plugin's HTTPS redirect.
- Rewrite the fixtures rule so its target is already in the final form, with HTTPS and www, instead of a relative path that lets the other rules fire afterwards.
- Re-test with
curl -ILfrom each of the four starting forms: plain and secure, with and without www.
Afterwards each of the four forms reaches the final page with exactly one 301. The change took twenty minutes, and the page started loading faster for every visitor who typed the address without the scheme.
Checking your work
Run curl -IL http://example.com and read each hop. You want exactly the final destination you intend, with a 301 on every step that is meant to be permanent. Test the old addresses that matter most, such as popular pages that have been linked from elsewhere.
A compact view of just the status and destination for each hop:
curl -sIL -o /dev/null -w "%{http_code} %{url_effective}\n" http://example.com/old-page
That prints only the final result, so for the full chain keep -I -L and read the output, or add --max-redirs 5 to stop a loop quickly. Also test with the trailing slash and without it, with a query string such as ?utm_source=newsletter, and as a mobile visitor if you have separate rules. Check that query strings survive the redirect, because losing them breaks campaign tracking.
The troubleshooting guide has a section for "too many redirects" that walks through the usual culprits in order.
Redirects, HSTS and the caches that remember them
Once HTTPS is working everywhere, a header called HSTS can tell browsers never to try the plain HTTP address for your domain again. The browser then upgrades the request itself, internally, with no round trip to your server at all. It is a good thing to turn on, but only after every part of the site, including subdomains you want covered, works over HTTPS. Start with a short max-age such as a day, and lengthen it once nothing complains.
The flip side is that the browser now remembers a decision you made on the server. Browsers also keep 301 responses in their cache, so if you change a rule and still see the old behaviour, try a private window or curl, which has no memory. A CDN or a page cache in front of the site can also store the redirect itself, in which case purging it is part of the fix. When a redirect refuses to change, work outwards from the origin: ask the server directly, then the CDN, then the browser.