The .htaccess file lets you configure Apache directly in the site folder without access to the server configuration. It is located in public_html/.
301 Redirects
RewriteEngine On
# From the old page to the new one
Redirect 301 /old-page.html /new-page
# Redirect an entire folder
RedirectMatch 301 ^/old-section/(.*)$ /new-section/$1
www ↔ non-www
RewriteEngine On
# Redirect ALL requests to www
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
# Or the opposite — remove www
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
HTTP → HTTPS Redirect
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Password-Protect a Folder
# Create the .htpasswd file:
# htpasswd -c /home/user/.htpasswd adminuser
# In the folder's .htaccess:
AuthType Basic
AuthName "Protected Area"
AuthUserFile /home/user/.htpasswd
Require valid-user
Blocking IP Addresses
# Block a specific IP
Deny from 203.0.113.42
# Allow only your own IP
Order deny,allow
Deny from all
Allow from 203.0.113.10
Caching Static Files
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
</IfModule>
Rule Order Matters
Combining the HTTPS redirect and the www redirect carelessly in one file can easily produce a double redirect or a loop — Apache applies rules top to bottom and stops only at the first rule whose L flag fires.
# Correct order: HTTPS first, then www,
# so the browser doesn't do two separate redirects in a row
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
Both conditions lead to the same final address with www and HTTPS at once, so the browser gets a single redirect instead of two in sequence.
Testing Rules Safely
Check every significant change with a saved backup copy next to it — a rule can be rolled back instantly instead of waiting to restore it through the hosting panel.
# Save a working copy before editing
cp .htaccess .htaccess.bak
# If the site returns 500 after an edit, restore the previous version with one command
cp .htaccess.bak .htaccess
Common .htaccess Errors
| Symptom | Cause |
|---|---|
| 500 Internal Server Error right after saving | A syntax error in a directive: a missing space between RewriteCond and its pattern, a stray quote, or an invalid flag like [R=301,L,X] |
| RewriteRule: bad flag delimiters | The flags inside the square brackets are missing a comma between them, or have a stray space inside the brackets |
| ERR_TOO_MANY_REDIRECTS in the browser | The www ↔ non-www rule and the HTTP → HTTPS rule conflict in order, especially when Cloudflare's Flexible SSL is already active and the repeated rule bounces the request back and forth |
| Invalid command 'ExpiresActive' | The directive is used without the <IfModule mod_expires.c> wrapper, and the module is disabled on the account — Apache does not understand the command and returns 500 |
| Basic Auth does not prompt for a password | The .htpasswd file path is wrong or the Apache process lacks read permission on the file |
When .htaccess Is Not the Right Tool
- The site runs on Nginx — only Apache reads .htaccess; on Nginx the rules are rewritten into the virtual host config on the provider's side
- The rules pile up and live inside subfolders — with AllowOverride All, Apache scans the directory tree on every request, which noticeably slows responses
- You need settings that AllowOverride does not permit (process limits, core modules) — .htaccess only overrides what the hosting explicitly allows
- Changes must apply to hundreds of domains at once — keeping an identical file in every folder is awkward and easy to let drift out of sync
Verifying the Result
After editing, always check the actual HTTP response, not just how the page looks in a browser — the status code is not always visible, especially when a cache is involved. If the redirect rules are part of an HTTPS migration, see also the article on setting up an HTTPS redirect; for a 500 error, the causes are covered in the article on diagnosing error_log.
# Check the response code and the redirect's destination
curl -I http://example.com/old-page.html
# Confirm the caching headers were applied to static assets
curl -I https://example.com/style.css | grep -i cache-control