HTTP

HTTP 虛擬主機 (Virtual Host)

(2-1) 統一資源識別符 (URI) 所述,
Client 會先透過 名稱解析服務 (ex: DNS),
找到 Server (或 代理、閘道、負載平衡器) 的 IP 位址,並藉此訪問:
dns-example
 
然而,因早期 HTTP 的錯誤設計,Client 並不會送出 目標 URI !  
 
若 單一主機 僅代表一個 網域名稱 那還好,那多個 呢 ?
 
本篇將說明 Server 與 代理 (proxy),
在沒有 目標 URI 的情況下,如何處理訊息路由。
 


 

Host 表頭欄位

有些人會問:
「Server 如何取得 client 輸入的 URL ?」
 
Ans:
Server 無法取得 目標 URI (target URI),是透過請求訊息中的:

Host 表頭欄位 -- 取得網域名稱 (domain name),

請求目標 (request-target) -- 取得路徑 (資源)。

 
重組 (reconstructs) 的方式,模擬出 目標 URI,
稱為 — — 有效請求 URI (Effective Request URI)
 
以 PHP 為例:

$effectiveRequestURI = (isset($_SERVER['HTTPS']) ? "https://" : "https://")
    . "$_SERVER[HTTP_HOST]"
    . "$_SERVER[REQUEST_URI]";

 
使用者代理 (user agent) 送出請求訊息前,
需分離出 目標 URI 中的 authority 元件 為 Host 表頭欄位值 :
(即 host + [ ":" port ])

目標 URI =
"http(s):" "//" authority path-abempty [ "?" query ] [ "#" fragment ]

 
例如,將此 GET 請求 送至 源伺服器 (origin server) — https://www.example.com/pub/WWW/:

GET /pub/WWW/ HTTP/1.1

Host: example.com

...

 
Host 表頭欄位 是處理請求的關鍵資訊,
對所有 HTTP/1.1 請求訊息 (request message),皆 必須包含 Host 表頭欄位
且建議做為 第一個 表頭欄位 (緊接在 request-line 之後),以利 Server 解析欄位。
 
若 請求訊息 (request message) 缺少了 Host 表頭欄位、
包含了多個 Host 表頭欄位 或 欄位值無效,
Server 需視請求 無效,並發送 400 (Bad Request) 的回應狀態碼。
 
如果 代理 (proxy) 接收到了 絕對形式 (absolute-form) 的 請求目標 (request-target),
需將 請求訊息 中的 Host 表頭欄位 視為 假的 忽略,
並以 絕對形式 (absolute-form) 提供的 URI 為主,生成新的 Host 表頭欄位
 
fake
 


 

虛擬主機 (Virtual Host)

Host 表頭欄位 是 HTTP/1.0 與 HTTP/1.1 最重要的改變之一,
解決了 HTTP/1.0 假定 「Server 與 網站 是 一對一」的錯誤,
因此,我們能標準的使用 虛擬主機 (Virtual Host) !
 
虛擬主機 (Virtual Host) 或稱 虛擬伺服器 (Virtual Server),
是指在同一機器上運行多個網站,
最適合貧窮如我,只租的起一台 Server 的人 😭。
 
又分為 基於網域名稱 (Name-based)基於 IP 位址 (IP-based)
 
 

基於網域名稱 (Name-based)

基於網域名稱 (Name-based),是最常見的 虛擬主機 (Virtual Host) 形式,是指:

在同一機器、IP 位址,架設 多個網域名稱 (例如 example.comabc.com) 的 網站,
當然,也包含子網域啦 (例如 blog.example.commail.example.com) !

 
以常見的 Web Server 為例:
 
Nginx (1.10) 可透過 網站設定檔 中 (通常位於 /etc/nginx/sites-available) 的,
server_name 指令 — 設定對應的網域名稱;root 指令 — 設定網站根目錄:

server {
    listen      80 default_server;
    server_name example.com www.example.net;
    root        /var/www/domain;
    ...
}

server {
    listen       80;
    server_name  abc.com;
    root         /var/www/otherdomain;
    ...
}

 
若使用的是新的網站設定檔,需記得使用 ln -s 指令,
建立 符號連結 (symbolic link) — 捷徑:

sudo ln -s /etc/nginx/sites-available/新網站設定檔 /etc/nginx/sites-enabled

 
並重啟 nginx:

sudo systemctl restart nginx

 
Apache (2.4) 也是大同小異:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.net;
    DocumentRoot "/www/domain"
</VirtualHost>

<VirtualHost *:80>
    ServerName abc.com
    DocumentRoot "/www/otherdomain"
</VirtualHost>

 


 

基於 IP 位址 (IP-based)

基於 IP 位址 (IP-based),是指:

在同一機器、不同的 IP 位址,架設 多個網站 (例如 192.168.1.1:80172.20.30.40:80),
當然,也包含 埠號 (TCP port) 啦 (例如 192.168.1.1:7577192.168.1.1:9487) !

 
以常見的 Web Server 為例:
 
Nginx (1.10) 可透過 網站設定檔 中 (通常位於 /etc/nginx/sites-available) 的,
listen 指令 — 設定監聽的 IP 位址;root 指令 — 設定網站根目錄:

server {
    listen      192.168.1.1:80 default_server;
    server_name example.com www.example.net;
    root        /var/www/domain;
    ...
}

server {
    listen       172.20.30.40:9487;
    server_name  abc.com;
    root         /var/www/otherdomain;
    ...
}

 
同樣,若使用的是新的網站設定檔,需記得使用 ln -s 指令,
建立 符號連結 (symbolic link) — 捷徑:

sudo ln -s /etc/nginx/sites-available/新網站設定檔 /etc/nginx/sites-enabled

並重啟 nginx:

sudo systemctl restart nginx

 
Apache (2.4) 也是大同小異:

<VirtualHost 192.168.1.1:80>
    ServerName example.com
    ServerAlias www.example.net;
    DocumentRoot "/www/domain"
</VirtualHost>

<VirtualHost 172.20.30.40:9487>
    ServerName abc.com
    DocumentRoot "/www/otherdomain"
</VirtualHost>

 
詳細配置 請參考:
Nginx 官網Apache 官網
 
 

作者: 鄭中勝
喜愛音樂,但不知為何總在打程式 ? 期許能重新審視、整理自身所學,幫助有需要的人。

發表迴響