['onRequest', 48]]; } public function onRequest(RequestEvent $event): void { $request = $event->getRequest(); // Nur lesende Anfragen: ein 301 macht aus einem POST ein GET und verwirft die Eingabe. // Ein Formular auf der anderen Schreibweise darf noch ankommen; die Seite dahinter // fuehrt dann ohnehin auf die richtige Adresse. if (!$event->isMainRequest() || !\in_array($request->getMethod(), ['GET', 'HEAD'], true)) { return; } $target = self::target($this->siteOrigin, $request); if (null !== $target) { $event->setResponse(new RedirectResponse($target, 301, ['Cache-Control' => 'public, max-age=3600'])); } } /** Die Adresse, auf die diese Anfrage gehoert - oder null, wenn sie schon dort ist. */ public static function target(string $origin, Request $request): ?string { $parts = parse_url(trim($origin)); $wanted = strtolower((string) ($parts['host'] ?? '')); $scheme = strtolower((string) ($parts['scheme'] ?? '')); if ('' === $wanted || !\in_array($scheme, ['http', 'https'], true)) { return null; } try { $host = strtolower($request->getHost()); } catch (\UnexpectedValueException) { // Ein Host, den Symfony nicht annimmt, bekommt seine 400 vom Kernel - nicht von hier. return null; } if ($host === $wanted || !self::wwwTwins($host, $wanted)) { return null; } $port = isset($parts['port']) ? ':'.$parts['port'] : ''; return $scheme.'://'.$wanted.$port.$request->getRequestUri(); } /** `example.de` und `www.example.de` - und sonst nichts. */ private static function wwwTwins(string $a, string $b): bool { return 'www.'.$a === $b || $a === 'www.'.$b; } }