Полный URL в PHP: как получить быстро и без ошибок

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Get the full URL in PHP: как не изобретать велосипед и не потерять производительность

Каждый PHP-разработчик рано или поздно сталкивается с задачей: «А как мне получить полный URL текущей страницы?». Казалось бы, что тут сложного — взял $_SERVER['REQUEST_URI'], добавил домен — и готово. Но дьявол, как всегда, в деталях. Вопрос «Get the full URL in PHP» на Stack Overflow собрал тысячи просмотров и десятки ответов. И это неспроста.

В этой статье мы разберём как получить полный URL в PHP правильно, быстро и без багов. Вы узнаете, чем отличается HTTP_HOST от SERVER_NAME, как правильно определить протокол (HTTPS или HTTP), и почему не стоит слепо копировать код из интернета. Поехали!

Почему простой способ — не всегда лучший?

Самый очевидный вариант выглядит так:

<?php
$fullUrl = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
?>

Этот код работает, но только если у вас:

  • Обязательно HTTP (не HTTPS)
  • Нет порта, отличного от 80 или 443
  • Сервер корректно передаёт заголовок Host

В реальном мире эти условия выполняются далеко не всегда. Если сайт работает через HTTPS, вы получите URL с http://, что приведёт к ошибке смешанного контента в браузере. Если порт нестандартный (например, 8080), он потеряется. А если злоумышленник подделает заголовок Host, вы можете получить URL на чужой домен.

Компоненты полного URL: разбор полётов

Прежде чем писать универсальное решение, давайте вспомним, из чего состоит полный URL:

scheme://host:port/path?query#fragment

В PHP нам доступны следующие элементы из суперглобального массива $_SERVER:

  • scheme — протокол (HTTP или HTTPS). Прямого ключа нет, но можно определить через $_SERVER['HTTPS'] или $_SERVER['REQUEST_SCHEME'] (доступен с PHP 7.4).
  • host — домен. Есть два кандидата: HTTP_HOST (из заголовка Host) и SERVER_NAME (из конфигурации сервера).
  • port — порт. $_SERVER['SERVER_PORT']. Если порт стандартный (80 для HTTP, 443 для HTTPS), его можно опустить.
  • path — путь. $_SERVER['REQUEST_URI'] содержит путь и query-строку.
  • query — строка запроса. Можно получить отдельно через $_SERVER['QUERY_STRING'].
  • fragment — якорь. Он не передаётся на сервер, поэтому его не получить.

Правильное решение: функция getFullUrl()

Вот надёжный и производительный способ получить полный URL в PHP:

<?php
function getFullUrl(): string {
    // Определяем протокол
    $scheme = 'http';
    if (
        (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] === '1'))
        || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
    ) {
        $scheme = 'https';
    }

    // Хост: предпочитаем HTTP_HOST, но проверяем на пустоту
    $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';

    // Порт: добавляем только если нестандартный
    $port = (int) ($_SERVER['SERVER_PORT'] ?? 80);
    $portStr = '';
    if (
        ($scheme === 'http' && $port !== 80) ||
        ($scheme === 'https' && $port !== 443)
    ) {
        $portStr = ':' . $port;
    }

    // URI
    $uri = $_SERVER['REQUEST_URI'] ?? '/';

    return $scheme . '://' . $host . $portStr . $uri;
}
?>

Эта функция учитывает:

  • Протокол HTTPS (в том числе через прокси-серверы с заголовком X-Forwarded-Proto).
  • Нестандартные порты.
  • Отсутствие заголовка Host (падение на локальном сервере).
  • Пустой REQUEST_URI (крайний случай).

Производительность: что быстрее?

Многие разработчики пишут что-то вроде:

<?php
$fullUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http')
    . '://' . $_SERVER['HTTP_HOST']
    . $_SERVER['REQUEST_URI'];
?>

Этот код короче, но он не проверяет порт и может работать некорректно за прокси. Если вам не нужна поддержка прокси и нестандартных портов, используйте упрощённый вариант — он быстрее. Но если сайт работает в облаке или за балансировщиком (AWS, nginx, Cloudflare), обязательно используйте полную версию.

Совет по оптимизации: если вы вызываете getFullUrl() много раз (например, в цикле или в каждом шаблоне), кешируйте результат в статической переменной или в реестре приложения:

<?php
function getFullUrlCached(): string {
    static $url = null;
    if ($url === null) {
        $url = getFullUrl();
    }
    return $url;
}
?>

Это сократит количество обращений к $_SERVER до одного за запрос.

HTTP_HOST vs SERVER_NAME: что выбрать?

Это извечный вопрос. Давайте разберёмся:

  • HTTP_HOST — значение заголовка Host из HTTP-запроса. Может быть подделано клиентом (но в большинстве случаев это не проблема для обычных сайтов).
  • SERVER_NAME — имя сервера из конфигурации Apache/Nginx. Безопаснее, но может не совпадать с реальным доменом, если настроен виртуальный хост.

На практике рекомендуется использовать HTTP_HOST, так как он всегда содержит то, что ввёл пользователь. Если вы опасаетесь подделки (например, в критичных системах), добавьте проверку белого списка доменов:

<?php
$allowedHosts = ['site.ru', 'www.site.ru'];
$host = $_SERVER['HTTP_HOST'];
if (!in_array($host, $allowedHosts, true)) {
    $host = $allowedHosts[0]; // или выбросить исключение
}
?>

Как быть с URL без query-строки?

Иногда нужно получить только путь без параметров. Для этого можно использовать parse_url():

<?php
$fullUrl = getFullUrl();
$urlWithoutQuery = parse_url($fullUrl, PHP_URL_SCHEME) .                

Блоги

Рекомендаций по книгам