Merge pull request 'Rework Apache vhost for SSL-terminating reverse proxy' (#79) from holler/WebHosting:fix/redirect_issue into main

Reviewed-on: linux/WebHosting#79
This commit is contained in:
2026-07-16 09:44:05 +02:00
committed by gitadmin
+32 -41
View File
@@ -1,51 +1,33 @@
# ============================================================
# omny-test.psi.ch — HTTPS test instance
# omny-test.psi.ch — behind an SSL-terminating reverse proxy
#
# 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
# The proxy terminates HTTPS and forwards requests to this
# Apache as plain HTTP on port 80. Therefore:
# * everything (auth-gate rewrites, deny rules) lives in the
# *:80 vhost — there is no *:443 vhost on this host
# * the HTTP->HTTPS redirect fires ONLY when the proxy
# explicitly reports "X-Forwarded-Proto: http". If the
# header is absent, no redirect happens (loop-proof).
#
# 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.
# * mod_rewrite enabled. The vhost will intentionally FAIL
# to load without it, 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.
# * IMPORTANT: mod_remoteip configured with the proxy's IP
# (RemoteIPHeader X-Forwarded-For +
# RemoteIPInternalProxy <proxy-ip>), so REMOTE_ADDR shows
# the real client. Several endpoints authorize by client
# IP; without mod_remoteip they see only the proxy address.
# ============================================================
# ------------------------------------------------------------
# Port 80: redirect everything to HTTPS, except ACME challenges
# (exception only matters if certbot webroot renewal is used;
# harmless otherwise)
# ------------------------------------------------------------
<VirtualHost *:80>
ServerName omny-test.psi.ch
DocumentRoot /var/www/omny
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.
</VirtualHost>
# ------------------------------------------------------------
# Port 443: the actual site
# ------------------------------------------------------------
<VirtualHost *:443>
ServerName omny-test.psi.ch
DocumentRoot /var/www/omny
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"
<Directory "/var/www/omny">
Options +ExecCGI -Indexes
AllowOverride None
@@ -67,10 +49,19 @@
RewriteEngine On
# 0. URL-level deny before any rewriting.
# --------------------------------------------------------
# 0. HTTPS enforcement, loop-proof behind the proxy:
# redirect ONLY if the proxy explicitly reports that the
# original request was plain HTTP. If the proxy does not
# send X-Forwarded-Proto, this never matches -> no loop.
# --------------------------------------------------------
RewriteCond %{HTTP:X-Forwarded-Proto} =http
RewriteRule ^ https://omny-test.psi.ch%{REQUEST_URI} [R=301,L]
# 1. URL-level deny before any rewriting.
RewriteRule \.(htpasswd|gen)$ - [F,L]
# 1. Guard: forbid direct client requests that pass the
# 2. 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
@@ -79,7 +70,7 @@
RewriteCond %{QUERY_STRING} (^|&)_file=
RewriteRule ^/(samples/)?auth_gate\.php$ - [F,L]
# 2. Pass-throughs (no cookie check) — first match wins.
# 3. Pass-throughs (no cookie check) — first match wins.
RewriteRule ^/login\.php$ - [L]
RewriteRule ^/upload\.php$ - [L]
RewriteRule ^/session_query\.php$ - [L]
@@ -91,11 +82,11 @@
RewriteRule ^/samples/upload\.php$ - [L]
RewriteRule ^/samples/newmeasurement\.php$ - [L]
# 3. Normalize /samples -> /samples/ so it hits the samples
# 4. 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.
# 5. 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.
@@ -104,6 +95,6 @@
# (auth_gate.php handles this form via strtok).
RewriteRule ^/samples/(.*)$ /samples/auth_gate.php?_file=$1 [END,QSA]
# 5. Everything else: main auth gate.
# 6. Everything else: main auth gate.
RewriteRule ^/(.*)$ /auth_gate.php?_file=$1 [END,QSA]
</VirtualHost>
</VirtualHost>