Яка різниця між HTTP_HOST та SERVER_NAME у PHP?


533

Яка різниця між PHP HTTP_HOSTі SERVER_NAMEв ньому?

де:

  • HTTP_POST === $_SERVER['HTTP_HOST']
  • SERVER_NAME === $_SERVER['SERVER_NAME']

Коли ви вирішили використовувати один над іншим і чому?


14
"Я зазвичай переходжу за HTTP_HOST, щоб користувач залишався на точному імені хоста, на якому вони почали працювати. Наприклад, якщо у мене той самий сайт у доменi .com та .org, я не хочу надсилати когось із .org до .com, особливо якщо вони можуть мати маркери для входу на .org, які вони втрачають, якщо надсилаються на інший домен. " - Цей та деякі інші цікаві моменти з stackoverflow.com/questions/1459739/…
Ярін

5
@ Yarin, не забудьте перевірити результати спискуHTTP_HOST . В іншому випадку зловмисник може ввести будь-яке значення у Host:запиті HTTP і змусити сервер прийняти його.
Pacerier

6
Початківці: Це питання стосується значень, зазвичай отриманих через $_SERVER['HTTP_HOST']або$_SERVER['SERVER_NAME']
Gregory Cosmo Haun

Відповіді:


780

HTTP_HOSTВиходить з заголовка запиту HTTP , і це те , що клієнт на насправді використовується в якості «цільового хоста» запиту. Значення SERVER_NAMEвизначено в конфігурації сервера. Яку використовувати, залежить від того, для чого це потрібно. Тепер ви повинні усвідомити, що одне є контрольованим клієнтом значенням, яке, таким чином, не може бути надійним для використання в бізнес-логіці, а інше - кероване сервером значення, яке є більш надійним. Однак вам потрібно переконатися, що відповідний веб-сервер SERVER_NAMEправильно налаштований. Взявши приклад Apache HTTPD, ось витяг з його документації :

Якщо ні ServerNameвказано, сервер намагається вивести ім'я хоста, виконавши зворотний пошук по IP-адресі. Якщо в розділі не вказано жодного порту ServerName, сервер буде використовувати порт від вхідного запиту. Для оптимальної надійності та передбачуваності слід вказати чітке ім'я хоста та порт, використовуючи ServerNameдирективу.


Оновлення : після перевірки відповіді Pekka на ваше запитання, що містить посилання на відповідь 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, використовуючи URLConnectionдля зміни Hostзаголовка та тести навчили мене, що це дійсно (неправильно) так.

Після першого підозри на PHP та копання у деяких звітах про помилки PHP щодо теми, я дізнався, що корінь проблеми полягає у використанні веб-сервера, що він неправильно повертав Hostзаголовка HTTP, коли SERVER_NAMEбуло подано запит. Тому я заглибився у звіти про помилки Apache HTTPD, використовуючи різні ключові слова стосовно теми, і нарешті знайшов пов’язану помилку . Така поведінка була впроваджена з моменту створення Apache HTTPD 1.3. Вам потрібно встановити UseCanonicalNameдирективи onв <VirtualHost>запису про ServerNameдюймі httpd.conf(також перевірити попередження в нижній частині документа !).

<VirtualHost *>
    ServerName example.com
    UseCanonicalName on
</VirtualHost> 

Це працювало для мене.

Узагальнено, SERVER_NAMEє більш надійним, але ви залежите від конфігурації сервера!


5
Гаразд, це вирішує мою проблему, яка не стосується ОП, але є актуальною. Мене дуже хвилювали проблеми безпеки, використовуючи все, що може надавати браузер. Ця відповідь була величезною допомогою. Дякую, що знайшли час, щоб зібрати його.
Іцхак

2
Чому, на вашу думку, HTTP_HOST не є надійним? Так, він постачається користувачем, але якщо користувач дасть деяке неправдиве значення, конфігурація вашого сервера автоматично поверне 503, і ​​ваш PHP-скрипт навіть не запуститься!
Pacerier

1
@Pacerier: на момент написання цієї відповіді цього не зробили. Версії згадуються у відповіді. Я більше не відстаю від PHP, тому не можу сказати, чи дійсно він змінився в новій версії.
BalusC

2
Найпростіший спосіб підманути Apache з WinXP - це додати рядок до файлу 'hosts', вказуючи, що IP-сервер призначений іншому домену, як-от так: "127.0.0.1 mydomain.com". Я багато разів використовував це, щоб показати місцевому веб-сайту, що обманює мою аудиторію, щоб подумати, що у мене підключення до Інтернету та сайт завантажується дуже швидко. Ви можете піти в інший бік і обдурити Apache, щоб він вважав, що він працює локально, з "173.194.41.5 localhost", тому ніколи не слід довіряти SERVER_NAME повністю, якщо ви впевнені, що Apache добре налаштований.
vicenteherrera

1
Я просто хочу додати, що NGINX + PHP-FPM повертає значення, встановлене server_nameдирективою. Особливо, якщо значення не server_nameвстановлено, також _SERVER["SERVER_NAME"]буде порожнім.
white_gecko

69

HTTP_HOSTце цільовий хост, надісланий клієнтом Користувачем ним можна вільно керувати. Це не проблема, щоб надіслати запит на ваш сайт із запитом про HTTP_HOSTзначення www.stackoverflow.com.

SERVER_NAME надходить від сервера VirtualHost і тому вважається більш надійним. Однак, за певних умов, пов’язаних із налаштуванням вашого веб-сервера, ним можна також керувати ззовні: Дивіться це питання ТАК, що стосується аспектів безпеки обох варіантів.

Ви не повинні покладатися на те, щоб бути безпечним. Однак сказане, що використовувати насправді, залежить від того, що ви хочете зробити. Якщо ви хочете визначити, на якому домені працює ваш скрипт, ви можете безпечно користуватися до HTTP_HOSTтих пір, поки недійсні значення, отримані від шкідливого користувача, нічого не можуть зламати.


8
Так, але запит про запит значення HTTP_HOST www.stackoverflow.com більшість серверів HTTP буде відхилено наперед, тому сценарій PHP навіть не побачить запит!
Pacerier

2
@Pacerier вірно, але не завжди, якщо сервер не налаштований належним чином.
Pekka

1
Як згадується у публікації BalusC, коли ви отримуєте доступ до віртуального хоста Apache по IP, обидві ці змінні містять IP (за замовчуванням), а не власне ім’я сервера. Ви повинні використовувати UseCanonicalName onв httpd.conf, щоб примусити себе SERVER_NAMEбути фактичним іменем сервера.
Саймон Схід

@Pekka 웃, Якщо сервер не налаштований належним чином, $_SERVER['SERVER_NAME']він також не працює . Неправильно налаштований сервер буде встановлений $_SERVER['SERVER_NAME']на основі значення Host:запиту клієнта . Обидва рівні.
Печер'є

Хороша відповідь, але я б не припускав віртуального хостингу.
Ентоні Рутлідж

55

Як я вже згадував у цій відповіді , якщо сервер працює на портах, відмінних від 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'

2
Коли вони виправлять цю підступну поведінку?
Печер'є

27

Зауважте, що якщо ви хочете використовувати 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}/

Це застосовується ТОЛЬКО, якщо ви отримуєте доступ до сервера без імені хоста.


1
SiteGround, в їхньому будинку http для https коду переадресації, використовуйтеhttps://%{SERVER_NAME}%{REQUEST_URI}
IXN

6

Якщо ви хочете перевірити через server.php або інше, ви хочете зателефонувати йому за допомогою наступного:

<?php
    phpinfo(INFO_VARIABLES);
?>

або

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

    print_r($_SERVER);
?>

Потім перейдіть до нього з усіма дійсними URL-адресами свого веб-сайту та перевірте різницю.


5

Залежить те, що я хочу з’ясувати. SERVER_NAME - ім'я хоста сервера, а HTTP_HOST - це віртуальний хост, до якого підключений клієнт.


4
Не зовсім правда Rowland, SERVER_NAMEзазвичай це ім'я VirtualHost, а не сам сервер. А в Apache SERVER_NAMEчасто заповнюється з тим же значенням, що й HTTP_HOST(див. Відповідь BalusC).
Саймон Схід

1
@Simon, Оскільки більшість хостів зараз VirtualHost, що б ви мали на увазі під назвою "сам сервер"?
Печер'є

Якщо ви працюєте з віртуальним приватним сервером (VPS) на одному веб-сайті, не потрібно вважати, що це SERVER_NAMEстосується віртуального хоста. Однак все одно можна використовувати налаштування віртуального хоста для одного сайту. Багато людей використовують спільний хостинг, тому я бачу вашу думку.
Ентоні Рутлідж

2

Мені знадобилося певний час, щоб зрозуміти, що люди розуміють під " SERVER_NAMEнадійнішими". Я використовую спільний сервер і не маю доступу до директив віртуального хоста. Отже, я використовую mod_rewrite в .htaccess для відображення різних HTTP_HOSTs у різних каталогах. У такому випадку саме це HTTP_HOSTмає сенс.

Ситуація схожа, якщо використовується віртуальний хост на основі імен: ServerNameдиректива в рамках віртуального хоста просто говорить, яке ім'я хоста буде відображено в цьому віртуальному хості. Суть полягає в тому, що в обох випадках ім'я хоста, що надається клієнтом під час запиту ( HTTP_HOST), має відповідати імені в межах сервера, яке саме відображається у каталозі. Незалежно від того, чи відображення виконано з віртуальними директивами хоста або з правилами htaccess mod_rewrite, тут є другорядним. У цих випадках HTTP_HOSTбуде те саме, що SERVER_NAME. Я радий, що Apache налаштований саме так.

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

Отже, починаючи з цього часу, я буду використовувати SERVER_NAME, на всякий випадок, якщо мій код переноситься в ці спеціальні конфігурації.


2

Якщо припустимо, що у вас є проста установка (CentOS 7, Apache 2.4.x і PHP 5.6.20) і лише один веб-сайт (не передбачаючи віртуального хостингу) ...

У сенсі PHP $_SERVER['SERVER_NAME']- це елемент, який PHP реєструє у $_SERVERнадглобальному на основі вашої конфігурації Apache ( **ServerName**директива з UseCanonicalName On) в httpd.conf (будь то з включеного файлу конфігурації віртуального хоста, будь-якого, тощо ...). HTTP_HOST походить від hostзаголовка HTTP . Трактуйте це як введення користувача. Фільтруйте та перевіряйте перед використанням.

Ось приклад того, де я використовую $_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'].

Тепер я використовую суперглобального Speak пояснити мій приклад, але це тільки тому , що деякі люди не знайомі з INPUT_GET, INPUT_POSTі INPUT_SERVERв ставленні до filter_input_array().

Суть полягає в тому, що я не обробляю POST-запити на своєму сервері, якщо всі 4 умови не виконані. Отже, з точки зору 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).


0

Як сказав balusC, SERVER_NAME не є надійним, і його можна змінити в налаштуваннях apache, конфігурації імені сервера та брандмауера, які можуть бути між вами та сервером.

Наступна функція завжди повертає справжній хост (набраний користувачем хост) без порту, і це майже надійно:

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

0

$ _SERVER ['SERVER_NAME'] заснований на конфігурації веб-серверів. $ _SERVER ['HTTP_HOST'] базується на запиті клієнта.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.