В чем разница между HTTP_HOST и SERVER_NAME в PHP?

В чем разница между $_SERVER['HTTP_HOST'] и $_SERVER['SERVER_NAME'] в PHP?

Когда вы могли бы подумать об использовании одного вместо другого и почему?


person Emanuil Rusev    schedule 19.02.2010    source источник
comment
Обычно я использую HTTP_HOST, чтобы пользователь оставался на том же имени хоста, с которого он начал. Например, если у меня один и тот же сайт в домене .com и .org, я не хочу отправлять кого-то с .org на .com, особенно если у них могут быть токены входа на .org, которые они потеряют при отправке на. другой домен. - Этот и некоторые другие интересные моменты из stackoverflow.com/questions/1459739/   -  person Yarin    schedule 01.11.2011
comment
@Yarin, не забудьте добавить в белый список - проверить результаты HTTP_HOST. В противном случае злоумышленник может ввести любое значение в Host: запрос HTTP и заставить сервер его принять.   -  person Pacerier    schedule 06.03.2015
comment
Новички: этот вопрос относится к значениям, обычно получаемым с помощью $_SERVER['HTTP_HOST'] или $_SERVER['SERVER_NAME']   -  person Gregory Cosmo Haun    schedule 25.03.2017


Ответы (10)


HTTP_HOST получается из заголовка HTTP-запроса и этого это то, что клиент фактически использовал в качестве «целевого хоста» запроса. SERVER_NAME определяется в конфигурации сервера. Какой из них использовать, зависит от того, для чего он вам нужен. Однако теперь вы должны понимать, что одно значение, управляемое клиентом, поэтому может быть ненадежным для использования в бизнес-логике, а другое - значение, управляемое сервером, которое более надежно. Однако вам необходимо убедиться, что на рассматриваемом веб-сервере правильно настроен SERVER_NAME. Взяв в качестве примера Apache HTTPD, вот выдержка из его документации :

Если ServerName не указан, сервер пытается определить имя хоста, выполняя обратный поиск по IP-адресу. Если в ServerName не указан порт, то сервер будет использовать порт из входящего запроса. Для оптимальной надежности и предсказуемости вы должны указать явное имя хоста и порт с помощью директивы ServerName.


Обновление: после проверки ответа Пекки на ваш вопрос, который содержит ссылку на ответ Bobince, что 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 с использованием _ 9_, чтобы изменить заголовок 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 более надежен, но вы зависите от конфигурации сервера!

person BalusC    schedule 19.02.2010
comment
Хорошо, это решает мою проблему, которая не связана с OP, но актуальна. Я был очень обеспокоен проблемами безопасности при использовании всего, что мог предоставить браузер. Этот ответ был ОГРОМНОЙ помощью. Спасибо, что нашли время собрать его воедино. - person Yitzhak; 05.03.2013
comment
Почему вы говорите, что HTTP_HOST ненадежен? Да, он предоставляется пользователем, но если пользователь укажет какое-то фиктивное значение, конфигурация вашего сервера автоматически вернет 503, и ваш PHP-скрипт даже не запустится! - person Pacerier; 14.07.2013
comment
@Pacerier: на момент написания этого ответа это не так. В ответе указаны версии. Я больше не в курсе PHP, поэтому не могу сказать, действительно ли он изменился в более новой версии. - person BalusC; 14.07.2013
comment
Простой способ обмануть Apache из WinXP - добавить строку в файл hosts, в которой указывается, что IP-адрес сервера назначен другому домену, например: 127.0.0.1 mydomain.com. Я использовал это много раз, чтобы показать местный веб-сайт, обманывающий мою аудиторию, заставляя думать, что у меня есть подключение к Интернету и сайт загружается очень быстро. Вы можете пойти другим путем и заставить Apache думать, что он работает локально, с 173.194.41.5 localhost, поэтому вам никогда не следует полностью доверять SERVER_NAME, если вы не уверены, что ваш Apache правильно настроен. - person vicenteherrera; 08.10.2013
comment
Я просто хочу добавить, что NGINX + PHP-FPM возвращает значение, установленное директивой server_name. Особенно, если server_name не установлен, также _SERVER["SERVER_NAME"] будет пустым. - person white_gecko; 05.06.2014
comment
SERVER_NAME также захватывает значения ServerAlias, установленные в файле VirtualHost? - person Abhishek Madhani; 30.09.2014
comment
Я не испытываю того явления, о котором вы говорите с Apache 2.4.16 и PHP 5.6.20, но я проведу еще несколько тестов. То, что я уже сделал, не совпадает с предыдущими выводами bobince о том, что PHP всегда будет возвращать значение HTTP_HOST для SERVER_NAME. В частности, при настройке SSL я должен был убедиться, что SERVER_NAME установлен правильно, иначе все развалится. Полагаться на предположение об использовании пользовательских агентов HTTP 1.1 под вопросом. - person Anthony Rutledge; 09.03.2017
comment
Если ваша конфигурация похожа на <VirtualHost *.example.com>, клиенты обращаются к вашему сайту как admin.example.com и site.example.com, в PHP + Apache вы получаете доменные имена в SERVER_NAME, но в PHP + Nginx вы получаете *.example.com в SERVER_NAME. Если вы делаете что-либо программно в зависимости от этого, вам нужно это учитывать. - person endo64; 06.02.2020

HTTP_HOST - это целевой хост, отправленный клиентом. Пользователь может свободно манипулировать им. Нет проблем отправить запрос на ваш сайт с указанием HTTP_HOST значения www.stackoverflow.com.

SERVER_NAME происходит из определения VirtualHost сервера и поэтому считается более надежным. Однако им также можно управлять извне при определенных условиях, связанных с настройкой вашего веб-сервера: см. Это Этот вопрос SO, который касается аспектов безопасности обоих вариантов.

Вы не должны полагаться ни на что, чтобы быть в безопасности. Тем не менее, то, что использовать, действительно зависит от того, что вы хотите делать. Если вы хотите определить, в каком домене работает ваш скрипт, вы можете безопасно использовать HTTP_HOST, если недопустимые значения, поступающие от злонамеренного пользователя, ничего не сломают.

person Pekka    schedule 19.02.2010
comment
Да, но запрос, запрашивающий значение HTTP_HOST для www.stackoverflow.com, будет отклонен большинством HTTP-серверов заранее, поэтому сценарий PHP даже не увидит запрос! - person Pacerier; 14.07.2013
comment
@Pacerier true, но не всегда, если сервер не настроен должным образом. - person Pekka; 14.07.2013
comment
Как упоминалось в сообщении BalusC, когда вы получаете доступ к виртуальному хосту Apache по IP, обе из этих переменных содержат IP (по умолчанию), а не фактическое имя сервера. Вы должны использовать UseCanonicalName on в httpd.conf, чтобы SERVER_NAME было фактическим именем сервера. - person Simon East; 20.08.2013
comment
@Pekka 웃, если сервер не настроен должным образом, $_SERVER['SERVER_NAME'] не будет работать. Плохо сконфигурированный сервер установит$_SERVER['SERVER_NAME'] в зависимости от значения Host: запроса клиента. Оба равны. - person Pacerier; 06.03.2015
comment
Хороший ответ, но я бы не стал предполагать виртуальный хостинг. - person Anthony Rutledge; 09.03.2017

Как я уже упоминал в этом ответе, если сервер работает на порту, отличном от 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'
person Simon East    schedule 20.08.2013
comment
Когда они исправят это коварное поведение? - person Pacerier; 06.03.2015

Обратите внимание, что если вы хотите использовать 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}/

Это применимо ТОЛЬКО в том случае, если вы обращаетесь к серверу без имени хоста.

person Daniel Marschall    schedule 27.02.2012
comment
SiteGround в своем внутреннем коде перенаправления http на https используйте https://%{SERVER_NAME}%{REQUEST_URI} - person IXN; 02.04.2018

Если вы хотите проверить через server.php или что-то еще, вы хотите вызвать его следующим образом:

<?php
    phpinfo(INFO_VARIABLES);
?>

or

<?php
    header("Content-type: text/plain");

    print_r($_SERVER);
?>

Затем войдите в него со всеми действующими URL-адресами вашего сайта и проверьте разницу.

person stevewh    schedule 24.05.2010

Зависит от того, что я хочу узнать. SERVER_NAME - это имя хоста сервера, а HTTP_HOST - это виртуальный хост, к которому подключился клиент.

person Rowland Shaw    schedule 19.02.2010
comment
Не совсем верно, Роуленд, SERVER_NAME - обычно имя VirtualHost, а не сам сервер. А в Apache SERVER_NAME часто заполняется тем же значением, что и HTTP_HOST (см. Ответ BalusC). - person Simon East; 20.08.2013
comment
@Simon, Поскольку большинство хостов теперь являются VirtualHost, что бы вы имели в виду под названием самого сервера? - person Pacerier; 06.03.2015
comment
Если вы используете виртуальный частный сервер (VPS) с одним веб-сайтом, вам не нужно предполагать, что SERVER_NAME применяется к виртуальному хосту. Однако можно по-прежнему использовать настройку виртуального хоста для одного сайта. Многие люди пользуются виртуальным хостингом, поэтому я понимаю вашу точку зрения. - person Anthony Rutledge; 09.03.2017

Мне потребовалось время, чтобы понять, что люди имели в виду под словом «SERVER_NAME более надежен». Я использую общий сервер и не имею доступа к директивам виртуального хоста. Итак, я использую mod_rewrite в .htaccess, чтобы отображать разные HTTP_HOST в разные каталоги. В этом случае значение имеет HTTP_HOST.

Ситуация аналогична, если вы используете виртуальные хосты на основе имен: директива ServerName внутри виртуального хоста просто говорит, какое имя хоста будет сопоставлено с этим виртуальным хостом. Суть в том, что в обоих случаях имя хоста, предоставленное клиентом во время запроса (HTTP_HOST), должно совпадать с именем на сервере, которое само отображается в каталог. Независимо от того, выполняется ли сопоставление с помощью директив виртуального хоста или правил htaccess mod_rewrite, здесь вторично. В этих случаях HTTP_HOST будет таким же, как SERVER_NAME. Я рад, что Apache настроен именно так.

Однако с виртуальными хостами на основе IP ситуация иная. В этом случае и только в этом случае SERVER_NAME и HTTP_HOST могут быть разными, потому что теперь клиент выбирает сервер по IP, а не по имени. Действительно, могут быть особые конфигурации, где это важно.

Итак, с этого момента я буду использовать SERVER_NAME, на всякий случай, если мой код будет перенесен в эти специальные конфигурации.

person Dominic108    schedule 16.02.2012

Предполагая, что у вас простая установка (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'] (в конечном итоге полученное из запрошенного HTTP-заголовка host) $_SERVER['SERVER_NAME'].

Теперь я использую суперглобальный язык, чтобы объяснить свой пример, но это просто потому, что некоторые люди не знакомы с INPUT_GET, INPUT_POST и INPUT_SERVER в отношении _ 18_.

Суть в том, что я не обрабатываю запросы POST на своем сервере, если не выполняются все четыре условия. Следовательно, с точки зрения запросов POST, отказ предоставить заголовок HTTP host (наличие проверено ранее) означает гибель для браузеров со строгим HTTP 1.0. Кроме того, запрошенный хост должен соответствовать значению для ServerName в httpd.conf и, в зависимости от расширения, значению для $_SERVER('SERVER_NAME') в $_SERVER суперглобальный. Опять же, я бы использовал INPUT_SERVER с функциями фильтра PHP, но вы уловили мой уклон.

Имейте в виду, что Apache часто использует ServerName в стандартных перенаправлениях (например, оставляя косую черту в конце URL-адреса: например, http://www.example.com становится http://www.example.com/), даже если вы не используете перезапись URL.

Я использую $_SERVER['SERVER_NAME'] как стандарт, а не $_SERVER['HTTP_HOST']. По этому вопросу много споров. $_SERVER['HTTP_HOST'] может быть пустым, поэтому это не должно быть основанием для создания соглашений о коде, таких как мой публичный метод выше. Но то, что оба могут быть установлены, не гарантирует, что они будут равны. Тестирование - лучший способ узнать наверняка (имея в виду версию Apache и версию PHP).

person Anthony Rutledge    schedule 09.03.2017

Как сказал balusC, SERVER_NAME ненадежен и может быть изменен в конфигурации apache, конфигурации имени сервера и брандмауэра, который может находиться между вами и сервером.

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

function getRealHost(){
   list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
   return $realHost;
}
person MSS    schedule 20.05.2016

$ _SERVER ['SERVER_NAME'] зависит от конфигурации вашего веб-сервера. $ _SERVER ['HTTP_HOST'] основан на запросе от клиента.

person Vitalie    schedule 14.03.2020