From dfb8419b083968203409431cf37e7f27befa71e4 Mon Sep 17 00:00:00 2001
From: hsiegeln <37154749+hsiegeln@users.noreply.github.com>
Date: Fri, 24 Apr 2026 17:22:25 +0200
Subject: [PATCH] Add .htaccess for origin hardening, HTTPS redirect, and cache
headers
---
public/.htaccess | 55 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
create mode 100644 public/.htaccess
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..bff4fa0
--- /dev/null
+++ b/public/.htaccess
@@ -0,0 +1,55 @@
+# ---------------------------------------------------------------
+# www.cameleer.io — Apache config at the Hetzner origin.
+# Defense in depth: Cloudflare handles most of this at the edge;
+# these rules make sure the origin is hardened even without the CDN.
+# ---------------------------------------------------------------
+
+# Enable rewriting
+RewriteEngine On
+
+# Force HTTPS — redundant with Cloudflare but belts-and-braces.
+RewriteCond %{HTTPS} !=on
+RewriteCond %{HTTP:X-Forwarded-Proto} !=https
+RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+
+# Redirect apex -> www.
+RewriteCond %{HTTP_HOST} ^cameleer\.io$ [NC]
+RewriteRule ^(.*)$ https://www.cameleer.io/$1 [L,R=301]
+
+# Disable directory listings.
+Options -Indexes
+
+# Block access to dotfiles and sensitive extensions that should never be here.
+
+ Require all denied
+
+
+# Prevent MIME sniffing, clickjacking, etc. (primary copy also comes from Astro middleware
+# and Cloudflare Transform Rules — these apply if either layer is bypassed).
+
+ Header always set X-Content-Type-Options "nosniff"
+ Header always set X-Frame-Options "DENY"
+ Header always set Referrer-Policy "strict-origin-when-cross-origin"
+ Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()"
+ Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
+
+ # Cache hashed build assets aggressively; HTML must be revalidated.
+
+ Header set Cache-Control "public, max-age=31536000, immutable"
+
+
+ Header set Cache-Control "public, max-age=3600, must-revalidate"
+
+
+ # Remove Server header leak where possible.
+ Header unset X-Powered-By
+
+
+# Compression (Hetzner supports mod_deflate).
+
+ AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json image/svg+xml text/plain
+
+
+# Custom error pages (optional — fall back to default if not present).
+ErrorDocument 404 /404.html
+ErrorDocument 403 /404.html