.htaccess & Apache Modules
.htaccess files allow per-directory configuration without restarting Apache. Combined with modules like mod_rewrite, mod_headers, and mod_proxy, they cover URL rewriting, compression, caching, and reverse proxying.
mod_rewrite — URL Rewriting
# Enable mod_rewrite
sudo a2enmod rewrite
# Ensure AllowOverride All is set in the Directory block for .htaccess to work
# .htaccess — SPA (Single Page App) routing
RewriteEngine On
RewriteBase /
RewriteRule ^index.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
# Redirect old URL to new URL (301 permanent)
RewriteRule ^old-page.html$ /new-page/ [R=301,L]
# Redirect entire old directory
RewriteRule ^old-blog/(.*)$ /blog/$1 [R=301,L]
# Force www
RewriteCond %{HTTP_HOST} !^www.
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
# Remove trailing slash (except root)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [R=301,L]
# RewriteCond flags:
# %{HTTP_HOST} — the incoming Host header
# %{REQUEST_FILENAME} — absolute filesystem path
# !-f — not a real file
# !-d — not a real directory
# [NC] — case-insensitive match
# [OR] — logical OR between conditions
# RewriteRule flags:
# [L] — last rule, stop processing
# [R=301] — redirect with HTTP status
# [P] — proxy (requires mod_proxy)
# [QSA] — append query string
# [NE] — no URL encoding of special charsmod_headers, mod_deflate & mod_expires
# Enable modules
sudo a2enmod headers deflate expires
# mod_headers — add/modify response headers in .htaccess or vhost config
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=()"
# Remove server version from response
Header unset Server
Header always unset X-Powered-By
</IfModule>
# mod_deflate — gzip compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css
AddOutputFilterByType DEFLATE application/javascript application/json
AddOutputFilterByType DEFLATE application/xml image/svg+xml font/woff2
# Don't compress already-compressed formats
SetEnvIfNoCase Request_URI .(?:gif|jpe?g|png|webp|zip|gz|br)$ no-gzip
</IfModule>
# mod_expires — browser cache control
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 month"
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 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>mod_proxy — Reverse Proxy
# Enable proxy modules
sudo a2enmod proxy proxy_http proxy_wstunnel
# Reverse proxy a Node.js app running on port 3000
<VirtualHost *:80>
ServerName api.example.com
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
# WebSocket support (for Socket.io etc.)
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://127.0.0.1:3000/$1" [P,L]
</VirtualHost>
# Proxy only a path prefix (e.g. /api → backend)
ProxyPass /api/ http://127.0.0.1:4000/
ProxyPassReverse /api/ http://127.0.0.1:4000/
# Module management commands
sudo a2enmod <module> # Enable a module
sudo a2dismod <module> # Disable a module
apache2ctl -M # List all loaded modules
apache2ctl -M | grep rewrite # Check if specific module is loadedKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free