HTTP_HOST = SERVER_NAME + (SERVER_PORT != 80 && SERVER_PORT != 443 ? ':' . SERVER_PORT : '')
| 變量 | 來源 | 包含端口 | 典型值 |
|---|---|---|---|
$_SERVER['HTTP_HOST'] | 客戶端請求頭 Host: | 是(非標準端口時) | example.com 或 example.com:8080 |
$_SERVER['SERVER_NAME'] | 服務器配置 | 否 | example.com |
$_SERVER['SERVER_PORT'] | 服務器監聽端口 | 純端口號 | 80、443、8080 |
<?php
// 請求:https://example.com:8080/path
var_dump($_SERVER['HTTP_HOST']); // string(17) "example.com:8080"
var_dump($_SERVER['SERVER_NAME']); // string(11) "example.com"
var_dump($_SERVER['SERVER_PORT']); // string(4) "8080"
// 請求:https://example.com/path (443)
var_dump($_SERVER['HTTP_HOST']); // string(11) "example.com" (標準端口不顯示)
var_dump($_SERVER['SERVER_NAME']); // string(11) "example.com"
var_dump($_SERVER['SERVER_PORT']); // string(3) "443"
?>
<?php
function currentUrl() {
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
return $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
}
// 正確處理非標準端口:https://example.com:8080/foo
?>
<?php
// Apache VirtualHost / Nginx server_name 配置的主域名
$mainDomain = $_SERVER['SERVER_NAME']; // 不受客戶端 Host 頭偽造影響
if ($mainDomain === 'api.example.com') { /* API 邏輯 */ }
?>
<?php
$allowedHosts = ['example.com', 'www.example.com', 'api.example.com'];
$host = $_SERVER['HTTP_HOST'];
// 移除端口部分比較
$host = preg_replace('/:\d+$/', '', $host);
if (!in_array($host, $allowedHosts, true)) {
header('HTTP/1.1 400 Bad Request');
exit('Invalid Host header');
}
?>
server_name 決定 SERVER_NAMEServerName / ServerAlias 決定 SERVER_NAMEUseCanonicalName On (Apache) 強制 SERVER_NAME 為配置值,忽略客戶端 HostHTTP_HOST 可能被改寫,需配合 proxy_set_header Host $host;HTTP_HOST 來自客戶端,含端口,用於生成面向用戶的 URL;SERVER_NAME 來自服務器配置,不含端口,用於內部邏輯判斷。理解二者差異,正確選擇,能避免端口丟失、Host 頭注入等安全與功能 Bug。