Using .htaccess
.htaccess (Hypertext Access) is a distributed configuration file that lets you override Apache settings on a per-directory basis. It is especially useful in shared hosting environments where you don't have access to modify the main configuration file. You can implement URL redirects, access control, cache headers, HTTPS enforcement, and more.
How .htaccess Works
When Apache processes an HTTP request, it traverses up the directory path to the requested file, reading .htaccess files along the way.
Request: GET /blog/2024/post.html
.htaccess files Apache reads (in order):
1. /.htaccess
2. /blog/.htaccess
3. /blog/2024/.htaccess ← Most specific settings take precedence
Performance:
.htaccesscauses file I/O on every request. Whenever possible, configure directly inhttpd.confand setAllowOverride Noneto disable.htaccess— this improves performance significantly.
Activation Requirement
.htaccess only works when AllowOverride is configured for the directory:
<Directory "/var/www/html">
AllowOverride All # Allow all .htaccess directives
</Directory>
URL Rewriting (mod_rewrite)
Enable mod_rewrite
RewriteEngine On
Force HTTPS Redirect
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
www → non-www Redirect
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
SPA (React/Vue) Client-Side Routing
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ /index.html [L]
Or with Apache 2.4.16+:
FallbackResource /index.html
Pretty URLs
RewriteEngine On
# example.com/blog/123 → example.com/blog.php?id=123
RewriteRule ^blog/([0-9]+)/?$ blog.php?id=$1 [L,QSA]
RewriteRule Flags
| Flag | Description |
|---|---|
R=301 | 301 permanent redirect |
R=302 | 302 temporary redirect |
L | Last rule (stop processing) |
QSA | Preserve existing query string |
NC | Case-insensitive |
NE | No escape of special characters |
F | Return 403 Forbidden |
G | Return 410 Gone |
Access Control
# Block specific IPs
<RequireAll>
Require all granted
Require not ip 203.0.113.5
Require not ip 198.51.100.0/24
</RequireAll>
# Allow only specific IPs
Require ip 127.0.0.1
Require ip 192.168.1.0/24
# Block bad bots
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (BadBot|Scraper|Harvester) [NC]
RewriteRule .* - [F,L]
Cache Headers (mod_expires, mod_headers)
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 week"
ExpiresByType text/html "access plus 0 seconds"
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(js|css)$">
Header set Cache-Control "max-age=31536000, public, immutable"
</FilesMatch>
<FilesMatch "\.(html|htm)$">
Header set Cache-Control "no-cache, no-store, must-revalidate"
</FilesMatch>
</IfModule>
Gzip Compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css
AddOutputFilterByType DEFLATE application/javascript application/json
AddOutputFilterByType DEFLATE image/svg+xml font/woff font/woff2
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp|zip|gz)$ no-gzip dont-vary
</IfModule>
Security Headers
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header unset Server
Header unset X-Powered-By
</IfModule>
Custom Error Pages
ErrorDocument 400 /errors/400.html
ErrorDocument 403 /errors/403.html
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.html
ErrorDocument 503 /errors/503.html
.htaccess vs httpd.conf — When to Use Which
| Situation | Recommended |
|---|---|
| Shared hosting (no root access) | .htaccess |
| Dedicated server (can edit httpd.conf) | httpd.conf (performance first) |
| CMS (WordPress, Drupal) | .htaccess (auto-generated by framework) |
| Development / testing | .htaccess (fast changes) |
| High-performance production | httpd.conf + AllowOverride None |
Summary
.htaccess: Per-directory distributed config, ideal for shared hosting and CMSmod_rewrite: Core for HTTPS enforcement, SPA routing, URL cleanupmod_expires+mod_headers: Cache and security header settings- In production: prefer
httpd.conf+AllowOverride Nonefor best performance