The .htaccess file lets you change how Apache handles requests for a directory without editing the main server configuration or restarting anything. It is the usual way to set up an .htaccess redirect, force HTTPS, choose between www and non-www, and add caching headers, especially on shared hosting where you have no access to the server config. This guide gives tested, copy-ready snippets and explains what each line does.
Before you start
- Apache only. Nginx ignores
.htaccessfiles. LiteSpeed and OpenLiteSpeed read most of the same rules. - Overrides must be allowed. The server config needs
AllowOverride All(or at leastFileInfoand the relevant categories) for the directory; otherwise your rules are silently ignored. - Modules must be enabled. Rewrites need
mod_rewrite, caching rules usemod_expiresandmod_headers. On Debian/Ubuntu:sudo a2enmod rewrite expires headers, then reload Apache. - Back up first. A typo in
.htaccessproduces a 500 Internal Server Error for the whole directory. Keep a copy and test immediately after every change. - Mind the order. Put redirects before application rules (such as the WordPress block) so they run first.
Simple .htaccess redirect rules
Redirect one page
For a single moved URL, mod_alias's Redirect directive is the simplest option:
Redirect 301 /old-page.html /new-page/
Redirect 301 /services/hosting https://www.example.com/server-management/
A 301 means "moved permanently"; browsers and search engines update their records and pass ranking signals to the new URL. Use 302 only for genuinely temporary moves.
Redirect a whole folder
RedirectMatch 301 ^/blog/(.*)$ /articles/$1
Avoid mixing Redirect/RedirectMatch with RewriteRule for the same URLs, as the two modules process requests at different stages and can produce confusing results. When in doubt, use mod_rewrite throughout.
Force HTTPS
Once your SSL certificate is installed and working (check it with our SSL checker), send all HTTP traffic to HTTPS:
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
If your site sits behind a load balancer, CDN or proxy that terminates SSL, Apache may see every request as HTTP and loop forever. In that case test the forwarded header instead:
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Choose www or non-www
Pick one version of your domain and redirect the other, so search engines see a single canonical site. Combined with HTTPS, using a single hop:
# non-www to www, and HTTP to HTTPS
RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\. [NC,OR]
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^ https://www.%1%{REQUEST_URI} [L,R=301]
For the opposite direction (www to non-www):
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]
Move an entire domain
When rebranding, redirect every path on the old domain to the same path on the new one, preserving deep links:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?old-domain\.com$ [NC]
RewriteRule ^(.*)$ https://www.new-domain.com/$1 [L,R=301]
Keep the old domain registered and these redirects running for a long time, ideally years, so old links and bookmarks keep working.
Browser caching
Telling browsers to keep static files reduces load times for returning visitors and load on your server:
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType text/html "access plus 0 seconds"
</IfModule>
Long cache times are safe only if file names change when content changes (for example style.3f9a.css or style.css?v=12). Otherwise visitors may see stale styles after an update. HTML is kept uncached here so new content appears immediately.
Compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml
AddOutputFilterByType DEFLATE application/javascript application/json image/svg+xml
</IfModule>
Images such as JPEG, PNG and WebP are already compressed, so they are left out. Use our HTTP header checker to confirm Content-Encoding and Cache-Control or Expires headers are being sent.
Useful security snippets
# Turn off directory listings
Options -Indexes
# Block access to hidden files such as .env and .git
<FilesMatch "^\.">
Require all denied
</FilesMatch>
# Basic security headers
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Frame-Options "SAMEORIGIN"
</IfModule>
Make sure the FilesMatch rule does not block the .well-known folder used for certificate validation; it applies to files, not directories, so .well-known/acme-challenge/ tokens remain reachable.
Testing redirects
Browsers cache 301 redirects aggressively, which makes testing in a browser misleading. Use curl instead:
curl -I http://example.com/old-page.html
curl -sIL http://example.com | grep -iE "^(HTTP|location)"
The second command follows the full chain so you can confirm there is only one hop to the final URL. Long chains slow visitors down and waste search engine crawl effort.
A note on performance
Apache checks for .htaccess files in every directory on the path for each request. If you control the server, moving rules into the virtual host configuration and setting AllowOverride None is slightly faster and easier to manage. On shared hosting, .htaccess remains the right tool. If you prefer someone else to handle web server configuration, it is part of our server management work.
Key takeaways
- Use
Redirect 301for single pages andmod_rewritefor patterns, HTTPS and domain rules. - Combine HTTPS and www rules into one redirect hop, and watch for proxy loops.
- Add long browser caching for versioned static files and compression for text.
- Test with
curl, not the browser, and keep a backup of every working version.