# ============================================================================ # visit.waterschurch.org — Apache configuration for the subdomain document root # Copied to out/.htaccess by `npm run build` (scripts/postbuild.mjs). # ============================================================================ Options -Indexes -MultiViews DirectoryIndex index.html ErrorDocument 404 /404.html # cPanel's default 403 page does not exist in this document root; show our branded page instead (status stays 403). ErrorDocument 403 /404.html # --- Private configuration directory for the PHP API ----------------------- # Uncomment and set to the absolute path of the folder holding your .env file # (OUTSIDE the document root). If omitted, the API looks for # /visit-private, e.g. /home/USER/visit-private. # SetEnv VISIT_PRIVATE_DIR /home/USER/visit-private RewriteEngine On # Force HTTPS and the canonical non-www host RewriteCond %{HTTPS} !=on [OR] RewriteCond %{HTTP_HOST} ^www\.visit\.waterschurch\.org$ [NC] RewriteRule ^(.*)$ https://visit.waterschurch.org/$1 [R=301,L] # Never serve dot-files (except the ACME challenge folder used for SSL issuance) RewriteCond %{REQUEST_URI} !^/\.well-known/ RewriteRule (^|/)\.(?!well-known) - [F,L] # Next.js static export: serve /path as /path.html when the file exists RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{DOCUMENT_ROOT}/$1.html -f RewriteRule ^(.+?)/?$ /$1.html [L] # Strip trailing slash on non-directories RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.+)/$ /$1 [R=301,L] # --- Security headers ------------------------------------------------------- Header always set X-Content-Type-Options "nosniff" Header always set X-Frame-Options "SAMEORIGIN" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Permissions-Policy "geolocation=(self), camera=(), microphone=(), payment=()" Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" "expr=%{HTTPS} == 'on'" # CSP is report-only until Waters Church confirms its tag/pixel list. See docs/DEPLOY-CPANEL.md. Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com https://connect.facebook.net https://analytics.tiktok.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://www.google-analytics.com https://analytics.google.com https://www.facebook.com https://analytics.tiktok.com https://challenges.cloudflare.com; frame-src https://challenges.cloudflare.com; base-uri 'self'; form-action 'self' sms:; frame-ancestors 'self'" # Immutable hashed assets Header set Cache-Control "public, max-age=31536000, immutable" # HTML and text files: always revalidate Header set Cache-Control "public, max-age=0, must-revalidate" # --- Compression ----------------------------------------------------------- AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml font/woff2 AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml # --- MIME types some cPanel builds miss ------------------------------------- AddType image/avif .avif AddType image/webp .webp AddType font/woff2 .woff2 # --- Expires fallback (when mod_headers is unavailable) --------------------- ExpiresActive On ExpiresByType image/avif "access plus 1 year" ExpiresByType image/webp "access plus 1 year" ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/png "access plus 1 year" ExpiresByType image/svg+xml "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" ExpiresByType text/html "access plus 0 seconds"