Apache Redirect Configuration Guide
Contents
Apache redirects are primarily configured through .htaccess files and the mod_rewrite module. This is especially useful on shared hosting where you don't have access to the main server config.
.htaccess basics
.htaccess is Apache's per-directory configuration file. Place it in your site root and Apache will read it automatically. The filename starts with a dot โ it's a hidden file on Unix systems.
Enable mod_rewrite
# Ubuntu/Debian
sudo a2enmod rewrite
sudo systemctl restart apache2
# CentOS/RHEL โ uncomment in /etc/httpd/conf/httpd.conf:
LoadModule rewrite_module modules/mod_rewrite.so
Allow .htaccess overrides
<Directory /var/www/html>
AllowOverride All
</Directory>
Redirect directive
The simplest approach โ no regex required:
# 301 permanent
Redirect 301 /old-page.html https://example.com/new-page.html
# 302 temporary
Redirect 302 /sale https://example.com/promo
# Whole-site redirect to new domain
Redirect 301 / https://new-domain.com/
๐ก Redirect limitations
The Redirect directive is simple but doesn't support regex or conditional logic. Use RewriteRule for anything more complex.
RewriteRule
RewriteEngine On
# HTTP โ HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
# non-www โ www
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]
Common flags
[R=301]โ 301 permanent redirect[R=302]โ 302 temporary redirect[L]โ Last rule, stop processing[NC]โ Case-insensitive match[QSA]โ Append original query string
RewriteCond
Conditions that must be true before the following RewriteRule fires:
# Redirect mobile users
RewriteCond %{HTTP_USER_AGENT} "android|iphone|ipad" [NC]
RewriteRule ^(.*)$ https://m.example.com/$1 [R=302,L]
# Redirect based on query string
RewriteCond %{QUERY_STRING} ^id=([0-9]+)$
RewriteRule ^article$ /post/%1? [R=301,L]
Common scenarios
Force HTTPS + www
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]
Remove .html extension
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.+)$ $1.html [L]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /(.+)\.html
RewriteRule ^ /%1 [R=301,L]
WordPress permalinks
# BEGIN WordPress
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress
Common pitfalls
500 Internal Server Error
Almost always a syntax error in .htaccess. Check the Apache error log:
tail -f /var/log/apache2/error.log
Redirect loop
# โ Loop โ no condition to stop re-matching
RewriteRule ^(.*)$ https://example.com/ [R=301,L]
# โ
Add HTTPS condition
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://example.com/ [R=301,L]
Lost query strings
# โ Query string dropped
RewriteRule ^old-page$ /new-page [R=301,L]
# โ
Preserve with QSA flag
RewriteRule ^old-page$ /new-page [R=301,L,QSA]
Debugging
# Enable rewrite logging (Apache 2.4)
LogLevel alert rewrite:trace3
# Test with curl
curl -I http://example.com/old-page
curl -IL http://example.com/old-page
Or use 301check.com to visualize the full redirect chain.