ความแตกต่างระหว่าง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_HOSTwww.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 จะลงทะเบียนใน$_SERVERsuperglobal ตามการกำหนดค่า 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_SERVERfilter_input_array()
บรรทัดล่างคือฉันไม่ได้จัดการคำขอ POST บนเซิร์ฟเวอร์ของฉันเว้นแต่ว่าจะปฏิบัติตามเงื่อนไขทั้งสี่ ดังนั้นในแง่ของการร้องขอ POST ความล้มเหลวในการจัดหาhostส่วนหัวHTTP (สถานะที่ทดสอบสำหรับก่อนหน้านี้) สะกดคำลงโทษสำหรับเบราว์เซอร์HTTP 1.0 ที่เข้มงวด นอกจากนี้เจ้าภาพขอจะต้องตรงกับค่าสำหรับServerNameในhttpd.confและโดยขยายค่าสำหรับ$_SERVER('SERVER_NAME')ใน$_SERVERsuperglobal อีกครั้งฉันจะใช้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']เป็นไปตามคำขอจากลูกค้า