Apache redirects: Redirect, RedirectMatch, HTTPS and www rules
A redirect tells the browser "this page is now over there" with a 3xx status and a Location header. In Apache httpd 2.4 most redirects need one Redirect line from mod_alias; mod_rewrite is only needed when the decision depends on the host name, the protocol or other conditions. This guide covers both, which status code to choose, and how to move a whole site without losing visitors or search rankings.
Redirect and RedirectMatch
mod_alias is enabled by default on Debian/Ubuntu and RHEL-family systems. Its directives work in the server config, in a virtual host and in .htaccess (with AllowOverride FileInfo).
# One page
Redirect permanent "/old-page.html" "https://www.example.com/new-page.html"
# A whole folder, to another host
Redirect permanent "/blog" "https://blog.example.com"
# A pattern: /news/2001/story.php becomes /archive/2001/story.html
RedirectMatch permanent "^/news/(\d{4})/(.*)\.php$" "https://www.example.com/archive/$1/$2.html"
# Removed for good
Redirect gone "/old-offer.html"
How Redirect matches:
- It is a prefix match on whole path segments.
Redirect "/blog" …also sends/blog/2020/post.htmltohttps://blog.example.com/2020/post.html, but not/blogger. - The old path is case-sensitive and must start with
/. - The query string is kept:
/blog/x?a=1goes to…/x?a=1. POST data is not carried over. - Without a status keyword the redirect is 302 (temporary). Write
permanent,temp,seeother,goneor a number such as308. - A target that is only a path (
"/new-page.html") gets the current scheme and host added. Behind a proxy that terminates TLS that can producehttp://links, so write fullhttps://targets.
RedirectMatch uses a regular expression on the path, with $1, $2 for groups. Anchor it with ^ and $, or it matches anywhere in the path. RedirectPermanent and RedirectTemp are short forms of Redirect permanent and Redirect temp.
Which status code
| Code | Meaning | Method on the next request | Use it for |
|---|---|---|---|
| 301 | Moved Permanently | GET stays GET; browsers usually turn POST into GET | Moved pages, HTTPS, www, domain changes |
| 308 | Permanent Redirect | Method and body kept | Permanent moves of APIs and forms that receive POST |
| 302 | Found (temporary) | As 301 | Short-term moves, testing a new redirect |
| 307 | Temporary Redirect | Method and body kept | Temporary moves where POST must survive |
| 303 | See Other | Always GET | After a form POST, to a result page |
| 410 | Gone (not a redirect) | – | Pages removed for good with no replacement |
301 and 308 are cacheable by default, so browsers may remember them, and Google treats both as permanent: it shows the new URL in search results. For 302, 303 and 307 it usually keeps showing the old URL. So use permanent codes only when you are sure, and test new redirects as 302 first: a mistaken 301 can stay in visitors' browser caches long after you fix the config.
HTTP to HTTPS
The cleanest way is a small port-80 virtual host that only redirects, next to the real HTTPS one (set up TLS first: HTTPS and TLS):
<VirtualHost *:80>
ServerName www.example.com
ServerAlias example.com
Redirect permanent "/" "https://www.example.com/"
</VirtualHost>
<VirtualHost *:443>
ServerName www.example.com
DocumentRoot /var/www/example.com/public
# SSLEngine on, certificate lines, logs ...
</VirtualHost>
Because Redirect keeps the rest of the path, http://example.com/a/b.html?x=1 lands on https://www.example.com/a/b.html?x=1 in one step. On Debian/Ubuntu the files live in /etc/apache2/sites-available/ (enable with sudo a2ensite); on RHEL-family systems in /etc/httpd/conf.d/. See Virtual hosts.
If you can only use .htaccess, mod_rewrite does it:
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Do not add [NE] here: %{REQUEST_URI} is already decoded, and without escaping a path such as /a%20b.html would be sent back with a raw space. Behind a load balancer or CDN that terminates TLS, Apache sees every request as plain HTTP and this rule loops forever. Then test the header the proxy sets instead, for example RewriteCond %{HTTP:X-Forwarded-Proto} !=https, and only if requests can reach Apache through that proxy alone.
Once HTTPS works everywhere, add an HSTS header so browsers skip the HTTP step next time.
www or non-www
Pick one host name and redirect the other, so that search engines and cookies see a single site. With access to the server config, give the unwanted name its own virtual host:
<VirtualHost *:443>
ServerName example.com
# same certificate lines as the main site; the certificate must cover example.com
Redirect permanent "/" "https://www.example.com/"
</VirtualHost>
Or, inside the one virtual host that answers both names:
<If "%{HTTP_HOST} == 'example.com'">
Redirect permanent "/" "https://www.example.com/"
</If>
For .htaccess, this rule does HTTPS and www in a single redirect, so visitors never go through two hops:
RewriteEngine On
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$
RewriteRule ^ https://www.%1%{REQUEST_URI} [R=301,L]
The last condition captures the host without www. into %1. To go the other way (drop the www), use RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] and the target https://%1%{REQUEST_URI}.
Keeping old URLs alive after a move
Links, bookmarks and search results pointing at your old addresses keep arriving for years. A move without redirects turns all of them into 404 errors. A good plan:
- List the old URLs from your sitemap, CMS export and access logs (requests that returned 200 in the last year are a good start).
- Map each one to its new address. Send it to the closest matching page, not to the home page. If nothing replaces it, return 410.
- Redirect straight to the final URL. Avoid chains such as old → http new → https new. Google advises going directly to the final destination and keeping chains short.
- Keep the redirects as long as possible. Google's site-move guidance says generally at least a year; for visitors, keeping them indefinitely costs almost nothing.
- Update your own links and sitemaps to the new URLs so your pages do not depend on the redirects.
Hundreds of redirects: RewriteMap
For a long list, a lookup file is easier to maintain than hundreds of Redirect lines. RewriteMap only works in the server or virtual host config:
# /etc/apache2/redirects.map (or /etc/httpd/conf/redirects.map)
# old path new path
/old/about.php /about/
/old/contact.php /contact/
# in the <VirtualHost>
RewriteEngine On
RewriteMap moved "txt:/etc/apache2/redirects.map"
RewriteCond ${moved:%{REQUEST_URI}} !=""
RewriteRule ^ ${moved:%{REQUEST_URI}} [R=301,L]
Paths not in the file are not affected; query strings are passed on. Apache re-reads a changed txt: map automatically.
Test and avoid loops
curl -sI http://example.com/a/b.html | grep -iE '^(HTTP|location)'
HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/a/b.html
curl -sIL -o /dev/null -w '%{num_redirects} redirects, final %{url_effective}\n' http://example.com/a/b.html
1 redirects, final https://www.example.com/a/b.html
- Each old URL reaches its final page in one redirect.
- The final page returns 200, not another redirect or a 404.
- No "too many redirects" error in the browser. That usually means a redirect whose target matches its own condition, or the HTTPS rule behind a TLS-terminating proxy.
- After editing the server config:
sudo apache2ctl configtest && sudo systemctl reload apache2(Debian/Ubuntu) orsudo apachectl configtest && sudo systemctl reload httpd(RHEL family).
Do not mix Redirect and RewriteRule for the same URLs. In a virtual host mod_rewrite runs first; in .htaccess mod_alias runs first, so the same lines can behave differently after you move them. More on rules and flags: mod_rewrite.
Sources
- mod_alias (Redirect, RedirectMatch), Apache HTTP Server 2.4 documentation
- Redirecting and remapping with mod_rewrite, Apache HTTP Server 2.4 documentation
- When not to use mod_rewrite, Apache HTTP Server 2.4 documentation
- mod_rewrite (RewriteMap, RewriteCond), Apache HTTP Server 2.4 documentation
- Redirect Request to SSL, Apache httpd wiki
- RFC 9110 HTTP Semantics, section 15.4 Redirection 3xx, IETF
- Redirections in HTTP, MDN Web Docs
- Redirects and Google Search, Google Search Central
- Site moves with URL changes, Google Search Central