HTTP_HOST และ SERVER_NAME ใน PHP แตกต่างกันอย่างไร


533

ความแตกต่างระหว่างHTTP_HOSTและSERVER_NAMEใน PHP คืออะไร?

ที่อยู่:

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

เมื่อใดที่คุณจะพิจารณาใช้อีกตัวหนึ่งและทำไม


14
"ปกติฉันจะใช้ HTTP_HOST เพื่อให้ผู้ใช้อยู่ในชื่อโฮสต์ที่แน่นอนที่พวกเขาเริ่มต้นตัวอย่างเช่นถ้าฉันมีเว็บไซต์เดียวกันบนโดเมน. com และ. org ฉันไม่ต้องการส่งคนจาก. org ไปยัง .com โดยเฉพาะอย่างยิ่งหากพวกเขาอาจมีโทเค็นการเข้าสู่ระบบใน. org ว่าพวกเขาจะสูญเสียถ้าส่งไปยังโดเมนอื่น " - นี่และบางจุดที่น่าสนใจอื่น ๆ จากstackoverflow.com/questions/1459739/…
Yarin

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 รายงานข้อผิดพลาดเกี่ยวกับเรื่องที่ฉันได้เรียนรู้ว่ารากของปัญหาที่อยู่ในเว็บเซิร์ฟเวอร์ที่ใช้ว่ามันไม่ถูกต้องกลับ 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มีความน่าเชื่อถือมากขึ้น แต่คุณขึ้นอยู่กับการกำหนดค่าเซิร์ฟเวอร์!


5
โอเคสิ่งนี้ช่วยแก้ปัญหาของฉันซึ่งไม่เกี่ยวข้องกับ OP แต่มีความเกี่ยวข้อง ฉันกังวลมากเกี่ยวกับปัญหาด้านความปลอดภัยโดยใช้สิ่งที่เบราว์เซอร์สามารถจัดหาได้ คำตอบนี้ช่วยได้มาก ขอบคุณที่สละเวลามารวมกัน
Yitzhak

2
ทำไมคุณถึงบอกว่า HTTP_HOST ไม่น่าเชื่อถือ ใช่มันมาจากผู้ใช้ แต่ถ้าผู้ใช้ให้ค่าปลอมการกำหนดค่าเซิร์ฟเวอร์ของคุณจะกลับมาโดยอัตโนมัติ 503 และสคริปต์ PHP ของคุณจะไม่ทำงาน!
Pacerier

1
@Pacerier: ในขณะที่เขียนคำตอบนี้มันไม่ได้ รุ่นที่กล่าวถึงในคำตอบ ฉันไม่ติดตาม PHP อีกต่อไปดังนั้นฉันจึงไม่สามารถบอกได้ว่ามันมีการเปลี่ยนแปลงในเวอร์ชันที่ใหม่กว่าหรือไม่
BalusC

2
วิธีง่ายๆในการหลอกลวง Apache จาก WinXP คือการเพิ่มบรรทัดไปยังไฟล์ 'โฮสต์' โดยระบุว่า 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_HOSTwww.stackoverflow.com

SERVER_NAMEมาจากVirtualHostข้อกำหนดของเซิร์ฟเวอร์และดังนั้นจึงถือว่าเชื่อถือได้มากขึ้น ภายใต้เงื่อนไขบางประการที่เกี่ยวข้องกับการตั้งค่าเว็บเซิร์ฟเวอร์ของคุณ: ดูคำถาม SO นี้ซึ่งเกี่ยวข้องกับด้านความปลอดภัยของรูปแบบทั้งสอง

คุณไม่ควรพึ่งพาเพื่อความปลอดภัย ที่กล่าวว่าสิ่งที่จะใช้จริง ๆ ขึ้นอยู่กับสิ่งที่คุณต้องการทำ หากคุณต้องการระบุโดเมนที่สคริปต์ของคุณกำลังทำงานอยู่คุณสามารถใช้งานได้อย่างปลอดภัยHTTP_HOSTตราบใดที่ค่าที่ไม่ถูกต้องซึ่งมาจากผู้ใช้ที่ประสงค์ร้ายไม่สามารถทำลายสิ่งใด ๆ ได้


8
ใช่ แต่คำขอที่ขอค่า HTTP_HOST ของ www.stackoverflow.com จะถูกปฏิเสธโดยเซิร์ฟเวอร์ HTTP ส่วนใหญ่ล่วงหน้าดังนั้นสคริปต์ PHP จะไม่เห็นคำขอเลย!
Pacerier

2
@Pacerier จริง แต่ไม่เสมอไปหากเซิร์ฟเวอร์ไม่ได้รับการกำหนดค่าอย่างเหมาะสม
Pekka

1
ดังที่กล่าวไว้ในโพสต์ของ BalusC เมื่อคุณเข้าถึง Apache virtualhost โดย IP ตัวแปรทั้งสองนี้มี IP (โดยค่าเริ่มต้น) ไม่ใช่ชื่อเซิร์ฟเวอร์จริง คุณต้องใช้UseCanonicalName onใน httpd.conf เพื่อบังคับSERVER_NAMEให้เป็นชื่อเซิร์ฟเวอร์จริง
Simon East

@Pekka 웃 ถ้าเซิร์ฟเวอร์ไม่ได้กำหนดค่าอย่างถูกต้องจะไม่ทำงานเช่นกัน$_SERVER['SERVER_NAME'] เซิร์ฟเวอร์ที่กำหนดค่าไม่ดีจะตั้งค่า$_SERVER['SERVER_NAME']ตามค่าHost:คำขอของลูกค้า ทั้งสองมีค่าเท่ากัน
Pacerier

คำตอบที่ดี แต่ฉันจะไม่สมมติว่าโฮสต์เสมือน
Anthony Rutledge

55

ดังที่ฉันได้กล่าวไปแล้วในคำตอบนี้หากเซิร์ฟเวอร์ทำงานบนพอร์ตอื่นที่ไม่ใช่ 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'

2
พวกเขาจะแก้ไขพฤติกรรมร้ายกาจเมื่อใด
Pacerier

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)
Simon East

1
@Simon เนื่องจากโฮสต์ส่วนใหญ่ตอนนี้เป็น VirtualHost คุณจะหมายถึงอะไรโดยใช้ชื่อของ "เซิร์ฟเวอร์ตัวเอง"?
Pacerier

หากคุณใช้เซิร์ฟเวอร์ส่วนตัวเสมือน (VPS) กับเว็บไซต์เดียวคุณไม่จำเป็นต้องสมมติว่าSERVER_NAMEใช้กับโฮสต์เสมือน อย่างไรก็ตามหนึ่งยังคงสามารถใช้การตั้งค่าโฮสต์เสมือนสำหรับหนึ่งไซต์ หลายคนใช้โฮสติ้งที่ใช้ร่วมกันดังนั้นฉันเห็นประเด็นของคุณ
Anthony Rutledge

2

ฉันใช้เวลาสักครู่เพื่อทำความเข้าใจกับสิ่งที่ผู้คนหมายถึง ' 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ในกรณีที่รหัสของฉันได้รับการพอร์ตในการกำหนดค่าพิเศษเหล่านี้


2

สมมติว่ามีการตั้งค่าอย่างง่าย (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)


0

ดังที่ balusC กล่าวว่า SERVER_NAME ไม่น่าเชื่อถือและสามารถเปลี่ยนแปลงได้ในการกำหนดค่า Apache, ชื่อเซิร์ฟเวอร์การกำหนดค่าเซิร์ฟเวอร์และไฟร์วอลล์ที่สามารถอยู่ระหว่างคุณและเซิร์ฟเวอร์

ฟังก์ชั่นต่อไปนี้จะส่งคืนโฮสต์จริง (โฮสต์ที่พิมพ์โดยผู้ใช้) โดยไม่มีพอร์ตและเกือบจะเชื่อถือได้:

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

0

$ _SERVER ['SERVER_NAME']ขึ้นอยู่กับการกำหนดค่าเว็บเซิร์ฟเวอร์ของคุณ $ _SERVER ['HTTP_HOST']เป็นไปตามคำขอจากลูกค้า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.