В чем разница между HTTP_HOST и SERVER_NAME в PHP?
-
21-09-2019 - |
Вопрос
Когда бы вы подумали об использовании одного поверх другого и почему?
Решение
В HTTP_HOST
получается из Заголовок HTTP - запроса и это то, что клиент фактически использовал в качестве "целевого хоста" запроса.В SERVER_NAME
определяется в конфигурации сервера.Какой из них использовать, зависит от того, для чего он вам нужен.Однако теперь вы должны понимать, что первое - это контролируемое клиентом значение, которое, таким образом, может быть ненадежным для использования в бизнес-логике, а другое - контролируемое сервером значение, которое является более надежным.Однако вам необходимо убедиться, что рассматриваемый веб-сервер имеет SERVER_NAME
правильно настроенный.Взяв Apache HTTPD в качестве примера, вот выдержка из его документация:
Если нет
ServerName
указывается, затем сервер пытается определить имя хоста, выполняя обратный поиск по IP-адресу.Если порт не указан вServerName
, тогда сервер будет использовать порт из входящего запроса.Для оптимальной надежности и предсказуемости вам следует указать явное имя хоста и порт, используяServerName
директива.
Обновить:после проверки ответ Пекки на ваш вопрос который содержит ссылку на ответ бобинса что PHP всегда будет возвращать HTTP_HOST
значение для SERVER_NAME
, что противоречит моему собственному опыту работы с PHP 4.x + Apache HTTPD 1.2.x пару лет назад я сдул немного пыли с моей текущей среды XAMPP в Windows XP (Apache HTTPD 2.2.1 с PHP 5.2.8), запустил ее, создал страницу PHP, которая печатает оба значения, создал тестовое приложение Java с использованием URLConnection
чтобы изменить Host
заголовок и тесты научили меня, что это действительно (неправильно) так.
После первого подозрения в PHP и копания в некоторых Отчеты об ошибках PHP что касается темы, я узнал, что корень проблемы кроется в используемом веб-сервере, что он неправильно вернул HTTP Host
заголовок , когда SERVER_NAME
был запрошен.Поэтому я углубился в Отчеты об ошибках Apache HTTPD используя различные ключевые слова что касается предмета, и я, наконец, нашел связанная ошибка.Такое поведение было введено примерно с Apache HTTPD 1.3.Вам нужно установить UseCanonicalName
директива для on
в <VirtualHost>
вступление в ServerName
в httpd.conf
(также проверьте предупреждение в нижней части документ!).
<VirtualHost *>
ServerName example.com
UseCanonicalName on
</VirtualHost>
У меня это сработало.
Обобщенный, SERVER_NAME
это более надежно, но вы зависимый в конфигурации сервера!
Другие советы
HTTP_HOST
является целевым хостом, отправленным клиентом.Пользователь может свободно манипулировать им.Нет проблем отправить запрос на ваш сайт с просьбой HTTP_HOST
значение www.stackoverflow.com
.
SERVER_NAME
поступает с сервера VirtualHost
определение и поэтому считается более надежным.Однако им также можно управлять извне при определенных условиях, связанных с настройкой вашего веб-сервера:Видишь это Это ТАКОЙ вопрос это касается аспектов безопасности обеих вариаций.
Вы не должны полагаться ни на то, ни на другое, чтобы быть в безопасности.Тем не менее, то, что использовать, действительно зависит от того, что вы хотите сделать.Если вы хотите определить, в каком домене запущен ваш скрипт, вы можете безопасно использовать HTTP_HOST
пока недопустимые значения, исходящие от злоумышленника, ничего не могут нарушить.
Как я уже упоминал в этот ответ, если сервер работает на порту, отличном от 80 (что может быть обычным на компьютере для разработки / интрасети), то HTTP_HOST
содержит порт, в то время как SERVER_NAME
не делает этого.
$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'
(По крайней мере, это то, что я заметил в виртуальных хостингах на основе портов Apache)
Обратите внимание , что HTTP_HOST
делает не содержать :443
при запуске по протоколу HTTPS (если только вы не используете нестандартный порт, который я не тестировал).
Как отмечали другие, эти два параметра также отличаются при использовании IPv6:
$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
Пожалуйста, обратите внимание, что если вы хотите использовать IPv6, вы, вероятно, захотите использовать HTTP_HOST
вместо того , чтобы SERVER_NAME
.Если вы войдете http://[::1]/
переменными среды будут следующие:
HTTP_HOST = [::1]
SERVER_NAME = ::1
Это означает, что если вы выполните mod_rewrite, например, вы можете получить неприятный результат.Пример для перенаправления SSL:
# SERVER_NAME will NOT work - Redirection to https://::1/
RewriteRule .* https://%{SERVER_NAME}/
# HTTP_HOST will work - Redirection to https://[::1]/
RewriteRule .* https://%{HTTP_HOST}/
Это применимо ТОЛЬКО в том случае, если вы обращаетесь к серверу без имени хоста.
если вы хотите проверить через server.php или как бы вы ни хотели это назвать, используя следующее:
<?php
phpinfo(INFO_VARIABLES);
?>
или
<?php
header("Content-type: text/plain");
print_r($_SERVER);
?>
Затем откройте его со всеми допустимыми URL-адресами для вашего сайта и проверьте разницу.
Зависит от того, что я хочу выяснить.SERVER_NAME - это имя хоста сервера, в то время как HTTP_HOST - это виртуальный хост, к которому подключен клиент.
Мне потребовалось некоторое время , чтобы понять , что люди имели в виду под 'SERVER_NAME
это более надежно".Я использую общий сервер и не имею доступа к директивам виртуального хоста.Итак, я использую mod_rewrite в .htaccess
для отображения различных HTTP_HOST
s в разные каталоги.В таком случае, это HTTP_HOST
это имеет смысл.
Аналогичная ситуация возникает, если использовать виртуальные хосты на основе имен:в ServerName
директива внутри виртуального хоста просто указывает, какое имя хоста будет сопоставлено этому виртуальному хосту.Суть в том, что в обоих случаях имя хоста, предоставленное клиентом во время запроса (HTTP_HOST
), должно быть сопоставлено с именем на сервере, которое само сопоставляется каталогу.Выполняется ли сопоставление с помощью директив виртуального хоста или с помощью правил htaccess mod_rewrite, здесь второстепенно.В этих случаях, HTTP_HOST
будет таким же , как SERVER_NAME
.Я рад, что Apache настроен таким образом.
Однако с виртуальными хостами на основе IP ситуация иная.В этом случае и только в этом случае, SERVER_NAME
и HTTP_HOST
может быть по-другому, потому что теперь клиент выбирает сервер по IP, а не по имени. Действительно, могут существовать особые конфигурации, где это важно.
Итак, начиная с этого момента, я буду использовать SERVER_NAME
, на всякий случай, если мой код перенесен в эти специальные конфигурации.
Предполагая, что у вас простая настройка (CentOS 7, Apache 2.4.x и PHP 5.6.20) и только один веб-сайт (не предполагающий виртуального хостинга)...
В смысле PHP, $_SERVER['SERVER_NAME']
является элементом, который PHP регистрирует в $_SERVER
суперглобальный на основе вашей конфигурации Apache (**ServerName**
директива с UseCanonicalName On
) в httpd.conf (будь то из включенного файла конфигурации виртуального хоста, что угодно и т.д. ). HTTP_HOST является производным от HTTP host
заголовок.Рассматривайте это как пользовательский ввод.Отфильтруйте и проверьте перед использованием.
Вот пример того, где я использую $_SERVER['SERVER_NAME']
в качестве основы для сравнения.Следующий метод взят из конкретного дочернего класса, который я создал с именем ServerValidator
(дитя Validator
). ServerValidator
проверяет шесть или семь элементов в $_SERVER перед их использованием.
При определении того, является ли HTTP-запрос POST, я использую этот метод.
public function isPOST()
{
return (($this->requestMethod === 'POST') && // Ignore
$this->hasTokenTimeLeft() && // Ignore
$this->hasSameGETandPOSTIdentities() && // Ingore
($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')));
}
К моменту вызова этого метода вся фильтрация и проверка соответствующих элементов $_SERVER были бы выполнены (и соответствующие свойства установлены).
Линия ...
($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')
...проверяет, что $_SERVER['HTTP_HOST']
значение (в конечном счете, полученное из запрошенного host
HTTP заголовок) соответствует $_SERVER['SERVER_NAME']
.
Теперь я использую superglobal speak для объяснения моего примера, но это только потому, что некоторые люди не знакомы с INPUT_GET
, INPUT_POST
, и INPUT_SERVER
в отношении filter_input_array()
.
Суть в том, что я не обрабатываю POST-запросы на своем сервере, если только ВСЕ соблюдены четыре условия.Следовательно, с точки зрения POST-запросов, неспособность предоставить HTTP host
заклинания заголовка (присутствие проверено для предыдущих) гибель для строгих HTTP 1.0 браузеры.Более того, запрошенный хост должно соответствовать значению для ServerName
в httpd.conf, и, путем расширения, значение для $_SERVER('SERVER_NAME')
в $_SERVER
суперглобальный.Опять же, я бы использовал INPUT_SERVER
с функциями фильтрации PHP, но вы уловили мой намек.
Имейте в виду, что Apache часто использует ServerName
в стандартные перенаправления (например, оставить косую черту в конце URL-адреса:Пример, http://www.foo.com становление http://www.foo.com/), даже если вы не используете перезапись URL-адреса.
Я использую $_SERVER['SERVER_NAME']
как стандарт, не $_SERVER['HTTP_HOST']
.По этому вопросу много говорят взад и вперед. $_SERVER['HTTP_HOST']
может быть пустым, поэтому это не должно быть основой для создания соглашений о коде, таких как мой открытый метод выше.Но только потому, что оба параметра могут быть установлены, не гарантирует, что они будут равны.Тестирование - лучший способ узнать наверняка (принимая во внимание версию Apache и версию PHP).
Как сказал балусК, ИМЯ_СЕРВЕРА ненадежно и может быть изменено в конфигурации apache, конфигурации имени сервера сервера и брандмауэра, который может быть между вами и сервером.
Следующая функция всегда возвращает реальный хост (введенный пользователем хост) без порта, и это почти надежно:
function getRealHost(){
list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
return $realHost;
}