diff --git a/apache/conf/omny.conf b/apache/conf/omny.conf
index 284416c..1fb8c30 100644
--- a/apache/conf/omny.conf
+++ b/apache/conf/omny.conf
@@ -1,11 +1,114 @@
+# ============================================================
+# omny-test.psi.ch — HTTPS test instance
+#
+# Based on the admin-provided vhost skeleton, extended with:
+# * HTTPS vhost + HTTP->HTTPS redirect (ACME path excepted)
+# * DirectoryIndex, -Indexes
+# * deny rules for .htpasswd/.gen/.inc/backup files
+# * auth-gate rewrite rules
+#
+# Requirements (for the administrators):
+# * mod_rewrite and mod_ssl enabled. The vhost will
+# intentionally FAIL to load without mod_rewrite, rather
+# than silently serving everything unauthenticated.
+# * PHP execution active for .php files in /var/www/omny.
+# * SSLCertificateFile / SSLCertificateKeyFile paths below
+# are PLACEHOLDERS (Let's Encrypt layout) — please adjust
+# to the actual certificate location for omny-test.psi.ch.
+# ============================================================
+
+# ------------------------------------------------------------
+# Port 80: redirect everything to HTTPS, except ACME challenges
+# (exception only matters if certbot webroot renewal is used;
+# harmless otherwise)
+# ------------------------------------------------------------
ServerName omny-test.psi.ch
DocumentRoot /var/www/omny
-
- Options +ExecCGI
- AllowOverride None
- Require all granted
-
+ RewriteEngine On
+ RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
+ RewriteRule ^ https://omny-test.psi.ch%{REQUEST_URI} [R=301,L]
+ # Query strings are preserved automatically on [R] redirects.
+# ------------------------------------------------------------
+# Port 443: the actual site
+# ------------------------------------------------------------
+
+ ServerName omny-test.psi.ch
+ DocumentRoot /var/www/omny
+
+ SSLEngine on
+ # PLACEHOLDER paths — adjust to the real certificate:
+ SSLCertificateFile /etc/letsencrypt/live/omny-test.psi.ch/fullchain.pem
+ SSLCertificateKeyFile /etc/letsencrypt/live/omny-test.psi.ch/privkey.pem
+
+ ErrorLog ${APACHE_LOG_DIR}/omny-test_error.log
+ CustomLog ${APACHE_LOG_DIR}/omny-test_access.log combined
+
+ # Optional hardening (requires mod_headers). Deliberately
+ # disabled on the test instance; enable on production:
+ # Header always set Strict-Transport-Security "max-age=31536000"
+
+
+ Options +ExecCGI -Indexes
+ AllowOverride None
+ Require all granted
+ # Bare "/" is normally caught by the catch-all rewrite
+ # below; DirectoryIndex kept as fallback.
+ DirectoryIndex login.php index.html index.php
+
+
+ # --------------------------------------------------------
+ # Block direct filesystem access to sensitive / junk files.
+ # (Apache's default "^\.ht" rule does NOT cover
+ # users.htpasswd / session.htpasswd — they don't start
+ # with ".ht".)
+ # --------------------------------------------------------
+
+ Require all denied
+
+
+ RewriteEngine On
+
+ # 0. URL-level deny before any rewriting.
+ RewriteRule \.(htpasswd|gen)$ - [F,L]
+
+ # 1. Guard: forbid direct client requests that pass the
+ # internal _file parameter to the gate scripts (would
+ # otherwise let a logged-in user stream PHP source incl.
+ # TOKEN_SECRET). Internal rewrites are unaffected: the
+ # gate rewrites below use [END], which skips rule
+ # re-processing.
+ RewriteCond %{QUERY_STRING} (^|&)_file=
+ RewriteRule ^/(samples/)?auth_gate\.php$ - [F,L]
+
+ # 2. Pass-throughs (no cookie check) — first match wins.
+ RewriteRule ^/login\.php$ - [L]
+ RewriteRule ^/upload\.php$ - [L]
+ RewriteRule ^/session_query\.php$ - [L]
+ RewriteRule ^/set_password\.php$ - [L]
+ RewriteRule ^/info\.php$ - [L]
+ RewriteRule ^/auth_gate\.php$ - [L]
+ RewriteRule ^/samples/auth_gate\.php$ - [L]
+ RewriteRule ^/\.well-known/acme-challenge/ - [L]
+ RewriteRule ^/samples/upload\.php$ - [L]
+ RewriteRule ^/samples/newmeasurement\.php$ - [L]
+
+ # 3. Normalize /samples -> /samples/ so it hits the samples
+ # gate instead of falling through to the main gate.
+ RewriteRule ^/samples$ /samples/ [R=301,L]
+
+ # 4. Samples area: fixed users only.
+ # [END] (not [L]) is essential: Apache re-runs the rule
+ # set after internal redirects; without END this rule
+ # would match its own output and loop.
+ # [QSA] appends the original query string, e.g.
+ # /samples/x.json?t=1 -> _file=x.json&t=1
+ # (auth_gate.php handles this form via strtok).
+ RewriteRule ^/samples/(.*)$ /samples/auth_gate.php?_file=$1 [END,QSA]
+
+ # 5. Everything else: main auth gate.
+ RewriteRule ^/(.*)$ /auth_gate.php?_file=$1 [END,QSA]
+
\ No newline at end of file