Apache
03 / 07

.htaccess & Modules

.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 chars

mod_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 loaded

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free