PHP 透過 $_SERVER 提供伺服器與執行環境資訊,關鍵 URL 相關鍵值如下:
| 鍵名 | 說明 | 範例值 |
|---|---|---|
REQUEST_URI | 請求 URI(含查詢字串),不含域名 | /article/123?page=2&sort=desc |
HTTP_HOST | 客戶端請求的 Host 標頭(含端口若非標準) | example.com:8080 |
SERVER_NAME | 伺服器設定的主機名(VirtualHost ServerName) | example.com |
SERVER_PORT | 伺服器監聽埠 | 80, 443, 8080 |
HTTPS | HTTPS 請求時為 'on',否則未設定 | on |
SCRIPT_NAME | 當前執行腳本路徑 | /index.php |
PHP_SELF | 同 SCRIPT_NAME(CGI 模式下可能不同) | /index.php |
QUERY_STRING | 查詢字串(不含 ?) | page=2&sort=desc |
DOCUMENT_ROOT | 網站根目錄絕對路徑 | /var/www/html |
<?php
/**
* 獲取當前頁面完整 URL
* @param bool $withQuery 是否包含查詢字串
* @return string 完整 URL
*/
function getCurrentUrl($withQuery = true) {
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
$uri = $_SERVER['REQUEST_URI'] ?? '';
if (!$withQuery) {
$uri = strtok($uri, '?');
}
// 處理非標準埠口(HTTP:80, HTTPS:443 為標準,不顯示)
$port = $_SERVER['SERVER_PORT'] ?? 80;
if (($protocol === 'http' && $port != 80) || ($protocol === 'https' && $port != 443)) {
$host .= ':' . $port;
}
return "{$protocol}://{$host}{$uri}";
}
// 使用範例
echo getCurrentUrl(); // https://example.com/article/123?page=2&sort=desc
echo getCurrentUrl(false); // https://example.com/article/123
?>
<?php
// 1. 僅取得基礎 URL(無查詢字串)
function getBaseUrl() {
return getCurrentUrl(false);
}
// 2. 取得網站根目錄 URL
function getSiteRootUrl() {
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
$port = $_SERVER['SERVER_PORT'] ?? 80;
if (($protocol === 'http' && $port != 80) || ($protocol === 'https' && $port != 443)) {
$host .= ':' . $port;
}
// 移除腳本路徑
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
$rootPath = rtrim(dirname($scriptName), '/\\');
return "{$protocol}://{$host}{$rootPath}";
}
// 3. 取得當前目錄 URL
function getCurrentDirUrl() {
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
$port = $_SERVER['SERVER_PORT'] ?? 80;
if (($protocol === 'http' && $port != 80) || ($protocol === 'https' && $port != 443)) {
$host .= ':' . $port;
}
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
$dirPath = rtrim(dirname($scriptName), '/\\');
$requestUri = $_SERVER['REQUEST_URI'] ?? '';
$requestDir = rtrim(dirname(parse_url($requestUri, PHP_URL_PATH)), '/\\');
return "{$protocol}://{$host}{$requestDir}";
}
// 4. 重建不含特定參數的 URL(分頁、排序保留,移除搜尋關鍵字等)
function rebuildUrlWithoutParams(array $excludeParams = array()) {
$query = $_SERVER['QUERY_STRING'] ?? '';
parse_str($query, $params);
foreach ($excludeParams as $param) {
unset($params[$param]);
}
$newQuery = http_build_query($params);
$uri = $_SERVER['REQUEST_URI'] ?? '';
$baseUri = strtok($uri, '?');
return $baseUri . ($newQuery ? '?' . $newQuery : '');
}
?>
HTTP_HOST:來自客戶端請求標頭,用戶可控,可能被偽造SERVER_NAME:來自伺服器設定,受信任,但需正確配置 VirtualHostHTTP_HOST(支援多域名、非標準埠口),安全敏感場景驗證 Host 白名單REQUEST_URI:原始請求路徑+查詢字串,最完整SCRIPT_NAME:當前執行腳本路徑,不含查詢字串PHP_SELF:歷史相容變數,CGI/Apache 模組下行為可能不同PHP_SELF 易遭 XSS 攻擊(form action=""),請改用 SCRIPT_NAME 或留空 action=""<?php
// 代理環境下,真實協定可能在 X-Forwarded-Proto
function getRealProtocol() {
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$protos = explode(',', $_SERVER['HTTP_X_FORWARDED_PROTO']);
return trim($protos[0]); // 取第一層代理協定
}
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
}
// 真實 Host 可能在 X-Forwarded-Host
function getRealHost() {
if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$hosts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
return trim($hosts[0]);
}
return $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
}
?>
<?php
// 取得目錄部分
$path = '/var/www/html/article/123';
echo dirname($path); // /var/www/html/article
// 取得檔名部分
echo basename($path); // 123
echo basename($path, '.php'); // 移除副檔名
// 取得絕對規範路徑(解析 ..、符號連結)
echo realpath('/var/www/html/../html/article'); // /var/www/html/article
// 組合 URL 路徑(避免雙斜線)
function joinPaths($base, $relative) {
return rtrim($base, '/') . '/' . ltrim($relative, '/');
}
?>
<?php
function getCanonicalUrl() {
$url = getCurrentUrl(false); // 不含查詢字串的標準 URL
// 可加入邏輯:移除 utm_* 參數、統一 www/非 www、強制 HTTPS
return $url;
}
?>
<link rel="canonical" href="<?php echo htmlspecialchars(getCanonicalUrl()); ?>">
<?php
// 登入/註冊/評論後導回
$redirectUrl = $_SERVER['HTTP_REFERER'] ?? getSiteRootUrl();
// 防開放重導向:驗證同域名
if (parse_url($redirectUrl, PHP_URL_HOST) !== ($_SERVER['HTTP_HOST'] ?? '')) {
$redirectUrl = getSiteRootUrl();
}
header("Location: {$redirectUrl}");
exit;
?>
<?php
function switchLocaleUrl($newLocale) {
$uri = $_SERVER['REQUEST_URI'] ?? '';
// 移除舊語言前綴 /zh-TW/、/en/ 等
$uri = preg_replace('~^/(zh-TW|zh-CN|en|ja)/~', '/', $uri);
// 加入新語言前綴
return '/' . $newLocale . $uri;
}
?>
proxy_set_header X-Forwarded-Proto $scheme;。
urldecode(),編碼用 urlencode() 或 rawurlencode()(RFC 3986 標準,空格編為 %20 而非 +)。
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 或 strtok($_SERVER['REQUEST_URI'], '?')。
$_SERVER['SCRIPT_FILENAME'] 或 $argv[0]。
掌握 $_SERVER 變數特性與組合技巧,能精準構建各類 URL 需求。核心原則:優先用 REQUEST_URI + HTTP_HOST、注意代理環境 Header、防範 PHP_SELF XSS、善用 parse_url/http_build_query 處理查詢字串。將常用邏輯封裝為工具類別(如 Url::current()、Url::previous()、Url::route()),提升開發效率與程式碼可維護性。