Sự khác biệt giữa HTTP_HOST và SERVER_NAME trong PHP là gì?


533

Sự khác biệt giữa HTTP_HOSTSERVER_NAMEtrong PHP là gì?

Ở đâu:

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

Khi nào bạn sẽ xem xét sử dụng cái này hơn cái kia và tại sao?


14
"Tôi thường sử dụng HTTP_HOST, để người dùng giữ nguyên tên máy chủ chính xác mà họ đã bắt đầu. Ví dụ: nếu tôi có cùng một trang web trên tên miền .com và .org, tôi không muốn gửi ai đó từ .org đến .com, đặc biệt nếu họ có thể có mã thông báo đăng nhập trên .org mà họ sẽ mất nếu được gửi đến tên miền khác. " - Điều này và một số điểm thú vị khác từ stackoverflow.com/questions/1459739/
Yarin

5
@Yarin, Đừng quên danh sách trắng-xác minh kết quả củaHTTP_HOST . Mặt khác, kẻ tấn công có thể đặt bất kỳ giá trị nào vào Host:yêu cầu của HTTP và khiến máy chủ chấp nhận nó.
Pacerier

6
Người mới bắt đầu: Câu hỏi này đề cập đến các giá trị thường thu được thông qua $_SERVER['HTTP_HOST']hoặc$_SERVER['SERVER_NAME']
Gregory Cosmo Haun

Câu trả lời:


780

Cái HTTP_HOSTđược lấy từ tiêu đề yêu cầu HTTP và đây là thứ mà máy khách thực sự sử dụng làm "máy chủ đích" của yêu cầu. Các SERVER_NAMEđịnh nghĩa trong cấu hình máy chủ. Cái nào để sử dụng phụ thuộc vào những gì bạn cần nó cho. Tuy nhiên, bây giờ bạn nên nhận ra rằng giá trị này là giá trị do khách hàng kiểm soát, do đó có thể không đáng tin cậy để sử dụng trong logic nghiệp vụ và giá trị kia là giá trị do máy chủ kiểm soát, đáng tin cậy hơn. Tuy nhiên, bạn cần đảm bảo rằng máy chủ web đang đề cập có SERVER_NAMEcấu hình chính xác. Lấy Apache HTTPD làm ví dụ, đây là một trích xuất từ tài liệu của nó :

Nếu không ServerNameđược chỉ định, thì máy chủ sẽ cố gắng suy ra tên máy chủ bằng cách thực hiện tra cứu ngược lại trên địa chỉ IP. Nếu không có cổng nào được chỉ định trong ServerNamethì máy chủ sẽ sử dụng cổng từ yêu cầu đến. Để có độ tin cậy và dự đoán tối ưu, bạn nên chỉ định tên máy chủ và cổng rõ ràng bằng cách sử dụng lệnh ServerName.


Cập nhật : sau khi kiểm tra câu trả lời của Pekka về câu hỏi của bạn có chứa liên kết đến câu trả lời của bobince rằng PHP sẽ luôn trả về HTTP_HOSTgiá trị của SERVER_NAMEnó, đi ngược lại với trải nghiệm PHP 4.x + Apache HTTPD 1.2.x của tôi từ vài năm trước , Tôi đã thổi bụi từ môi trường XAMPP hiện tại của mình trên Windows XP (Apache HTTPD 2.2.1 với PHP 5.2.8), đã khởi động nó, tạo một trang PHP in cả hai giá trị, tạo một ứng dụng thử nghiệm Java bằng cách sử dụng URLConnectionđể sửa đổi Hosttiêu đề và các xét nghiệm đã dạy tôi rằng đây thực sự là (không chính xác) trường hợp.

Sau lần đầu tiên nghi ngờ PHP và đào một số báo cáo lỗi PHP liên quan đến chủ đề này, tôi đã biết rằng gốc rễ của vấn đề là ở máy chủ web được sử dụng, rằng nó trả lại Hosttiêu đề HTTP không chính xác khi SERVER_NAMEđược yêu cầu. Vì vậy, tôi đã tìm hiểu các báo cáo lỗi HTTPD bằng cách sử dụng các từ khóa khác nhau liên quan đến chủ đề và cuối cùng tôi đã tìm thấy một lỗi liên quan . Hành vi này đã được giới thiệu từ khoảng Apache HTTPD 1.3. Bạn cần phải thiết lập UseCanonicalNamechỉ để ontrong <VirtualHost>entry của ServerNametrong httpd.conf(cũng kiểm tra các cảnh báo ở dưới cùng của tài liệu !).

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

Điều này làm việc cho tôi.

Tóm tắt, SERVER_NAMEđáng tin cậy hơn, nhưng bạn phụ thuộc vào cấu hình máy chủ!


5
Được rồi, điều này giải quyết vấn đề của tôi, không liên quan đến OP nhưng có liên quan. Tôi đã rất lo lắng về các vấn đề bảo mật bằng cách sử dụng bất cứ thứ gì mà trình duyệt có thể cung cấp. Câu trả lời này là một sự giúp đỡ LỚN. Cảm ơn bạn đã dành thời gian để đặt nó lại với nhau.
Yitzhak

2
Tại sao bạn nói HTTP_HOST không đáng tin cậy? Có, nó được cung cấp bởi người dùng, nhưng nếu người dùng đưa ra một số giá trị không có thật, cấu hình máy chủ của bạn sẽ tự động trả về 503 và tập lệnh PHP của bạn thậm chí sẽ không được chạy!
Pacerier

1
@Pacerier: tại thời điểm viết câu trả lời này, nó đã không. Các phiên bản được đề cập trong câu trả lời. Tôi không theo kịp PHP nữa, vì vậy tôi không thể nói nếu nó thực sự đã thay đổi trong phiên bản mới hơn.
BalusC

2
Một cách dễ dàng để lừa Apache từ WinXP là thêm một dòng vào tệp 'hosts' nói rằng IP của máy chủ được gán cho một tên miền khác, như sau: "127.0.0.1 mydomain.com". Tôi đã sử dụng điều này rất nhiều lần để hiển thị một trang web địa phương lừa khán giả của tôi nghĩ rằng tôi đã có kết nối internet và trang web được tải rất nhanh. Bạn có thể đi theo một cách khác và lừa Apache để nghĩ rằng nó đang chạy cục bộ, với "173.194.41.5 localhost", vì vậy bạn không bao giờ nên tin tưởng hoàn toàn vào SERVER_NAME trừ khi bạn chắc chắn rằng Apache của bạn được cấu hình tốt.
Abbeyenteherrera

1
Tôi chỉ muốn thêm, NGINX + PHP-FPM trả về giá trị được đặt bởi lệnh server_name. Đặc biệt nếu không có server_namethiết lập cũng _SERVER["SERVER_NAME"]sẽ trống rỗng.
trắng_gecko

69

HTTP_HOSTlà máy chủ đích được gửi bởi khách hàng. Nó có thể được thao tác tự do bởi người dùng. Không có vấn đề gì để gửi yêu cầu đến trang web của bạn yêu cầu HTTP_HOSTgiá trị www.stackoverflow.com.

SERVER_NAMExuất phát từ VirtualHostđịnh nghĩa của máy chủ và do đó được coi là đáng tin cậy hơn. Tuy nhiên, nó cũng có thể bị thao túng từ bên ngoài trong một số điều kiện nhất định liên quan đến cách thiết lập máy chủ web của bạn: Xem câu hỏi SO này liên quan đến các khía cạnh bảo mật của cả hai biến thể.

Bạn không nên dựa vào một trong hai để được an toàn. Điều đó nói rằng, những gì để sử dụng thực sự phụ thuộc vào những gì bạn muốn làm. Nếu bạn muốn xác định miền nào tập lệnh của bạn đang chạy, bạn có thể sử dụng một cách an toàn HTTP_HOSTmiễn là các giá trị không hợp lệ đến từ người dùng độc hại không thể phá vỡ bất cứ điều gì.


8
Có nhưng một yêu cầu yêu cầu giá trị HTTP_HOST của www.stackoverflow.com sẽ bị từ chối bởi hầu hết các máy chủ HTTP ở phía trước để tập lệnh PHP thậm chí không nhìn thấy yêu cầu!
Pacerier

2
@Pacerier đúng, nhưng không phải lúc nào cũng được nếu máy chủ không được cấu hình đúng.
Pekka

1
Như đã đề cập trong bài đăng của BalusC, khi bạn truy cập một máy chủ ảo Apache bằng IP, cả hai biến này đều chứa IP (theo mặc định), không phải tên máy chủ thực tế. Bạn phải sử dụng UseCanonicalName ontrong httpd.conf để buộc SERVER_NAMEphải là tên máy chủ thực tế.
Simon East

@Pekka, Nếu máy chủ không được cấu hình đúng, $_SERVER['SERVER_NAME']nó cũng sẽ không hoạt động . Một máy chủ được cấu hình kém sẽ được thiết lập $_SERVER['SERVER_NAME']dựa trên giá trị Host:yêu cầu của khách hàng . Cả hai đều bằng nhau.
Pacerier

Câu trả lời tốt, nhưng tôi sẽ không giả sử lưu trữ ảo.
Anthony Rutledge

55

Như tôi đã đề cập trong câu trả lời này , nếu máy chủ chạy trên một cổng khác 80 (như có thể phổ biến trên máy phát triển / mạng nội bộ) thì HTTP_HOSTcó chứa cổng, trong khi SERVER_NAMEkhông.

$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'

(Ít nhất đó là những gì tôi đã nhận thấy trong các máy chủ ảo dựa trên cổng Apache)

Lưu ý rằng HTTP_HOSTkhông không chứa :443khi chạy trên HTTPS (trừ khi bạn đang chạy trên một cổng không chuẩn, mà tôi đã không kiểm tra).

Như những người khác đã lưu ý, cả hai cũng khác nhau khi sử dụng IPv6:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'

2
Khi nào họ sẽ sửa chữa hành vi quỷ quyệt này?
Pacerier

27

Xin lưu ý rằng nếu bạn muốn sử dụng IPv6, có lẽ bạn muốn sử dụng HTTP_HOSThơn là SERVER_NAME. Nếu bạn nhập http://[::1]/các biến môi trường sẽ như sau:

HTTP_HOST = [::1]
SERVER_NAME = ::1

Điều này có nghĩa là, nếu bạn làm một mod_rewrite chẳng hạn, bạn có thể nhận được một kết quả khó chịu. Ví dụ cho chuyển hướng 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}/

Điều này chỉ áp dụng nếu bạn truy cập máy chủ mà không có tên máy chủ.


1
SiteGround, trong mã chuyển hướng http đến https trong kênh của họ, sử dụnghttps://%{SERVER_NAME}%{REQUEST_URI}
IXN

6

Nếu bạn muốn kiểm tra thông qua server.php hoặc bất cứ điều gì, bạn muốn gọi nó bằng cách sau:

<?php
    phpinfo(INFO_VARIABLES);
?>

hoặc là

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

    print_r($_SERVER);
?>

Sau đó truy cập nó với tất cả các URL hợp lệ cho trang web của bạn và kiểm tra sự khác biệt.


5

Phụ thuộc vào những gì tôi muốn tìm hiểu. SERVER_NAME là tên máy chủ của máy chủ, trong khi HTTP_HOST là máy chủ ảo mà máy khách được kết nối.


4
Rowland không chính xác, SERVER_NAMEthường là tên của Virtualhost, không phải máy chủ. Và trong Apache, SERVER_NAMEthường được điền với cùng giá trị như HTTP_HOST(xem câu trả lời của BalusC).
Simon East

1
@Simon, vì hầu hết các máy chủ hiện đang là Virtualhost, bạn có ý nghĩa gì với tên của "chính máy chủ"?
Pacerier

Nếu bạn đang chạy một máy chủ riêng ảo (VPS) với một trang web, bạn không cần phải giả sử SERVER_NAMEáp dụng cho máy chủ ảo. Tuy nhiên, người ta vẫn có thể sử dụng thiết lập máy chủ ảo cho một trang web. Nhiều người sử dụng lưu trữ chia sẻ, vì vậy tôi thấy quan điểm của bạn.
Anthony Rutledge

2

Phải mất một thời gian tôi mới hiểu mọi người có nghĩa là ' SERVER_NAMEđáng tin cậy hơn'. Tôi sử dụng một máy chủ được chia sẻ và không có quyền truy cập vào các chỉ thị máy chủ ảo. Vì vậy, tôi sử dụng mod_rewrite .htaccess để ánh xạ các HTTP_HOSTs khác nhau vào các thư mục khác nhau. Trong trường hợp đó, nó HTTP_HOSTcó ý nghĩa.

Tình huống tương tự nếu một người sử dụng máy chủ ảo dựa trên tên: lệnh ServerNametrong máy chủ ảo chỉ đơn giản nói tên máy chủ nào sẽ được ánh xạ tới máy chủ ảo này. Điểm mấu chốt là, trong cả hai trường hợp, tên máy chủ được cung cấp bởi máy khách trong yêu cầu ( HTTP_HOST), phải được khớp với một tên trong máy chủ, chính nó được ánh xạ tới một thư mục. Việc ánh xạ được thực hiện với các chỉ thị máy chủ ảo hay với các quy tắc mod_rewrite của htaccess là thứ yếu ở đây. Trong những trường hợp này, HTTP_HOSTsẽ giống như SERVER_NAME. Tôi vui vì Apache được cấu hình theo cách đó.

Tuy nhiên, tình hình là khác nhau với các máy chủ ảo dựa trên IP. Trong trường hợp này và chỉ trong trường hợp này, SERVER_NAMEHTTP_HOSTcó thể khác, bởi vì bây giờ máy khách chọn máy chủ theo IP, không phải theo tên. Thật vậy, có thể có cấu hình đặc biệt trong đó điều này là quan trọng.

Vì vậy, bắt đầu từ bây giờ, tôi sẽ sử dụng SERVER_NAME, chỉ trong trường hợp mã của tôi được chuyển trong các cấu hình đặc biệt này.


2

Giả sử một người có một thiết lập đơn giản (CentOS 7, Apache 2.4.x và PHP 5.6.20) và chỉ có một trang web (không giả sử lưu trữ ảo) ...

Theo nghĩa của PHP, $_SERVER['SERVER_NAME']là một phần tử mà các thanh ghi PHP trong siêu lớp $_SERVERdựa trên cấu hình Apache của bạn ( **ServerName**chỉ thị với UseCanonicalName On) trong httpd.conf (có thể là từ tệp cấu hình máy chủ ảo được bao gồm, bất cứ điều gì, v.v.). HTTP_HOST có nguồn gốc từ hosttiêu đề HTTP . Coi đây là đầu vào của người dùng Lọc và xác nhận trước khi sử dụng.

Dưới đây là một ví dụ về nơi tôi sử dụng $_SERVER['SERVER_NAME']làm cơ sở để so sánh. Phương pháp sau đây là từ một lớp con cụ thể mà tôi đã đặt tên ServerValidator(con của Validator). ServerValidatorkiểm tra sáu hoặc bảy yếu tố trong $ _SERVER trước khi sử dụng chúng.

Để xác định xem yêu cầu HTTP có phải là POST không, tôi sử dụng phương thức này.

public function isPOST()
{
    return (($this->requestMethod === 'POST')    &&  // Ignore
            $this->hasTokenTimeLeft()            &&  // Ignore
            $this->hasSameGETandPOSTIdentities() &&  // Ingore
            ($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')));
}

Vào thời điểm phương thức này được gọi, tất cả việc lọc và xác thực các phần tử $ _SERVER có liên quan sẽ xảy ra (và các thuộc tính có liên quan được đặt).

Dòng ...

($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')

... Kiểm tra xem $_SERVER['HTTP_HOST']giá trị (cuối cùng xuất phát từ hosttiêu đề HTTP được yêu cầu ) khớp với $_SERVER['SERVER_NAME'].

Bây giờ, tôi đang sử dụng speak superglobal để giải thích ví dụ của tôi, nhưng đó chỉ là vì một số người không quen với INPUT_GET, INPUT_POSTINPUT_SERVERliên quan đến filter_input_array().

Điểm mấu chốt là, tôi không xử lý các yêu cầu POST trên máy chủ của mình trừ khi tất cả bốn điều kiện được đáp ứng. Do đó, về mặt yêu cầu POST, thất bại trong việc cung cấp một HTTP hostheader (hiện diện kiểm tra trước đó) phép thuật Doom cho nghiêm ngặt HTTP 1.0 trình duyệt. Hơn nữa, các máy chủ yêu cầu phải phù hợp với giá trị cho ServerNametrong httpd.conf , và, bằng cách gia hạn, giá trị cho $_SERVER('SERVER_NAME')trong $_SERVERsuperglobal. Một lần nữa, tôi sẽ sử dụng INPUT_SERVERvới các hàm bộ lọc PHP, nhưng bạn bắt được sự trôi dạt của tôi.

Hãy nhớ rằng Apache thường xuyên sử dụng ServerNametrong các chuyển hướng tiêu chuẩn (chẳng hạn như bỏ dấu gạch chéo ra khỏi một URL: Ví dụ, http://www.foo.com trở thành http://www.foo.com/ ), ngay cả khi bạn không sử dụng viết lại URL.

Tôi sử dụng $_SERVER['SERVER_NAME']như là tiêu chuẩn, không $_SERVER['HTTP_HOST']. Có rất nhiều vấn đề qua lại về vấn đề này. $_SERVER['HTTP_HOST']có thể trống, vì vậy đây không phải là cơ sở để tạo các quy ước mã như phương thức công khai của tôi ở trên. Nhưng, chỉ vì cả hai có thể được đặt không đảm bảo chúng sẽ bằng nhau. Kiểm tra là cách tốt nhất để biết chắc chắn (ghi nhớ phiên bản Apache và phiên bản PHP).


0

Như balusC cho biết SERVER_NAME không đáng tin cậy và có thể được thay đổi trong cấu hình apache, cấu hình tên máy chủ của máy chủ và tường lửa có thể nằm giữa bạn và máy chủ.

Chức năng theo sau luôn trả về máy chủ thực (máy chủ đã gõ) mà không có cổng và nó gần như đáng tin cậy:

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

0

$ _SERVER ['SERVER_NAME'] dựa trên cấu hình máy chủ web của bạn. $ _SERVER ['HTTP_HOST'] dựa trên yêu cầu từ khách hàng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.