Skip to main content
Advertisement

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: .htaccess causes file I/O on every request. Whenever possible, configure directly in httpd.conf and set AllowOverride None to 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

FlagDescription
R=301301 permanent redirect
R=302302 temporary redirect
LLast rule (stop processing)
QSAPreserve existing query string
NCCase-insensitive
NENo escape of special characters
FReturn 403 Forbidden
GReturn 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

SituationRecommended
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 productionhttpd.conf + AllowOverride None

Summary

  • .htaccess: Per-directory distributed config, ideal for shared hosting and CMS
  • mod_rewrite: Core for HTTPS enforcement, SPA routing, URL cleanup
  • mod_expires + mod_headers: Cache and security header settings
  • In production: prefer httpd.conf + AllowOverride None for best performance
Advertisement