Извлечение URL из HTTP-запроса в PHP

Раздел: Веб-программирование -> Обработка HTTP-запросов

Получение текущего URL веб-страницы является одной из базовых задач при обработке HTTP-запросов в PHP. Знание того, как извлечь полный адрес, путь, домен или протокол, необходимо для построения редиректов, формирования абсолютных ссылок, логирования и динамической настройки контента. В статье рассмотрены различные способы получения URL с примерами кода и указанием возможных проблем.

Основное решение: функция получения полного URL

Наиболее универсальный способ получить текущий полный URL (включая протокол, домен, порт, путь и параметры запроса) заключается в комбинировании нескольких суперглобальных переменных $_SERVER. Ниже приведена функция, которая корректно обрабатывает HTTP и HTTPS, нестандартные порты и учитывает возможное использование обратного прокси.


function getCurrentUrl(): string {
    $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
    $port = $_SERVER['SERVER_PORT'];
    // Добавляем порт только если он нестандартный
    if (($protocol === 'http' && $port != 80) || ($protocol === 'https' && $port != 443)) {
        $host .= ':' . $port;
    }
    $uri = $_SERVER['REQUEST_URI'];
    return $protocol . '://' . $host . $uri;
}

получить get php (получение get-параметров в php)

Пояснение шагов:

  • Определение протокола: проверяется переменная $_SERVER['HTTPS']. Если она не пуста и не равна 'off', значит соединение по HTTPS.
  • Домен: сначала берется $_SERVER['HTTP_HOST'], который содержит имя хоста и порт (если он указан в запросе). Если эта переменная недоступна, используется $_SERVER['SERVER_NAME'].
  • Порт: $_SERVER['SERVER_PORT'] содержит порт, на котором работает сервер. Добавление порта в URL выполняется только для нестандартных портов (80 для HTTP, 443 для HTTPS).
  • Путь и параметры: $_SERVER['REQUEST_URI'] содержит строку запроса (например, /page?key=value).
  • Результат собирается в единую строку.

Типичные проблемы и их решение

  • Некорректное определение HTTPS при использовании обратного прокси: прокси-сервер может передавать исходный протокол через заголовок X-Forwarded-Proto. Для корректной работы следует дополнительно проверять этот заголовок:
    $protocol = 'http';
    if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
        $protocol = 'https';
    } elseif (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
        $protocol = 'https';
    }

    Php получить html (получение html-кода в php)

  • Переменная HTTP_HOST может содержать порт: если в запросе был указан порт (например, example.com:8080), он уже включён. В этом случае повторное добавление порта из SERVER_PORT может привести к дублированию. Рекомендуется анализировать HTTP_HOST на наличие порта с помощью parse_url или регулярного выражения.
  • Различия между HTTP_HOST и SERVER_NAME: HTTP_HOST получается из заголовка Host запроса клиента, SERVER_NAME берётся из конфигурации сервера. HTTP_HOST предпочтительнее, так как соответствует тому, что ввёл пользователь, но может быть подделан. Для критичных операций (например, генерация подписей) стоит использовать SERVER_NAME после валидации.
  • Отсутствие переменной REQUEST_URI: в некоторых окружениях (например, при использовании PHP из командной строки) REQUEST_URI может быть не определена. В таких случаях следует предусмотреть значение по умолчанию '/'.

Различные варианты получения частей URL

Как получить только путь запроса?

Если требуется только путь и параметры (без домена и протокола), используется переменная $_SERVER['REQUEST_URI'].


$requestUri = $_SERVER['REQUEST_URI'];
echo $requestUri; // /page?foo=bar

Php получить id (получение id в php)

Цель: получение относительного пути для маршрутизации, формирования ссылок относительно корня.

Ошибка: REQUEST_URI может быть не установлен при запуске из CLI. Решение - проверять с помощью isset() и задавать значение '/'.

Как получить домен сайта?

Для извлечения имени хоста применяются $_SERVER['HTTP_HOST'] или $_SERVER['SERVER_NAME'].


$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
echo $host; // example.com:8080 или просто example.com

получаем url php (получение url в php)

Цель: построение абсолютных ссылок, проверка соответствия ожидаемому домену.

Проблема: HTTP_HOST может включать порт (example.com:8080). При необходимости отделить порт можно воспользоваться функцией parse_url().

Как определить протокол (HTTP или HTTPS)?

Проверка $_SERVER['HTTPS'] является стандартным способом.


$isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
echo $isHttps ? 'https' : 'http';

Php получить адрес (получение адреса в php)

Цель: выбор протокола при формировании URL, принудительное перенаправление на HTTPS.

Ошибка: при использовании прокси (nginx, Varnish) переменная HTTPS часто не установлена. Следует проверять заголовок X-Forwarded-Proto или X-Forwarded-Scheme.

Как получить URL без параметров запроса?

Иногда требуется путь без query-строки. Это можно сделать с помощью strtok() или parse_url().


// Способ 1: strtok
$path = strtok($_SERVER['REQUEST_URI'], '?');

// Способ 2: parse_url
$parsed = parse_url($_SERVER['REQUEST_URI']);
$path = $parsed['path'];

echo $path; // /page

Php получить данные (получение данных в php)

Цель: роутинг по страницам без учёта параметров, получение «чистого» URL.

Нюанс: если в URL есть якорь (#), он не передаётся на сервер, поэтому parse_url его не отобразит. Для клиентской части якорь доступен через JavaScript.

Как получить URL с учётом порта?

Порт можно получить из $_SERVER['SERVER_PORT']. При сборке полного URL следует добавлять порт, только если он нестандартный.


$port = $_SERVER['SERVER_PORT'];
$host = $_SERVER['HTTP_HOST'];
// Если в host уже есть порт, не добавляем повторно
if (strpos($host, ':') === false && (($port != 80 && $_SERVER['HTTPS'] != 'on') || ($port != 443 && $_SERVER['HTTPS'] == 'on'))) {
    $host .= ':' . $port;
}
$url = ($_SERVER['HTTPS'] ? 'https' : 'http') . '://' . $host . $_SERVER['REQUEST_URI'];

Php получить заголовок (получение заголовка в php)

Цель: точное воспроизведение текущего адреса, особенно на серверах с нестандартными портами.

Проблема: если HTTP_HOST уже содержит порт (например, example.com:8080), его повторное добавление из SERVER_PORT создаст некорректный URL example.com:8080:8080. Нужна проверка на наличие порта в HTTP_HOST.

Как обрабатывать URL при использовании обратного прокси?

При размещении PHP-приложения за обратным прокси (nginx, Apache mod_proxy) реальный протокол, порт и даже хост могут быть переданы через специальные заголовки. Для правильного получения URL следует анализировать заголовки X-Forwarded-*.


function getProxiedUrl(): string {
    $protocol = $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
    $host = $_SERVER['HTTP_X_FORWARDED_HOST'] ?? $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
    $port = $_SERVER['HTTP_X_FORWARDED_PORT'] ?? $_SERVER['SERVER_PORT'];
    // ... сборка URL с учётом порта, аналогично базовой функции
    return $protocol . '://' . $host . ($port ? ':' . $port : '') . $_SERVER['REQUEST_URI'];
}

Цель: получение исходного URL, который видит клиент, для корректных редиректов и генерации ссылок.

Важно: доверять заголовкам X-Forwarded-* можно только в том случае, если прокси-сервер настроен на их проверку и удаление из внешних запросов. В противном случае злоумышленник может подменить URL. Рекомендуется комбинировать проверку IP-адреса доверенных прокси.

Цели и случаи использования каждого варианта

  • Полный URL - для редиректов (header('Location: ...')), формирования абсолютных ссылок в письмах или RSS, логирования запросов.
  • Только путь - маршрутизация, определение активного пункта меню, формирование относительных ссылок.
  • Домен - проверка текущего сайта при мультидоменной конфигурации, построение ссылок на статические ресурсы.
  • Протокол - принудительное использование HTTPS, выбор схемы при загрузке ресурсов (http vs https).
  • URL без параметров - для кэширования страниц, сравнения URL без учёта query-строки.
  • Порт - в средах разработки или нестандартных конфигурациях, где порт является частью адреса.
  • Обратное прокси - при использовании балансировщиков или кэширующих прокси (Varnish, Cloudflare) для получения реального адреса посетителя.
- получить ссылку php (получение ссылки в php)

Расширенные примеры получения URL

Пример 1: Функция получения полного URL с поддержкой X-Forwarded-*

Функция учитывает заголовки обратного прокси, что позволяет корректно работать за Cloudflare, Nginx и другими реверс-прокси. Используется доверенный IP-адрес прокси (в примере 127.0.0.1 для локального теста).

Пример

function getFullUrl(): string {
    $trustedProxies = ['127.0.0.1', '::1']; // доверенные прокси
    $ip = $_SERVER['REMOTE_ADDR'];
    $isTrusted = in_array($ip, $trustedProxies, true);
    
    // Протокол
    if ($isTrusted && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
        $protocol = $_SERVER['HTTP_X_FORWARDED_PROTO'];
    } elseif (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
        $protocol = 'https';
    } else {
        $protocol = 'http';
    }
    
    // Хост (с возможным портом)
    if ($isTrusted && isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
        $host = $_SERVER['HTTP_X_FORWARDED_HOST'];
    } else {
        $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
    }
    
    // Порт
    if ($isTrusted && isset($_SERVER['HTTP_X_FORWARDED_PORT'])) {
        $port = $_SERVER['HTTP_X_FORWARDED_PORT'];
    } else {
        $port = $_SERVER['SERVER_PORT'];
    }
    
    // Проверка, не содержит ли host уже порт
    if (strpos($host, ':') === false) {
        $defaultPorts = ['http' => 80, 'https' => 443];
        if (isset($defaultPorts[$protocol]) && $port != $defaultPorts[$protocol]) {
            $host .= ':' . $port;
        }
    }
    
    $uri = $_SERVER['REQUEST_URI'];
    return $protocol . '://' . $host . $uri;
}

echo getFullUrl();
https://example.com:8080/path?query=1

Пояснение: Функция проверяет, пришёл ли запрос от доверенного прокси. Если да, используются заголовки X-Forwarded-*. В противном случае применяются стандартные переменные. Это предотвращает подделку URL через подставные заголовки.

Пример 2: Разбор URL на компоненты с parse_url

Функция parse_url() разбивает URL на составные части: схема, хост, порт, путь, параметры и якорь (якорь не доступен на серверной стороне).

Пример

$url = 'https://user:pass@example.com:8080/path/to/page?name=value#anchor';
$parts = parse_url($url);
var_dump($parts);
array(8) {
  ["scheme"]=> string(5) "https"
  ["host"]=> string(11) "example.com"
  ["port"]=> int(8080)
  ["user"]=> string(4) "user"
  ["pass"]=> string(4) "pass"
  ["path"]=> string(13) "/path/to/page"
  ["query"]=> string(10) "name=value"
  ["fragment"]=> string(6) "anchor"
}

Пояснение: Результат можно использовать для извлечения отдельных компонентов. Для текущего URL нужно подставить результат из функции getFullUrl(). Однако якорь (fragment) не передаётся на сервер, поэтому для его получения потребуется JavaScript.

Пример 3: Получение базового URL (схема + домен + порт)

Базовый URL часто требуется для построения абсолютных путей к ресурсам. Ниже приведена функция, возвращающая только схему, хост и порт (если нестандартный).

Пример

function getBaseUrl(): string {
    $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
    // Если в host нет порта, добавляем его при необходимости
    if (strpos($host, ':') === false) {
        $port = $_SERVER['SERVER_PORT'];
        if (($protocol === 'http' && $port != 80) || ($protocol === 'https' && $port != 443)) {
            $host .= ':' . $port;
        }
    }
    return $protocol . '://' . $host;
}

echo getBaseUrl();
https://example.com:8080

Пояснение: Такая функция удобна для передачи в шаблоны, чтобы не дублировать логику определения протокола и порта.

Пример 4: Получение чистого пути без параметров запроса

Для маршрутизации часто нужен только путь без строки запроса. Используется parse_url() или strtok().

Пример

$requestUri = $_SERVER['REQUEST_URI'] ?? '/';
$cleanPath = parse_url($requestUri, PHP_URL_PATH);
echo $cleanPath;
/example/page

Пояснение: Второй параметр PHP_URL_PATH в parse_url позволяет извлечь только путь. Это надёжнее, чем strtok, так как parse_url корректно обрабатывает закодированные символы и не теряет фрагменты (хотя фрагменты всё равно не передаются на сервер). Если URL содержит символ ? в самом пути (например, /page?foo=bar?baz), parse_url выделит первую часть как путь, а остальное как query. Такие ситуации редки, но их следует учитывать.

Пример 5: Формирование URL из массива компонентов с http_build_url

Существует пользовательская функция http_build_url() (из PECL pecl_http), которая собирает URL из массива компонентов, подобного возвращаемому parse_url. Если расширение не установлено, можно реализовать простую замену:

Пример

// Альтернатива без расширения
function buildUrl(array $parts): string {
    $url = '';
    if (isset($parts['scheme'])) {
        $url .= $parts['scheme'] . '://';
    }
    if (isset($parts['user'])) {
        $url .= $parts['user'];
        if (isset($parts['pass'])) {
            $url .= ':' . $parts['pass'];
        }
        $url .= '@';
    }
    if (isset($parts['host'])) {
        $url .= $parts['host'];
    }
    if (isset($parts['port'])) {
        $url .= ':' . $parts['port'];
    }
    if (isset($parts['path'])) {
        $url .= $parts['path'];
    }
    if (isset($parts['query'])) {
        $url .= '?' . $parts['query'];
    }
    if (isset($parts['fragment'])) {
        $url .= '#' . $parts['fragment'];
    }
    return $url;
}

$components = parse_url('https://example.com:8080/path?query=1#anchor');
$built = buildUrl($components);
echo $built;
https://example.com:8080/path?query=1#anchor

Пояснение: Подобная функция позволяет модифицировать отдельные части URL (например, заменить путь или добавить параметр) и собрать новый URL. Это удобно для манипуляции ссылками внутри приложения.

Получение URL в PHP - comments

En
получаем url php (php)