Warum meine robots.txt-Route in Laravel nie antwortete
Die robots.txt dieser Website wird erzeugt, nicht von Hand gepflegt. Die
Route war geschrieben, 37 Feature-Tests dazu waren grün — und ausgeliefert
wurde trotzdem eine ganz andere Datei. Der Fehler lag nicht im Code,
sondern in der Lücke zwischen dem, was ein Test prüft, und dem, was ein
Crawler bekommt.
Was ausgeliefert wurde und was hätte ausgeliefert werden sollen
Die Route erzeugt eine robots.txt mit genau einer Gruppe für alle
Crawler, einigen gesperrten Pfaden und einem Verweis auf die Sitemap. Ein echter
HTTP-Abruf gegen den laufenden Server zeigte stattdessen das hier:
User-agent: *
Disallow:
Das ist die Datei, die Laravel beim Anlegen eines neuen Projekts unter
public/robots.txt mitbringt. Sie ist nicht falsch — sie erlaubt alles —,
aber sie hat mit der erzeugten Datei nichts zu tun. Weder die Sitemap noch
die gesperrten Pfade standen darin.
Warum der Webserver die Route nie erreicht
Ein Laravel-Projekt wird so ausgeliefert, dass der Webserver zuerst im
Ordner public/ nachsieht. Existiert der angefragte Pfad dort als Datei,
liefert er sie direkt aus. Erst wenn keine Datei passt, geht der Request an
index.php — und damit an Laravels Routing.
Das gilt in der üblichen Konfiguration für Caddy, nginx und Apache ebenso wie
für den Entwicklungsserver.
php artisan serve nutzt dafür ein kleines Router-Skript, dessen
entscheidende Zeile so aussieht:
if ($uri !== '/' && file_exists($publicPath.$uri)) {
return false;
}
return false bedeutet: Der eingebaute PHP-Server liefert die Datei
selbst aus. Die Anwendung wird für diesen Request gar nicht erst gestartet.
Warum 37 grüne Tests nichts davon bemerkt haben
Laravels Feature-Tests schicken keinen echten HTTP-Request. $this->get('/robots.txt')
ruft den HTTP-Kernel der Anwendung direkt auf — und der kennt nur Routen,
keine Dateien in public/. Der Test fragt also genau die Stelle, die im
Betrieb nie gefragt wird.
| Prüfweg | Was er sieht | Hätte den Fehler gefunden |
|---|---|---|
| Pest-Feature-Test | den HTTP-Kernel | nein |
curl gegen den laufenden Server |
die tatsächliche Auslieferung | ja |
| Playwright gegen den laufenden Server | die tatsächliche Auslieferung | ja |
Die Tests waren nicht falsch. Sie haben nur eine andere Frage beantwortet als die, die gestellt werden musste.
Die Behebung in zwei Schritten
Der erste Schritt ist trivial: public/robots.txt löschen. Danach
erreicht der Request das Routing, und die erzeugte Datei wird
ausgeliefert.
Der zweite Schritt ist der wichtigere. Ein neuer Browser-Test ruft jeden
Maschinen-Endpunkt über den laufenden Server ab und prüft Inhalt und
Content-Type. Für robots.txt gibt es zusätzlich eine gezielte Prüfung
auf die Laravel-Standarddatei:
test('robots.txt stammt aus der Anwendung, nicht aus public/', async ({ request }) => {
// Laravels Standarddatei beginnt ohne Kommentarzeile und enthält ein
// leeres `Disallow:`. Findet sich das, ist die Route überschattet.
const body = await (await request.get('/robots.txt')).text();
expect(body).toContain('# alexander-seidler.com');
expect(body).not.toMatch(/^Disallow:\s*$/m);
});
Der entscheidende Unterschied zu den Feature-Tests: Playwright startet einen echten Server und stellt echte HTTP-Anfragen. Liegt je wieder eine Datei im Weg, schlägt dieser Test an, egal wie viele Feature-Tests grün sind.
Was ich daraus mitnehme
Ein grüner Feature-Test ist eine Aussage über die Anwendung, nicht über die Auslieferung. Für die meisten Seiten ist das dasselbe. Für Pfade, die auch als Datei existieren können, ist es das nicht — und genau dort liegen die Dateien, die Suchmaschinen und KI-Crawler zuerst abrufen.
Wie die übrigen Tests dieser Website aufgebaut sind, beschreibt die Fallstudie zu alexander-seidler.com. Um Cookies und Middleware-Gruppen geht es im Artikel Warum meine Laravel-Startseite kein Cookie setzt.
Quellen: PHP — Eingebauter Webserver · Laravel 13 — HTTP-Tests
Häufige Fragen zum Artikel
Warum ignoriert Laravel meine Route für robots.txt?
Weil im Ordner public eine gleichnamige Datei liegt. Der Webserver prüft zuerst, ob der Pfad als Datei existiert, und liefert sie direkt aus. Erst wenn keine Datei passt, geht der Request an index.php und damit an das Routing. Die Route existiert also, wird aber nie erreicht.
Warum sind meine Pest-Tests grün, obwohl die falsche Datei ausgeliefert wird?
Feature-Tests rufen den HTTP-Kernel von Laravel direkt auf. Die Dateiauslieferung des Webservers liegt davor und wird dabei übersprungen. Ein grüner Feature-Test sagt deshalb nur etwas über die Anwendung aus, nicht darüber, was ein Crawler vom laufenden Server tatsächlich bekommt.
Welche Pfade sind außer robots.txt betroffen?
Jeder Pfad, den es auch als Datei in public geben könnte, etwa sitemap.xml, humans.txt, llms.txt oder favicon.ico. Wer einen dieser Pfade als Route erzeugt, sollte sicherstellen, dass keine gleichnamige Datei existiert, und das mit einem echten HTTP-Abruf prüfen.