ความแตกต่างระหว่างHTTP_HOST
และSERVER_NAME
ใน PHP คืออะไร?
ที่อยู่:
HTTP_POST
===$_SERVER['HTTP_HOST']
SERVER_NAME
===$_SERVER['SERVER_NAME']
เมื่อใดที่คุณจะพิจารณาใช้อีกตัวหนึ่งและทำไม
$_SERVER['HTTP_HOST']
หรือ$_SERVER['SERVER_NAME']
ความแตกต่างระหว่างHTTP_HOST
และSERVER_NAME
ใน PHP คืออะไร?
ที่อยู่:
HTTP_POST
=== $_SERVER['HTTP_HOST']
SERVER_NAME
=== $_SERVER['SERVER_NAME']
เมื่อใดที่คุณจะพิจารณาใช้อีกตัวหนึ่งและทำไม
$_SERVER['HTTP_HOST']
หรือ$_SERVER['SERVER_NAME']
คำตอบ:
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 รายงานข้อผิดพลาดเกี่ยวกับเรื่องที่ฉันได้เรียนรู้ว่ารากของปัญหาที่อยู่ในเว็บเซิร์ฟเวอร์ที่ใช้ว่ามันไม่ถูกต้องกลับ 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
มีความน่าเชื่อถือมากขึ้น แต่คุณขึ้นอยู่กับการกำหนดค่าเซิร์ฟเวอร์!
server_name
คำสั่ง โดยเฉพาะถ้าไม่มีการserver_name
ตั้งค่าก็_SERVER["SERVER_NAME"]
จะว่างเปล่า
HTTP_HOST
เป็นโฮสต์เป้าหมายที่ส่งโดยลูกค้า ผู้ใช้สามารถจัดการได้อย่างอิสระ มันไม่มีปัญหาที่จะส่งคำขอไปยังเว็บไซต์ของคุณขอเป็นค่าของHTTP_HOST
www.stackoverflow.com
SERVER_NAME
มาจากVirtualHost
ข้อกำหนดของเซิร์ฟเวอร์และดังนั้นจึงถือว่าเชื่อถือได้มากขึ้น ภายใต้เงื่อนไขบางประการที่เกี่ยวข้องกับการตั้งค่าเว็บเซิร์ฟเวอร์ของคุณ: ดูคำถาม SO นี้ซึ่งเกี่ยวข้องกับด้านความปลอดภัยของรูปแบบทั้งสอง
คุณไม่ควรพึ่งพาเพื่อความปลอดภัย ที่กล่าวว่าสิ่งที่จะใช้จริง ๆ ขึ้นอยู่กับสิ่งที่คุณต้องการทำ หากคุณต้องการระบุโดเมนที่สคริปต์ของคุณกำลังทำงานอยู่คุณสามารถใช้งานได้อย่างปลอดภัยHTTP_HOST
ตราบใดที่ค่าที่ไม่ถูกต้องซึ่งมาจากผู้ใช้ที่ประสงค์ร้ายไม่สามารถทำลายสิ่งใด ๆ ได้
UseCanonicalName on
ใน httpd.conf เพื่อบังคับSERVER_NAME
ให้เป็นชื่อเซิร์ฟเวอร์จริง
$_SERVER['SERVER_NAME']
เซิร์ฟเวอร์ที่กำหนดค่าไม่ดีจะตั้งค่า$_SERVER['SERVER_NAME']
ตามค่าHost:
คำขอของลูกค้า ทั้งสองมีค่าเท่ากัน
ดังที่ฉันได้กล่าวไปแล้วในคำตอบนี้หากเซิร์ฟเวอร์ทำงานบนพอร์ตอื่นที่ไม่ใช่ 80 (ตามปกติอาจเกิดขึ้นได้กับเครื่องพัฒนา / อินทราเน็ต) จากนั้นจะHTTP_HOST
มีพอร์ตในขณะที่SERVER_NAME
ไม่มี
$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'
(อย่างน้อยนั่นคือสิ่งที่ฉันสังเกตเห็นใน virtualhosts ที่ใช้พอร์ต 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}/
สิ่งนี้ใช้ได้เฉพาะถ้าคุณเข้าถึงเซิร์ฟเวอร์โดยไม่มีชื่อโฮสต์
https://%{SERVER_NAME}%{REQUEST_URI}
หากคุณต้องการตรวจสอบผ่าน server.php หรืออะไรก็ตามคุณต้องการโทรหาด้วยวิธีดังต่อไปนี้:
<?php
phpinfo(INFO_VARIABLES);
?>
หรือ
<?php
header("Content-type: text/plain");
print_r($_SERVER);
?>
จากนั้นเข้าถึงกับ URL ที่ถูกต้องทั้งหมดสำหรับเว็บไซต์ของคุณและตรวจสอบความแตกต่าง
ขึ้นอยู่กับสิ่งที่ฉันต้องการค้นหา SERVER_NAME เป็นชื่อโฮสต์ของเซิร์ฟเวอร์ในขณะที่ HTTP_HOST เป็นโฮสต์เสมือนที่ไคลเอ็นต์เชื่อมต่อ
SERVER_NAME
มักจะเป็นชื่อของ VirtualHost ไม่ใช่เซิร์ฟเวอร์ และใน Apache SERVER_NAME
นั้นมักมีค่าเหมือนกันHTTP_HOST
(ดูคำตอบของ BalusC)
SERVER_NAME
ใช้กับโฮสต์เสมือน อย่างไรก็ตามหนึ่งยังคงสามารถใช้การตั้งค่าโฮสต์เสมือนสำหรับหนึ่งไซต์ หลายคนใช้โฮสติ้งที่ใช้ร่วมกันดังนั้นฉันเห็นประเด็นของคุณ
ฉันใช้เวลาสักครู่เพื่อทำความเข้าใจกับสิ่งที่ผู้คนหมายถึง ' 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
ในกรณีที่รหัสของฉันได้รับการพอร์ตในการกำหนดค่าพิเศษเหล่านี้
สมมติว่ามีการตั้งค่าอย่างง่าย (CentOS 7, Apache 2.4.x และ PHP 5.6.20) และมีเพียงหนึ่งเว็บไซต์ (ไม่ใช่สมมติว่าโฮสต์เสมือน) ...
ในแง่$_SERVER['SERVER_NAME']
ของ PHP องค์ประกอบของ PHP จะลงทะเบียนใน$_SERVER
superglobal ตามการกำหนดค่า 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']
ไม้ขีดไฟ
ตอนนี้ผมใช้พูด superglobal จะอธิบายตัวอย่างของฉัน แต่นั่นเป็นเพียงเพราะบางคนไม่คุ้นเคยกับINPUT_GET
, INPUT_POST
และในเรื่องที่เกี่ยวกับINPUT_SERVER
filter_input_array()
บรรทัดล่างคือฉันไม่ได้จัดการคำขอ POST บนเซิร์ฟเวอร์ของฉันเว้นแต่ว่าจะปฏิบัติตามเงื่อนไขทั้งสี่ ดังนั้นในแง่ของการร้องขอ POST ความล้มเหลวในการจัดหาhost
ส่วนหัวHTTP (สถานะที่ทดสอบสำหรับก่อนหน้านี้) สะกดคำลงโทษสำหรับเบราว์เซอร์HTTP 1.0 ที่เข้มงวด นอกจากนี้เจ้าภาพขอจะต้องตรงกับค่าสำหรับServerName
ในhttpd.confและโดยขยายค่าสำหรับ$_SERVER('SERVER_NAME')
ใน$_SERVER
superglobal อีกครั้งฉันจะใช้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)
ดังที่ balusC กล่าวว่า SERVER_NAME ไม่น่าเชื่อถือและสามารถเปลี่ยนแปลงได้ในการกำหนดค่า Apache, ชื่อเซิร์ฟเวอร์การกำหนดค่าเซิร์ฟเวอร์และไฟร์วอลล์ที่สามารถอยู่ระหว่างคุณและเซิร์ฟเวอร์
ฟังก์ชั่นต่อไปนี้จะส่งคืนโฮสต์จริง (โฮสต์ที่พิมพ์โดยผู้ใช้) โดยไม่มีพอร์ตและเกือบจะเชื่อถือได้:
function getRealHost(){
list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
return $realHost;
}
$ _SERVER ['SERVER_NAME']ขึ้นอยู่กับการกำหนดค่าเว็บเซิร์ฟเวอร์ของคุณ $ _SERVER ['HTTP_HOST']เป็นไปตามคำขอจากลูกค้า