Avatar ·

Как получить полный URL текущей страницы в PHP?

Всем привет! Я начинающий PHP-разработчик, и у меня возникла проблема. Мне нужно получить полный URL текущей страницы, включая протокол (http или https), домен, путь и все GET-параметры. Я перепробовал несколько способов, но каждый раз что-то идёт не так. Пробовал использовать `$_SERVER['REQUEST_URI']` — он возвращает только путь, например `/page.php?id=5`, без домена и протокола. А `$_SERVER['HTTP_HOST']` даёт только домен, например `example.com`. Если я просто склею их через `echo "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI"];`, то при работе через HTTPS получится неправильный протокол — останется `http://`. Ещё я заметил, что `$_SERVER['HTTPS']` иногда не определён, особенно на локальном сервере. А если страница открыта через `www` или без `www` — это тоже влияет. Плюс, некоторые хостинги используют порты, например `localhost:8080`, и их нужно учитывать. В общем, я запутался. Подскажите, как правильно и надёжно собрать полный URL в PHP, чтобы он работал в любых условиях: и на локальном сервере, и на продакшене, и с HTTPS, и с портами? Заранее спасибо! Пример моего кода, который не работает: ```php $url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; echo $url; // Выдаёт http://example.com/page.php?id=5, даже если сайт на HTTPS ``` <<>>

Полное решение: универсальная функция для получения URL

Проблема, которую вы описали, действительно частая среди новичков. Главные сложности — определение протокола (HTTP/HTTPS), учёт порта и корректная обработка всех суперглобальных переменных. Вот надёжное решение, которое работает на 99% серверов:

function getFullUrl() {
    // Определяем протокол
    $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') 
                || $_SERVER['SERVER_PORT'] == 443 
                ? 'https://' 
                : 'http://';
    
    // Получаем домен (с портом, если он нестандартный)
    $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
    
    // Получаем путь и GET-параметры
    $uri = $_SERVER['REQUEST_URI'] ?? '/';
    
    // Собираем URL
    return $protocol . $host . $uri;
}

echo getFullUrl(); // Пример: https://example.com/page.php?id=5

Подробное объяснение

Разберём каждый шаг:

  • Протокол: Проверяем $_SERVER['HTTPS']. Если он равен 'on' (или не 'off'), то используем HTTPS. Дополнительно проверяем порт 443 — это стандартный порт HTTPS. Это нужно для случаев, когда $_SERVER['HTTPS'] не определён (например, на некоторых хостингах или при обратном прокси).
  • Домен: Используем $_SERVER['HTTP_HOST'] — он уже включает порт, если он нестандартный (например, localhost:8080). Если эта переменная пуста, падаем на $_SERVER['SERVER_NAME'], а в крайнем случае — на 'localhost'.
  • Путь: $_SERVER['REQUEST_URI'] содержит путь и все GET-параметры (например, /folder/page.php?id=5&name=test).

Улучшенная версия с учётом прокси

Если ваш сайт работает за обратным прокси (например, Nginx, Cloudflare), стандартные переменные могут быть недоступны. Добавьте проверку заголовков:

function getFullUrl() {
    // Определяем протокол через заголовки прокси (если есть)
    if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
        $protocol = $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ? 'https://' : 'http://';
    } else {
        $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') 
                    || $_SERVER['SERVER_PORT'] == 443 
                    ? 'https://' 
                    : 'http://';
    }
    
    // Домен: сначала проверяем заголовки прокси
    $host = $_SERVER['HTTP_X_FORWARDED_HOST'] 
            ?? $_SERVER['HTTP_HOST'] 
            ?? $_SERVER['SERVER_NAME'] 
            ?? 'localhost';
    
    $uri = $_SERVER['REQUEST_URI'] ?? '/';
    
    return $protocol . $host . $uri;
}

Важные замечания

  • Не используйте $_SERVER['SERVER_PORT'] в URL, если порт стандартный (80 для HTTP, 443 для HTTPS) — это создаст лишние символы. $_SERVER['HTTP_HOST'] уже содержит порт только в нестандартных случаях.
  • Всегда проверяйте существование ключей массива $_SERVER через ?? (null coalescing operator), чтобы избежать ошибок на разных серверах.
  • Для абсолютной надёжности можно использовать класс Symfony\Component\HttpFoundation\Request, если вы работаете с фреймворком. Но для чистого PHP приведённая функция — золотой стандарт.
<<>>

Альтернативный подход: использование фильтров и parse_url

Если вам нужно не просто вывести URL, а разобрать его на части или проверить валидность, можно использовать комбинацию filter_var и parse_url. Этот способ удобен, когда вы строите URL из отдельных компонентов:

function buildFullUrl() {
    $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
    $port = $_SERVER['SERVER_PORT'] ?? 80;
    
    // Собираем URL через parse_url и фильтр
    $url = $scheme . '://' . $host;
    if (($scheme === 'http' && $port != 80) || ($scheme === 'https' && $port != 443)) {
        $url .= ':' . $port;
    }
    $url .= $_SERVER['REQUEST_URI'] ?? '/';
    
    // Фильтруем для безопасности
    return filter_var($url, FILTER_VALIDATE_URL) ? $url : false;
}

$url = buildFullUrl();
if ($url) {
    echo "Полный URL: " . $url;
} else {
    echo "Не удалось собрать корректный URL";
}

Плюсы: filter_var проверяет, что URL действительно валидный — это защищает от ошибок, если $_SERVER содержит мусор. Минусы: Метод может отбрасывать некоторые корректные URL (например, с кириллическими доменами в Punycode). Используйте этот вариант, если вам важна безопасность, а не универсальность.