From 760efc3297899a6daacb6b76eab25cce95bb623f Mon Sep 17 00:00:00 2001 From: Umut Kantarci Date: Mon, 12 Oct 2026 13:33:31 +0200 Subject: [PATCH] Stop the rewrite rule from looping on IONOS IONOS answered every rewritten address with a blank Apache 500: the !-f condition did not recognise the already rewritten index.php, so the rule fired again until Apache gave up. RewriteBase and an explicit stop for index.php, as in WordPress, fix it (ADR 114). Co-Authored-By: Claude Opus 5.5 --- docs/ARCHITECTURE.md | 10 +++++++++- docs/STATUS.md | 12 ++++++++++++ public/.htaccess | 7 +++++++ tests/CmsTest.php | 5 ++++- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 0a89a04..e3f0e8a 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,6 +1,14 @@ # Architekturentscheidungen -Stand: 09.10.2026. Arbeitstitel KantSites; Produktname offen. +Stand: 12.10.2026. Arbeitstitel KantSites; Produktname offen. + +## ADR 114 – Die Umschreibregel fängt index.php selbst ab (12.10.2026) + +Anlass: huefner-gartenbau.de bei IONOS (PHP 8.4.26) antwortete auf `/`, `/install` und `/assets/` mit einem weißen 500er von Apache. Die Notfallschicht (ADR 113) griff nicht, weil PHP gar nicht erreicht wurde. `/index.php/install` lieferte dagegen 200, auch mit der `.htaccess` – die Datei wurde also gelesen, der Fehler lag in der Umschreibregel. Vermutete Ursache: `RewriteCond %{REQUEST_FILENAME} !-f` erkennt dort die bereits umgeschriebene `index.php` nicht als Datei, die Regel greift erneut, Apache bricht nach zehn internen Umleitungen ab. + +**Entscheidung.** `public/.htaccess` setzt `RewriteBase /` und beendet die Umschreibung für `index.php` selbst (`RewriteRule ^index\.php$ - [L]`), wie die `.htaccess` von WordPress. Auf dem Hosting von Umut mit beiden Zeilen bestätigt; welche der beiden allein genügt hätte, ist nicht geprüft. `CmsTest` sichert die Zeilen ab. + +**Folgen.** `RewriteBase /` setzt voraus, dass die Domain auf `public/` zeigt – das war schon Bedingung, eine Installation in einem Unterordner einer Domain ist nicht vorgesehen. Bestehende Installationen bekommen die Zeilen mit dem nächsten Archivupdate, weil der Updater die `.htaccess` des Releases übernimmt (nur eingeschaltete HTTPS-/HSTS-Zeilen werden erhalten). `Options -Indexes -MultiViews` verträgt IONOS: auf huefner-gartenbau.de wieder eingeschaltet, die Seite lädt weiter. Ausgeliefert mit 1.28.1. ## ADR 113 – Kein leerer 500er mehr, Update mit einem Klick nach reiseSoft (09.10.2026) diff --git a/docs/STATUS.md b/docs/STATUS.md index f9bb821..e24bc2c 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,5 +1,17 @@ # Projektstatus +## Fassung 1.28.1: weißer 500er bei IONOS durch die Umschreibregel (12.10.2026) + +**Anlass:** huefner-gartenbau.de bei IONOS zeigte nach der Einrichtung auf jeder Adresse einen weißen 500er. + +**Ursache und Änderung (ADR 114):** PHP 8.4.26 lief (`test.php`), `/index.php/install` lieferte 200, nur umgeschriebene Adressen scheiterten – Umschreibschleife in `public/.htaccess`. Neu im Kern: `RewriteBase /` und `RewriteRule ^index\.php$ - [L]` vor der `!-f`-Regel; Test in `CmsTest::testEveryPublicAnswerCarriesItsProtectiveHeaders`. + +**Geprüft:** Umut hat beide Zeilen auf IONOS eingetragen, danach lädt `/install`; auch mit eingeschalteter `Options`-Zeile. PHPUnit vollständig: 643 Tests grün, eine Warnung aus `UpdaterTest` (fehlende `.env` im Testordner, unabhängig von dieser Änderung); php-cs-fixer sauber. + +**Offen:** Der Installer auf huefner-gartenbau.de war bis zum Abschluss der Einrichtung öffentlich erreichbar. + +**Nächster Schritt:** Bestehende IONOS-Installationen auf 1.28.1 aktualisieren. + ## Fassung 1.28.0: Notfallseite, Fehlerprotokoll und Update mit einem Klick (09.10.2026) **Anlass:** kantarci-reisen.de zeigt nach einer per FTP ersetzten Datenbank auf jeder Adresse eine leere 500-Seite. Umut: „Fang mit 1 und 2 an und verbessere den Update-Prozess … mach es wie bei reiseSoft.“ diff --git a/public/.htaccess b/public/.htaccess index 15a87a6..f80abb1 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -2,7 +2,14 @@ DirectoryIndex index.php Options -Indexes -MultiViews # Alles, was keine Datei ist, beantwortet PHP. +# +# Die Zeile fuer index.php selbst ist kein Zierrat: Bei IONOS erkennt !-f die bereits +# umgeschriebene index.php nicht als Datei, die Regel greift wieder und wieder, und Apache bricht +# nach zehn Runden mit einem weissen 500er ab (huefner-gartenbau.de, 12.10.2026). Dieselbe +# Absicherung steht in der .htaccess von WordPress. RewriteEngine On +RewriteBase / +RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] diff --git a/tests/CmsTest.php b/tests/CmsTest.php index 085af9d..ec1cf86 100644 --- a/tests/CmsTest.php +++ b/tests/CmsTest.php @@ -1618,7 +1618,10 @@ final class CmsTest extends TestCase // Und der Vordereingang verrät nicht, welche PHP-Fassung hier läuft. $index = (string) file_get_contents(__DIR__.'/../public/index.php'); self::assertStringContainsString("header_remove('X-Powered-By')", $index); - self::assertStringContainsString('Header always unset X-Powered-By', (string) file_get_contents(__DIR__.'/../public/.htaccess')); + $htaccess = (string) file_get_contents(__DIR__.'/../public/.htaccess'); + self::assertStringContainsString('Header always unset X-Powered-By', $htaccess); + // Ohne diese Zeile schreibt IONOS index.php endlos auf sich selbst um: weißer 500er (ADR 114). + self::assertStringContainsString("RewriteBase /\nRewriteRule ^index\\.php$ - [L]\nRewriteCond %{REQUEST_FILENAME} !-f", $htaccess); } public function testABackgroundPictureShrinksForSmallScreensEvenWhenSetOnce(): void