.htaccess
Copypasta
# =============================================
# 0. GENERAL SETTINGS
# =============================================
AddDefaultCharset UTF-8
# Redirect to an initial page other than index.html
# DirectoryIndex 0000_table_of_contents.html index.html
# =============================================
# 1. SEO & SECURITY: Canonical Domain (Force non-www and HTTPS)
# =============================================
RewriteEngine On
RewriteBase /
# Force non-www:
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]
# Force HTTPS (Proxy-Safe):
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# =============================================
# 2. PERFORMANCE: Compression & Browser Caching
# =============================================
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain text/html text/xml text/css application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript application/json image/svg+xml
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 0 seconds"
ExpiresByType text/html "access plus 0 seconds"
# Bumped static assets to 1 year for better Lighthouse/PageSpeed scores
ExpiresByType image/x-icon "access plus 1 year"
ExpiresByType image/gif "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 image/svg+xml "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
</IfModule>
# =============================================
# 3. SECURITY: HTTP Headers & Secure Folders
# =============================================
<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"
# HSTS (Removed includeSubDomains to prevent accidental lockouts)
Header always set Strict-Transport-Security "max-age=31536000" env=HTTPS
# Content Security Policy (Not necessary for my static website on private VPS...)
# Note: Add a report-uri if you want to capture these reports externally!
# Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self';"
#Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'self';"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self';"
</IfModule>
# Password-protect all subdirectories named "secure" only when HTTPS is active
<If "%{HTTPS} == 'on' && %{REQUEST_URI} =~ m#(^|/)secure(/|$)#i">
AuthType Basic
AuthName "Restricted Secure Area"
AuthUserFile /home/justinmichaelbonanno/.htpasswd
Require user bonju
</If>
<If "%{HTTPS} == 'on' && %{REQUEST_URI} =~ m#(^|/)qsjwbuf(/|$)#i">
AuthType Basic
AuthName "Restricted Secure Area"
AuthUserFile /home/justinmichaelbonanno/.htpasswd
Require user bonju
</If>
<If "%{HTTPS} == 'on' && %{REQUEST_URI} =~ m#(^|/)secure_guest(/|$)#i">
AuthType Basic
AuthName "Restricted Secure Area"
AuthUserFile /home/justinmichaelbonanno/.htpasswd
Require valid-user
</If>
# =============================================
# 4. GENERAL SECURITY & ERROR HANDLING
# =============================================
# MultiViews MUST be off for extensionless URL routing to work properly
Options -Indexes -MultiViews
<FilesMatch "^\.ht">
Require all denied
</FilesMatch>
ErrorDocument 401 /401.html
ErrorDocument 404 /404.html
ErrorDocument 500 /500.html
ErrorDocument 503 /503.html
# =============================================
# 5. URL AESTHETICS & ROUTING (Internal Rewrites)
# =============================================
# Force trailing slash for ACTUAL directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*[^/])$ /$1/ [L,R=301]
# Remove trailing slash from non-directories (prevents 404 on /page/)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
# SEO: Redirect /index.html to root /
RewriteCond %{THE_REQUEST} \s/+(.*?/)?index\.html[\s?] [NC]
RewriteRule ^ /%1 [R=301,L]
# SEO: Redirect /page.html directly to /page
RewriteCond %{THE_REQUEST} \s/+(.+)\.html[\s?] [NC]
RewriteRule ^ /%1 [R=301,L]
# INTERNAL: Allow accessing /page by serving /page.html silently
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.*)$ $1.html [L]