Force HTTPS from the admin, with a probe before and after

System > HTTPS and cms:https switch the redirect lines in public/.htaccess
only when the site answers over HTTPS, and take them back on a loop. HSTS
is a separate second switch. The redirect block moves before the front
controller rules, where it never reached pages, and .well-known is no
longer denied, so the origin probe works on Apache. The Git updater keeps
switched-on lines like the archive update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5.5 committed 2026-10-12 17:22:56 +02:00
1 parent daad5a1740
commit 8b212d5cc9
15 files changed
+614 -24

No files matched your search

+19 -8
View File
@@ -9,18 +9,25 @@ Options -Indexes -MultiViews
# Absicherung steht in der .htaccess von WordPress.
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
# HTTPS erzwingen. Auskommentiert, weil diese Datei auch in Installationen ohne Zertifikat
# liegt und eine Endlosschleife schlimmer waere als fehlendes HTTPS. Vor dem Livegang mit
# Zertifikat: die drei Zeilen einschalten - und erst danach die HSTS-Zeile weiter unten,
# denn HSTS laesst sich in einem Browser ein halbes Jahr lang nicht zuruecknehmen.
# liegt und eine Endlosschleife schlimmer waere als fehlendes HTTPS. Einschalten am besten in
# der Verwaltung unter System -> "HTTPS erzwingen": dort wird vorher und nachher geprueft und
# bei einer Schleife zurueckgenommen (ADR 120). Von Hand: die drei Zeilen einschalten - und erst
# danach die HSTS-Zeile weiter unten, denn HSTS laesst sich in einem Browser ein Jahr lang nicht
# zuruecknehmen.
#
# Die Zeilen stehen vor den Regeln fuer index.php, nicht dahinter: Nach dem Umschreiben auf
# index.php beginnt die Runde neu, und "^index\.php$ - [L]" beendet sie, bevor eine spaetere
# Regel an die Reihe kommt. Weiter unten griff die Umleitung nur fuer echte Dateien.
#RewriteCond %{HTTPS} !=on
#RewriteCond %{HTTP:X-Forwarded-Proto} !=https
#RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
# Eine Domain, eine Adresse - mit oder ohne www. Das erledigt die Anwendung selbst nach der
# Einstellung SITE_ORIGIN in .env: die jeweils andere Schreibweise antwortet mit 301 dorthin.
# Hier braucht es dafuer keine Regel; eine zweite Stelle wuerde nur auseinanderlaufen.
@@ -65,13 +72,17 @@ RewriteRule ^ index.php [L]
# ist fuer einen Besucher wertlos und fuer einen Scanner eine Einkaufsliste.
Header always unset X-Powered-By
Header always unset X-AspNet-Version
# Erst einschalten, wenn HTTPS steht und bleiben soll (siehe oben).
# Erst einschalten, wenn HTTPS steht und bleiben soll (siehe oben; System -> HSTS).
#Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</IfModule>
# Nichts ausliefern, was nur intern gebraucht wird - falls jemand das Paket versehentlich
# vollstaendig in den oeffentlichen Ordner legt statt nur public/.
<FilesMatch "^\.|\.(env|db|sqlite|lock|md|yaml|yml|dist|log)$">
# .well-known ausgenommen: Unter /.well-known/kantsites-origin prueft die Verwaltung, ob die
# Website unter einer Adresse hier ankommt (Adresse speichern, HTTPS erzwingen), und Apache
# prueft den ersten nicht vorhandenen Pfadteil gegen diese Regel - vorher antwortete die
# Probe mit 403. Zertifikatsanbieter (acme-challenge) brauchen den Ordner ebenso.
<FilesMatch "^\.(?!well-known$)|\.(env|db|sqlite|lock|md|yaml|yml|dist|log)$">
Require all denied
</FilesMatch>
# Eine Ausnahme mit Grund: der Editor liest die Liste der Gestaltungswerte als JSON.
+2
View File
@@ -697,6 +697,8 @@ details.card > summary:hover { background: #fafbf8 }
.origin-row { display: flex; flex-wrap: wrap; gap: 8px }
.origin-row input { flex: 1 1 240px; width: auto; box-sizing: border-box; min-height: 40px; height: 40px }
.card-body .origin-row .secondary { margin: 0 }
.https-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px }
.card-body .https-actions .secondary { margin: 0 }
.origin-form .check-label { margin-top: 10px }
.link-button { border: 0; background: none; padding: 0; min-height: 0; font: inherit; color: var(--accent); text-decoration: underline; cursor: pointer }
.check-list { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 10px; font-size: 13px; line-height: 1.55 }