PHP: aktuelle URL auslesen – sicher, mit HTTPS & Port
Christoph Schwob·3 Min. Lesezeit
Was ist neu: Snippet korrigiert (Port-Bug bei HTTPS, HTTP_HOST statt SERVER_NAME), Proxy- und Sicherheitshinweise sowie Framework-Varianten ergänzt.
Kurz & knapp
Die aktuelle URL setzt sich in PHP aus Protokoll, $_SERVER['HTTP_HOST'] und $_SERVER['REQUEST_URI'] zusammen. Den Host gegen eine Liste erlaubter Domains prüfen, hinter einem Proxy X-Forwarded-Proto auswerten und die URL bei der Ausgabe immer mit htmlspecialchars() maskieren. In Frameworks gibt es fertige Methoden, z. B. $request->getUri() in Symfony.
Getestet mit: PHP 8.5
Ob für Canonical-Tags, Weiterleitungen nach dem Login oder Share-Buttons: Die aktuell aufgerufene URL braucht man in PHP immer wieder. PHP liefert sie nicht als fertigen Wert – sie muss aus mehreren Einträgen in $_SERVER zusammengesetzt werden. Dabei gibt es ein paar Fallstricke, an denen auch die erste Version dieses Snippets gescheitert ist.
Das Snippet
<?php
declare(strict_types=1);
/**
* Liefert die aktuell aufgerufene URL, z. B. https://example.com/blog/?page=2
*
* @param string[] $allowedHosts erlaubte Hostnamen (Schutz vor Host-Header-Injection)
* @param bool $trustProxy X-Forwarded-Proto auswerten (nur hinter eigenem Proxy!)
*/
function getCurrentUrl(array $allowedHosts, bool $trustProxy = false): string
{
$https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| (int) ($_SERVER['SERVER_PORT'] ?? 0) === 443;
if ($trustProxy && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$https = strtolower(explode(',', $_SERVER['HTTP_X_FORWARDED_PROTO'])[0]) === 'https';
}
// HTTP_HOST enthält bei Nicht-Standard-Ports bereits den Port, z. B. "localhost:8080"
$host = strtolower($_SERVER['HTTP_HOST'] ?? '');
$hostname = preg_replace('/:\d+$/', '', $host);
if (!in_array($hostname, $allowedHosts, true)) {
$host = $allowedHosts[0];
}
return ($https ? 'https' : 'http') . '://' . $host . ($_SERVER['REQUEST_URI'] ?? '/');
}
Verwendung:
$url = getCurrentUrl(['example.com', 'www.example.com']);
// bei der Ausgabe in HTML immer maskieren!
echo '<link rel="canonical" href="' . htmlspecialchars($url, ENT_QUOTES) . '">';
Getestet mit PHP 8.5 – Ergebnisse für typische Server-Konstellationen:
| Situation | Ergebnis |
|---|---|
| HTTPS auf Port 443 | https://example.com/blog/?page=2 |
| HTTP auf Port 8080 | http://example.com:8080/a |
hinter Proxy mit X-Forwarded-Proto: https |
https://example.com/ |
gefälschter Host evil.test |
https://example.com/x (Fallback auf erlaubte Domain) |
HTTPS = off |
http://www.example.com/ |
Was am alten Snippet falsch war
Die ursprüngliche Version aus 2017 setzte die URL aus SERVER_NAME und SERVER_PORT zusammen und hängte den Port an, sobald er nicht 80 war. Das führte zu drei Problemen:
- Port-Bug bei HTTPS: Auf Port 443 entstand
https://example.com:443/…– technisch gültig, aber unschön und für Canonical-URLs ungeeignet. - Falscher Host:
SERVER_NAMEstammt aus der Webserver-Konfiguration und ist nicht zwingend die Domain, die aufgerufen wurde – etwa bei Aliases oderwww-Varianten. HTTPS = off: Laut PHP-Handbuch istHTTPSbei verschlüsselten Aufrufen „nicht leer“. Manche Server-Setups setzen den Wert bei HTTP aber explizit aufoff– ein reinesisset()hält das fälschlich für HTTPS. Das neue Snippet prüft deshalb auch darauf.
Welcher $_SERVER-Wert enthält was?
Beispiel-Aufruf: https://example.com:8443/blog/artikel.php?page=2
| Schlüssel | Inhalt |
|---|---|
HTTP_HOST |
example.com:8443 – Host inkl. Port (vom Client gesendet) |
SERVER_NAME |
Hostname laut Webserver-Konfiguration |
SERVER_PORT |
8443 |
HTTPS |
nicht leer (meist on), wenn per HTTPS aufgerufen |
REQUEST_URI |
/blog/artikel.php?page=2 – Pfad inkl. Query-String |
QUERY_STRING |
page=2 |
SCRIPT_NAME |
/blog/artikel.php |
Hinter Proxy, Load Balancer oder Cloudflare
Läuft die Seite hinter einem Reverse Proxy – Cloudflare, ein Load Balancer oder auch ein lokaler Docker-Router –, endet die verschlüsselte Verbindung oft schon dort. PHP sieht dann eine normale HTTP-Verbindung, und $_SERVER['HTTPS'] ist leer. Das ursprüngliche Protokoll übermittelt der Proxy im Header X-Forwarded-Proto, in PHP als $_SERVER['HTTP_X_FORWARDED_PROTO'].
So geht’s in Frameworks
Frameworks erledigen all das bereits – inklusive Proxy-Erkennung, sofern die vertrauenswürdigen Proxies konfiguriert sind.
TYPO3
In TYPO3 stellt der PSR-7-Request die normalisierten Server-Parameter bereit. Sie berücksichtigen auch Reverse-Proxy-Konstellationen und ersetzen das ältere GeneralUtility::getIndpEnv():
/** @var \TYPO3\CMS\Core\Http\NormalizedParams $normalizedParams */
$normalizedParams = $request->getAttribute('normalizedParams');
$url = $normalizedParams->getRequestUrl(); // z. B. https://example.com/blog/?page=2
$uri = $normalizedParams->getRequestUri(); // z. B. /blog/?page=2
Symfony
$url = $request->getUri(); // mit Query-String
$base = $request->getSchemeAndHttpHost(); // https://example.com
Hinter einem Proxy müssen dessen IP-Adressen über trusted_proxies bzw. Request::setTrustedProxies() freigegeben sein.
Laravel
$url = url()->current(); // ohne Query-String
$full = url()->full(); // mit Query-String
// oder über das Request-Objekt
$url = $request->url();
$full = $request->fullUrl();Häufige Fragen
Was ist der Unterschied zwischen HTTP_HOST und SERVER_NAME?
HTTP_HOST ist der Host, den der Browser im Request mitschickt – inklusive eines eventuellen Ports. SERVER_NAME kommt aus der Webserver-Konfiguration und muss nicht der aufgerufenen Domain entsprechen. Für die aktuelle URL ist HTTP_HOST richtig, sollte aber gegen erlaubte Domains geprüft werden.
Wie bekomme ich die URL ohne Query-String?
Mit strtok($_SERVER['REQUEST_URI'], '?') oder parse_url($url, PHP_URL_PATH) erhältst du nur den Pfad ohne ?….
Warum erkennt PHP hinter Cloudflare oder einem Load Balancer kein HTTPS?
Weil die Verbindung zwischen Proxy und Webserver oft unverschlüsselt ist. Das ursprüngliche Protokoll steht dann im Header X-Forwarded-Proto, den man aber nur auswerten darf, wenn der Request wirklich vom eigenen Proxy kommt.
Muss ich die aktuelle URL bei der Ausgabe maskieren?
Ja. REQUEST_URI enthält vom Besucher kontrollierte Zeichen. Bei der Ausgabe in HTML immer htmlspecialchars($url, ENT_QUOTES) verwenden, sonst droht Cross-Site-Scripting (XSS).
Quellen & weiterführende Links
- $_SERVER – Server- und Ausführungsumgebungsinformationen (öffnet in neuem Tab) – PHP-Handbuch
- htmlspecialchars (öffnet in neuem Tab) – PHP-Handbuch
- X-Forwarded-Proto (öffnet in neuem Tab) – MDN Web Docs
- Normalized parameters (normalizedParams) (öffnet in neuem Tab) – TYPO3 Dokumentation
- How to Configure Symfony to Work behind a Load Balancer or a Reverse Proxy (öffnet in neuem Tab) – Symfony Dokumentation
- Request.php (HttpFoundation) (öffnet in neuem Tab) – Symfony auf GitHub
- URL Generation (öffnet in neuem Tab) – Laravel Dokumentation
Geschrieben von
Christoph Schwob (öffnet in neuem Tab)
Ich bin Christoph Schwob, halbkreativer Entwickler aus Österreich und arbeite seit über 10 Jahren an und mit TYPO3. Hier sammle ich Lösungen, Code-Snippets und Notizen rund um TYPO3, macOS, Server, Homelab und alles andere.
Das könnte dich auch interessieren
- DDEV: Staging-Datenbank per ddev pull importieren (TYPO3) – 27. Juli 2022
- ASCII-Tabelle: alle 128 Zeichen mit Dezimal-, Hex- und Binärwerten – 21. Februar 2021
- TYPO3: Open-Graph-Daten von jh_opengraphprotocol zu EXT:seo migrieren – 18. März 2025