Практическое получение веб-адреса в PHP: методы и нюансы
Основные способы получения URL в PHP
Как собрать полный URL текущей страницы?
Наиболее универсальное решение - написать собственную функцию, которая объединяет все необходимые переменные $_SERVER. Это позволяет учесть протокол, хост, порт и путь с параметрами.
function getCurrentUrl() {
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
$port = $_SERVER['SERVER_PORT'] ?? 80;
$uri = $_SERVER['REQUEST_URI'] ?? '/';
// Добавляем порт только если он нестандартный
$portPart = (($protocol === 'http' && $port != 80) || ($protocol === 'https' && $port != 443)) ? ':' . $port : '';
return $protocol . '://' . $host . $portPart . $uri;
}
echo getCurrentUrl();
Php get client (получить данные клиента в php)
http://example.com/path/to/page?param=1
Php get url (получить url в php)
Функция проверяет наличие HTTPS, определяет хост из HTTP_HOST (или резервного SERVER_NAME), добавляет порт только если он отличается от стандартного для протокола, и возвращает полный URL.
Типичные ошибки:
- Если скрипт работает через обратный прокси, переменная HTTPS может быть не установлена. В таком случае нужно проверять заголовок HTTP_X_FORWARDED_PROTO.
- Если сайт доступен по нескольким доменам, HTTP_HOST вернет тот, который указал клиент. Для коррекции нужно использовать SERVER_NAME (если настроен корректно).
- При работе с командной строкой (CLI) переменные $_SERVER могут отсутствовать. Следует добавить проверки на существование.
Как получить только путь и параметры (относительный URL)?
echo $_SERVER['REQUEST_URI'];
/index.php?utm_source=twitter
Переменная REQUEST_URI содержит путь от корня домена вместе с query-строкой. Это самый простой способ получить относительный адрес.
Ошибка: в некоторых конфигурациях Apache может отсутствовать, если не используется mod_rewrite или неправильно настроен FallbackResource. Решение - использовать $_SERVER['PATH_INFO'] или $_SERVER['ORIG_PATH_INFO'].
Как получить только имя скрипта (без параметров)?
echo $_SERVER['SCRIPT_NAME'];
echo $_SERVER['PHP_SELF'];
/scripts/index.php
Обе переменные показывают путь к текущему скрипту относительно корня, но PHP_SELF может содержать модификации от пользовательского ввода (уязвимость). Рекомендуется использовать SCRIPT_NAME.
Проблема: если в URL передан путь после имени скрипта (path info), PHP_SELF включает его, а SCRIPT_NAME - нет. Выбор зависит от задачи.
Как получить хост без протокола?
echo $_SERVER['HTTP_HOST'];
echo $_SERVER['SERVER_NAME'];
www.example.com
HTTP_HOST берется из HTTP-заголовка Host, а SERVER_NAME - из настройки сервера. Первый удобнее для динамических сред, второй - для стабильного имени.
Ошибка: при использовании виртуальных хостов SERVER_NAME может не совпадать с реальным доменом, если он задан как *.
Как определить, используется ли HTTPS?
$isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
echo $isHttps ? 'https' : 'http';
https
Проверка через HTTPS и SERVER_PORT не всегда надёжна за прокси. Альтернатива - заголовок HTTP_X_FORWARDED_PROTO.
Типичная ошибка: в некоторых окружениях $_SERVER['HTTPS'] может быть 'off' или пустым. Проверка на 'off' обязательна.
Как получить URL без GET-параметров?
$url = $_SERVER['REQUEST_URI'];
$path = strtok($url, '?');
echo $path;
// или
$parsed = parse_url($url);
echo $parsed['path'];
/products/category/123
Функция strtok отсекает все после первого вопроса, а parse_url возвращает массив с компонентами. Второй способ надёжнее.
Проблема: если в пути встречается символ ?, strtok сработает некорректно. parse_url безопаснее.
Как получить базовый URL сайта (протокол + хост)?
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'];
echo $protocol . '://' . $host;
https://example.com
Этот базовый URL часто используется для построения абсолютных ссылок на ресурсы или редиректов.
Ошибка: если порт нестандартный, нужно добавлять :port. Иначе ссылки будут вести не туда.
Как учесть обратный прокси (Nginx, Cloudflare)?
function getFromForwardedHeaders() {
$protocol = $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? (empty($_SERVER['HTTPS']) ? 'http' : 'https');
$host = $_SERVER['HTTP_X_FORWARDED_HOST'] ?? $_SERVER['HTTP_HOST'];
$port = $_SERVER['HTTP_X_FORWARDED_PORT'] ?? $_SERVER['SERVER_PORT'];
$uri = $_SERVER['REQUEST_URI'];
return $protocol . '://' . $host . ($port ? ':' . $port : '') . $uri;
}
// Вызов
echo getFromForwardedHeaders();
https://example.com/path
Прокси часто подставляют специальные заголовки (X-Forwarded-*). Их использование обязательно, если сайт за балансировщиком.
Проблема безопасности: заголовки X-Forwarded-* могут быть подделаны клиентом. Доверять им нужно только если прокси настроен на удаление внешних заголовков (например, через trusted proxies).
Расширенные примеры получения URL в PHP
Полная функция с поддержкой обратного прокси и настройками безопасности
Эта функция позволяет получать правильный URL в большинстве окружений: от обычного Apache до сложных балансировщиков.
function getCurrentUrlSecure(array $trustedProxies = []) {
// 1. Определяем, можно ли доверять заголовкам прокси
$clientIp = $_SERVER['REMOTE_ADDR'] ?? '';
$useForwarded = in_array($clientIp, $trustedProxies, true) || empty($trustedProxies);
// 2. Протокол
if ($useForwarded && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$protocol = $_SERVER['HTTP_X_FORWARDED_PROTO'];
} else {
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
}
// 3. Хост
if ($useForwarded && isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$host = $_SERVER['HTTP_X_FORWARDED_HOST'];
} else {
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
}
// 4. Порт
if ($useForwarded && isset($_SERVER['HTTP_X_FORWARDED_PORT'])) {
$port = (int)$_SERVER['HTTP_X_FORWARDED_PORT'];
} else {
$port = (int)($_SERVER['SERVER_PORT'] ?? 80);
}
// 5. Путь и параметры
$uri = $_SERVER['REQUEST_URI'] ?? '/';
// 6. Формируем порт (если нестандартный)
$portPart = '';
if (($protocol === 'http' && $port !== 80) || ($protocol === 'https' && $port !== 443)) {
$portPart = ':' . $port;
}
return $protocol . '://' . $host . $portPart . $uri;
}
// Использование
echo getCurrentUrlSecure(['192.168.1.1', '10.0.0.1']);
https://example.com:8080/page?action=edit
В примере добавлена проверка доверенных прокси. Если IP клиента входит в список, используются forwarded-заголовки. Это предотвращает атаки с подделкой заголовков.
Разбор URL на компоненты с помощью parse_url
$url = 'https://user:pass@example.com:8080/path/to/page?name=john#section1';
$parts = parse_url($url);
print_r($parts);
Array
(
[scheme] => https
[host] => example.com
[port] => 8080
[user] => user
[pass] => pass
[path] => /path/to/page
[query] => name=john
[fragment] => section1
)
Обратная операция - http_build_url (из PECL) или сборка вручную. parse_url особенно полезна для преобразования относительных путей в абсолютные.
Получение базового URL (без пути и параметров) для построения ссылок
function getBaseUrl() {
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'];
$port = $_SERVER['SERVER_PORT'];
$portPart = (($protocol === 'http' && $port != 80) || ($protocol === 'https' && $port != 443)) ? ':' . $port : '';
return $protocol . '://' . $host . $portPart;
}
$base = getBaseUrl();
$fullUrl = $base . '/assets/css/style.css';
echo $fullUrl;
http://example.com/assets/css/style.css
Базовый URL необходим для создания абсолютных ссылок на CSS, JS, изображения в многосайтовых установках или при отправке email.
Использование в маршрутизации (роутинг) для извлечения пути без параметров
$uri = $_SERVER['REQUEST_URI'];
$cleanPath = parse_url($uri, PHP_URL_PATH); // только путь
// Пример роутинга
switch (true) {
case $cleanPath === '/' :
$controller = 'HomeController';
break;
case preg_match('#^/blog/(\d+)$#', $cleanPath, $matches):
$controller = 'BlogController';
$id = $matches[1];
break;
default:
http_response_code(404);
$controller = 'ErrorController';
}
echo "Controller: $controller";
Controller: BlogController
При разработке собственного роутера важно отбросить GET-параметры и использовать только путь. parse_url справляется с этой задачей.
Безопасное получение URL через filter_input
$uri = filter_input(INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL);
if ($uri === false || $uri === null) {
$uri = '/';
}
echo $uri;
/about?ref=home
Функция filter_input очищает значение от потенциально опасных символов, хотя для URL достаточно санитизации. Это альтернатива прямому доступу к $_SERVER.
Замечание: filter_input не проверяет существование ключа, нужно явно проверять на false/null.
Получение URL с учётом порта и сокета (CLI)
При запуске из командной строки переменные сервера обычно отсутствуют. Можно задать значения по умолчанию или взять из аргументов.
$protocol = 'http';
$host = 'localhost';
$port = 8000;
$uri = '/index.php';
if (php_sapi_name() === 'cli') {
// Для встроенного сервера PHP порт передаётся через argv
global $argv;
if (isset($argv[1])) {
$uri = $argv[1];
}
}
echo "$protocol://$host:$port$uri";
http://localhost:8000/index.php
Этот пример полезен для тестирования консольных скриптов, которые должны вести себя как веб-приложения.