# 網頁伺服器 Nginx
###### tags: `ncnu`, `lsa`, `nginx`
[TOC]
---
## 網頁伺服器概論
### 什麼是 Server?
伺服器(Server)這個名詞,最早的意涵其實很直白——「提供服務的人或東西」。在電腦網路世界中,凡是能接收請求並回應資料的實體,都可以被稱為伺服器。不論它是一台電腦、一個軟體,甚至是一個雲端平台,重點在於它「提供服務(Service)」這件事。
以實際例子來說:
當你打開瀏覽器輸入 `https://www.google.com` 時,你的電腦會向 Google 的伺服器發出請求,伺服器接收到後回傳網頁內容——這整個過程中,Google 的主機就是伺服器,而你的電腦則是「客戶端(Client)」。
常見伺服器種類如下:
* **網頁伺服器(Web Server)**:回應使用者的網頁請求,提供 HTML、CSS、圖片等內容。
* **資料庫伺服器(Database Server)**:儲存與管理資料,供其他系統查詢。
* **檔案伺服器(File Server)**:集中管理檔案與資料夾,支援上傳與下載。
* **郵件伺服器(Email Server)**:處理電子郵件的寄送、接收與儲存。
* **DNS 伺服器(Domain Name Server)**:把我們輸入的網域名稱(如 `google.com`)轉換成實際的 IP 位址,讓網路找到正確的目標主機。
#### 什麼是 Web Server?
Web Server 是一種提供網頁相關服務的伺服器。它的主要功能是讓使用者能夠透過瀏覽器,在世界各地任何連上網路的地方顯示自己的網頁內容。
Web Server 存放著網路伺服器軟體以及網站檔案,例如 HTML、CSS、JavaScript、圖片、影片等。當客戶端 (Client),通常是瀏覽器(如 Chrome、Firefox),主動向伺服器發送請求 (request) 時,Web Server 會接收並處理這些請求,然後回傳一個回應 (response) 給客戶端,使客戶端能夠呈現這些資訊。
##### Client-Server 架構
```mermaid
graph TD
A[Client 瀏覽器] <-->|HTTP Request/Response| B[Web Server]
B --> C[網站檔案]
```
<p style="text-align: center;">Client-Server 架構示意圖</p>
Web Server 通常會在主機上開啟一個預設為 80 的 Port,其他電腦即可透過該主機的 IP 和這個 Port 與其建立連線。
```mermaid
flowchart TB
A1[Client]
A2[Client]
A3[Client]
B[獨立IP]
C[主機 Nginx Port 80]
A1 --> B
A2 --> B
A3 --> B
B --> C
```
<p style="text-align: center;">Web Server 開啟 80 Port 示意圖</p>
### HTTP(Hypertext Transfer Protocol)
HTTP,全名 **H**yper**t**ext **T**ransfer **P**rotocol,「超文本傳輸協定」。
它是 **TCP/IP 模型**中的「應用層(Application Layer)」協定,用來規範 **Client** 與 **Server** 之間,如何發送請求(Request)與接收回應(Response)。
HTTP 預設使用的埠號(Port)是 **80**,但這是慣例設定,協定本身並不限定埠號。
#### 🌐 協定比較表
| 協定 | 所屬層級 (TCP/IP 模型) | 是否加密 | 預設埠號 | 備註 |
| ----- | ---------------- | ---- | ---- | ------------------------------------- |
| HTTP | 應用層 | 否 | 80 | 傳輸明文,安全性低 |
| HTTPS | 應用層(經 TLS 加密) | 是 | 443 | 需要向CA 機構申請憑證,自簽名的憑證大多不被信任 |
---
#### 📌 重點整理
* HTTP 的核心在於定義「請求/回應」的溝通格式與行為,而**不是**定義 Port。
* HTTP 使用 TCP 進行資料傳輸,確保資料封包可靠、有序抵達。
* HTTPS 是 HTTP 加上 **TLS(Transport Layer Security)** 加密後的安全版本。
* **Let's Encrypt** 提供免費憑證服務,使加密網站普及化,推動整個網路從「HTTP」邁向「HTTPS」。
telnet moli.lsa.tw 80
```bash!
GET / HTTP/1.0
Host: moli.lsa.tw
```
### Port (通訊埠ㄅㄨˋ)


Port 用於區分同一個 IP 地址下的不同服務。每個 Port 提供特定的服務,例如 SSH 使用 22 Port,網頁瀏覽預設使用 80 Port。
#### 為什麼需要 Port?
當電腦傳輸和接收來自網路的資料時(例如來自 Chrome、Telegram、遊戲等),電腦需要區分這些混合的資料應該傳送到哪個服務處理。Port 的作用就像郵局的窗口,封包在回傳到電腦時會標記要給哪個 Port,電腦便會將封包分配給目前佔用該 Port 的服務,從而確保資料傳送給正確的應用程式。
#### Port 號範圍
Port 號以 16 位元表示,範圍從 0 到 65535。
- **0**:保留 Port,通常不會啟用。
- **1 ~ 1023 (Well-Known Ports)**:這些 Port 需要超級使用者 (root) 權限才能佔用和使用,主要用於一些常見的通訊服務。由 IANA (Internet Assigned Numbers Authority) 進行管理和分配。
常見的 Well-Known Ports:
- **20/21**: FTP (檔案傳輸協定)
- **22**: SSH (安全遠端登入)
- **23**: Telnet
- **25**: SMTP (郵件傳送)
- **53**: DNS (網域名稱系統)
- **80**: HTTP (網頁瀏覽)
- **110**: POP3 (郵件接收)
- **143**: IMAP (郵件接收)
- **443**: HTTPS (加密網頁瀏覽)
**1024 ~ 49151 (Registered Ports)**:公司或組織可以向 IANA 申請註冊這些 Port。它們不需要超級使用者權限,用於特定的服務,例如 MySQL 資料庫服務的 3306 Port。
**49152 ~ 65535 (Private/Dynamic Ports)**:這些 Port 未向 IANA 註冊,可以在本地或由應用程式動態使用。
#### `/etc/services` 檔案
`/etc/services` 檔案記錄了各種服務會佔用的 Port 號,包含服務名稱、Port 號、使用的協定以及別名。
```bash
$ cat /etc/services
```
你可以在這個檔案中查看系統預設的服務與 Port 對應關係。
#### 檢查開啟的 Port
可以使用 `ss` 指令來查看目前開啟的 Port 和網路連線狀態。
```bash
sudo ss -ntupl
```
可以帶入哪些參數:
- `-a`: 顯示所有的網路連線狀態(包含監聽中和已建立的連線)
- `-t`: 顯示 TCP 連線
- `-u`: 顯示 UDP 連線
- `-p`: 顯示此連線的 PID(Process ID)和程式名稱。若沒有 `sudo` 權限,則無法顯示 PID/Program name
- `-l`: 只顯示處於 LISTEN(監聽)狀態的連線
- `-n`: 以數字顯示 IP 位址和 Port 號,而不轉換成主機名和服務名
輸出欄位說明:
- `Netid`: 協定類型(tcp 或 udp)
- `State`: 連線狀態,`LISTEN` 表示服務正在監聽 Port,`ESTAB` 表示連線已建立
- `Recv-Q` / `Send-Q`: 接收和傳送佇列中的資料量
- `Local Address:Port`: 本地 IP 和 Port,`0.0.0.0` 或 `*` 表示可接受來自任何位址的請求
- `Peer Address:Port`: 遠端位址和 Port,`*:*` 表示尚未建立具體連線(監聽狀態)
- `Process`: 程序 ID 和程式名稱
---
## 網頁伺服器,以 Nginx 為例
在了解 Nginx 之前,讓我們先認識一下網頁伺服器的發展脈絡。
### 網頁伺服器市占率演變

根據歷年市場調查數據:
- **2010 年代初期**:Apache 獨占鰲頭,市占率超過 60%
- **2019 年**:Nginx 首次超越 Apache,成為市占率最高的網頁伺服器
- **2024 年最新數據**:
- Nginx 約占 39.36%
- Apache 約占 25.72%
- Microsoft 約佔 9.63%
- 其他伺服器(包含 Lighttpd、Caddy、 等)約占60%
### 主要網頁伺服器介紹
#### 1. Apache HTTP Server
- **架構特性**:採用「一個請求、一個 Process」的處理模式
- **優勢**:歷史悠久、模組豐富、設定靈活
- **劣勢**:記憶體使用較多、高並行處理能力有限
- **現況**:目前主要用於維護舊系統,新專案較少採用
#### 2. Lighttpd(讀作 lighty)
- **架構特性**:輕量級設計
- **優勢**:靜態檔案處理效率高,**靜態內容效能強於 Apache**
- **劣勢**:動態內容處理能力有限、社群相對較小
- **適用場景**:靜態網站、資源受限環境
#### 3. Nginx(發音同「engine X」)
**誕生背景**:為了解決 **C10K 問題**(同時處理一萬個並行連線)而設計,目標是超越 Apache 的效能極限。
**核心特性**:
- **事件驅動架構**:不像 Apache 每個請求都開一個 Process,而是用事件循環處理多個連線
- **非同步非阻塞**:能同時處理大量並行請求而不占用過多資源
- **模組化設計**:功能透過模組擴充,保持核心輕量
**效能表現**(2024 年數據):
- 高並發情況下效能優於 Apache **2.5 倍**
- 記憶體占用比 Apache 少約 **40%**
- 可穩定承受大流量衝擊
**適用場景**:
- 高流量網站
- 反向代理與負載均衡
- 靜態檔案伺服
- API Gateway
### 為什麼上課選擇 Nginx?
1. **產業主流**:自 2019 年超越 Apache 後持續成長,是現代網站架構的首選
2. **效能卓越**:解決了傳統伺服器的並行處理瓶頸
3. **資源友善**:記憶體占用少、穩定性高
4. **應用廣泛**:不只是網頁伺服器,更常用於反向代理、負載均衡等進階架構
5. **學習價值**:理解事件驅動架構,對未來系統設計很有幫助
---
#### 安裝 Nginx
```bash
# 更新套件資訊
sudo apt update
# 安裝 Nginx
sudo apt install nginx
# 檢查服務狀態
sudo systemctl status nginx
# 或使用更現代的 ss 指令
sudo ss -tulnp | grep nginx
```
#### Web Server 服務操作指令
在實際操作 Web Server 時,有多種指令格式可以使用。了解這些指令的差異很重要。
```bash
# 方式 1:使用 service 指令
sudo service <service_name> <操作>
# 方式 2:使用 systemctl 指令(推薦)
sudo systemctl <操作> <service_name>
```
**說明**:
- `service` 是較舊的指令,但仍然常用且簡潔
- `systemctl` 是 systemd 系統的標準指令,功能更完整(推薦使用)
#### 常用操作指令
- **`status`**:查看服務狀態
```bash
sudo systemctl status nginx
```
- **`start`**:開啟服務
```bash
sudo systemctl start nginx
```
- **`stop`**:關閉服務
```bash
sudo systemctl stop nginx
```
- **`reload`**:不停止服務,重載設定檔,使更改生效
```bash
sudo systemctl reload nginx
```
**重點**:`reload` 不會中斷現有服務,適合在生產環境更新設定
- **`restart`**:停止服務後重新開啟
```bash
sudo systemctl restart nginx
```
**重點**:`restart` 會中斷現有服務,但確保設定完全重新載入
### reload vs restart 的差異
| 操作 | 中斷服務 | 適用場景 |
|------|----------|----------|
| **reload** | 否 | 修改設定檔後平滑更新 |
| **restart** | 是 | 重大更新、安裝新模組 |
**實務建議**:除非必要,優先使用 `reload` 來更新設定,避免影響使用者。
#### systemctl 額外功能
`systemctl` 提供比 `service` 更多的操作:
```bash
# 檢查服務是否失敗
sudo systemctl is-failed nginx
# 如果可以就 reload,否則 restart
sudo systemctl reload-or-restart nginx
# 啟用服務(開機自動啟動)
sudo systemctl enable nginx
# 停用服務(開機不自動啟動)
sudo systemctl disable nginx
```
### Nginx 設定檔結構
Nginx 的設定檔主要集中在 `/etc/nginx/` 目錄下,方便管理並避免系統更新時覆蓋使用者自定義設定:
```text!
/etc/nginx/
├── nginx.conf # 主設定檔
├── mime.types # MIME 類型對應檔
├── fastcgi_params # FastCGI 參數設定
├── uwsgi_params # uWSGI 參數設定
├── scgi_params # SCGI 參數設定
├── proxy_params # 代理伺服器參數設定
├── sites-available/ # 可用站點設定檔
│ ├── default # 預設站點設定
│ └── example.com.conf # 其他站點設定檔
├── sites-enabled/ # 已啟用站點(軟連結 symlink)
│ └── default -> ../sites-available/default
├── conf.d/ # 額外設定檔(自動載入 *.conf)
│ └── custom.conf # 自訂設定檔
├── modules-available/ # 可用模組
├── modules-enabled/ # 已啟用模組(軟連結)
└── snippets/ # 可重複使用的設定片段
├── fastcgi-php.conf
└── snakeoil.conf
```
- **`nginx.conf`**:主設定檔,包含 Nginx 的全域設定,例如 `worker_processes` (工作程序數量)、Log 路徑等。一般情況下不建議直接修改主設定檔,以免影響系統更新。
- **`sites-available`**:所有可用的 Web Server設定檔的集中地,方便管理。這些檔案本身不會生效。
- **`sites-enabled`**:實際讓服務運作的設定檔。此目錄中的檔案是從 `sites-available` 目錄中的檔案軟連結過來的。當 Nginx 啟動或重載時,會載入此目錄中的設定檔。
### Nginx 主設定檔
#### nginx.conf 全域設定
```nginx
# 工作程序數量,auto 會根據 CPU 核心數自動設定
worker_processes auto;
# 錯誤 Log 位置和等級
error_log /var/log/nginx/error.log warn;
# PID 檔案位置
pid /run/nginx.pid;
events {
# 每個 worker 程序可處理的最大連線數
worker_connections 1024;
}
http {
# 包含 MIME 類型定義
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 存取日誌格式和位置
access_log /var/log/nginx/access.log;
# 包含其他設定檔
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
```
**重點說明**:
- `worker_processes auto`:讓 Nginx 自動根據 CPU 核心數調整工作程序數量,是最佳實踐
- 日誌檔案對於除錯和監控非常重要
#### sites-available 默認設定檔
`/etc/nginx/sites-available`
在 Nginx 的 `server` 區塊中,包含以下重要參數:
- **`listen`**:伺服器監聽的 Port
```nginx
listen 80; # IPv4
listen [::]:80; # IPv6
```
- **`default_server`**:當沒有其他 `server_name` 符合時,會連到此 `server` 區塊。每個 Port 只能有一個 `default_server`。
```nginx
listen 80 default_server;
```
- **`root`**:設定網站的根目錄,Nginx 會從這裡提供檔案給存取者
```nginx
root /var/www/html;
```
- **`index`**:設定預設首頁檔案的名稱
```nginx
index index.html index.htm index.nginx-debian.html;
```
當使用者存取目錄時,Nginx 會按順序查找並載入第一個名稱成功配對到的檔案,如果沒有,就回傳 404。
- **`server_name`**:根據客戶端請求的 `Host` 標頭 (Domain Name) 選擇正確的 `server` 區塊
```nginx
server_name example.com www.example.com;
server_name _; # 底線表示預設伺服器
```
- **`location`**:定義對特定路徑或 URL 模式的操作
#### try_files 指令詳解
`try_files` 是 Nginx 中非常重要的指令,它定義了處理請求的優先順序。
```nginx
location / {
try_files $uri $uri/ =404;
}
```
**處理流程**(以請求 `/example` 為例):
```mermaid
flowchart TD
A[請求 /example] --> B{檢查檔案<br/>$uri}
B -->|存在| C[提供 /example 檔案]
B -->|不存在| D{檢查目錄<br/>$uri/}
D -->|存在| E[進入 /example/ 目錄<br/>尋找 index 檔案]
D -->|不存在| F[回傳 404 錯誤]
```
**步驟說明**:
1. **檢查檔案 (`$uri`)**:先檢查 `root` 目錄下是否存在名為 `example` 的檔案。若存在,則提供該檔案。
2. **檢查目錄 (`$uri/`)**:若 `example` 檔案不存在,則檢查是否有名為 `example` 的目錄 (即 `/example/`)。若存在,則進入此目錄,並依照 `index` 欄位查找指定的檔案。
3. **回傳 404**:如果 `example` 既不是檔案也不是目錄,Nginx 會回傳 `404 Not Found` 錯誤。
#### location 區塊範例
```nginx
# 基本路徑匹配
location / {
try_files $uri $uri/ =404;
}
# 禁止存取隱藏檔案
location ~ /\. {
deny all;
}
# 靜態檔案快取
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
```
### 基本配置範例
```nginx
# /etc/nginx/sites-available/default
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
```
### 更改 Port 範例
如果需要讓 Nginx 監聽不同的 Port(例如 8080):
```bash
# 1. 停止 Nginx
sudo systemctl stop nginx
# 2. 編輯設定檔
sudo vim /etc/nginx/sites-available/default
```
修改 `listen` 參數:
```nginx
server {
listen 8080 default_server;
listen [::]:8080 default_server;
# ... 其他設定
}
```
```bash
# 3. 測試設定檔語法
sudo nginx -t
# 4. 啟動 Nginx
sudo systemctl start nginx
# 5. 驗證 Port
sudo ss -tulpn | grep nginx
```
---
## 虛擬主機 (Virtual Host)
虛擬主機允許一台伺服器提供多個網站或網頁應用程式,且每個網站可以有自己的 Domain Name 和網頁內容。這樣可以最大化利用伺服器資源,節省硬體成本。伺服器會依據使用者的請求資訊(如 Domain Name, IP, Port)來提供不同的內容。
### 虛擬主機的三種類型
#### 1. 基於域名的虛擬主機 (Name-Based Virtual Host)
使用不同的 Domain Name 來區分不同的網站。
**情境**:多個網站共享同一個 IP 地址,伺服器根據請求中的 Domain Name (Host header) 來分配請求。
**範例**:`example1.com` 和 `example2.com` 都執行在同一台伺服器的相同 IP 上。
```mermaid
flowchart TB
AA[Client 客戶端<br/>瀏覽器輸入網址] --> |HTTP Request<br/>包含 Host header| A[Web Server<br/>Apache/Nginx<br/>Port 80/443]
A --> VH{Virtual Host<br/>判斷機制<br/>讀取 Host header}
VH -->|Host: site-a.com| B[內部轉發<br/>192.168.1.100:80]
VH -->|Host: site-b.com| C[內部轉發<br/>192.168.1.101:80]
B --> D[網站 A<br/>回傳內容]
C --> E[網站 B<br/>回傳內容]
D --> AA
E --> AA
```
**優點**:
- 節省 IP 位址
- 設定簡單
- 最常用的方式
**缺點**:
- 需要註冊 Domain Name
#### 2. 基於 IP 的虛擬主機 (IP-Based Virtual Host)
使用不同的 IP 地址來區分網站。
**情境**:每個網站分配一個專屬的 IP 地址。在一台主機上,可以將多個 IP 位址綁定在同一張網卡上,並針對不同 IP 監聽。
```mermaid
flowchart LR
AA[Client] -->|連接到 192.168.1.100:80| A[Nginx 伺服器]
AA -->|連接到 192.168.1.101:80| A
A -->|listen 192.168.1.100:80| B[192.168.1.100]
A -->|listen 192.168.1.101:80| C[192.168.1.101]
B --> D[網站 A<br/>/var/www/site1]
C --> E[網站 B<br/>/var/www/site2]
```
**設定範例**:
```nginx
# 監聽第一個 IP
server {
listen 192.168.1.100:80;
server_name _;
root /var/www/site1;
index index.html index.htm;
}
# 監聽第二個 IP
server {
listen 192.168.1.101:80;
server_name _;
root /var/www/site2;
index index.html index.htm;
}
```
**優點**:
- 完全獨立的 IP
- 不依賴 Domain Name
**缺點**:
- 消耗 IP 位址資源
- IPv4 位址日益稀缺
- 成本較高
**適用場景**:
- 需要獨立 IP 的企業網站
- 舊版不支援 SNI 的 HTTPS 網站(例如 Windows XP 上的 IE、Android 2.3 以下)
- 特殊安全需求
- 法規遵循要求(Compliance Requirements)
- 金融業 PCI DSS
- 醫療業 HIPAA
#### 3. 基於 Port 的虛擬主機 (Port-Based Virtual Host)
利用 Port 來區別不同的網站。
**情境**:在不使用多個 Domain Name 或 IP 的情況下,運行多個內部應用或 API,或節省 Domain Name 註冊費用。
```mermaid
flowchart LR
A[Client] -->|連接 :80| B[Nginx 伺服器]
A -->|連接 :8080| B
A -->|連接 :8081| B
B -->|listen 80| E[網站 A<br/>/var/www/site1]
B -->|listen 8080| F[網站 B<br/>/var/www/site2]
B -->|listen 8081| G[網站 C<br/>/var/www/site3]
```
**設定範例**:
```nginx
# Port 80
server {
listen 80;
server_name localhost;
root /var/www/site1;
}
# Port 8080
server {
listen 8080;
server_name localhost;
root /var/www/site2;
}
# Port 8081
server {
listen 8081;
server_name localhost;
root /var/www/site3;
}
```
**優點**:
- 不需要額外的 IP 或 Domain Name
- 節省成本
- 配置簡單
- 適合內部測試或開發環境
**缺點**:
- 使用者需要在瀏覽器中輸入 Port 號(例如 http://example.com:8080),這可能會讓非技術用戶感到困惑
- 非標準 Port 可能被防火牆封鎖,每個開放的 Port 都會增加攻擊面
- 對外服務的使用者體驗較差
**適用場景**:
- 開發和測試環境
- 開發者可以在 localhost:8080 執行測試網站,在 localhost:80 執行正式網站,全部使用同一個 Nginx 伺服器
- 內部應用系統
- API 服務
- 預算有限的小型專案
### Name-Based Virtual Host 實作範例
這個實驗演示如何讓一個 Nginx 伺服器同時為多個 Domain Name 提供服務。
#### 步驟 1:停止 Nginx 服務
```bash
sudo systemctl stop nginx
```
#### 步驟 2:建立新的網站根目錄
```bash
sudo mkdir -p /var/www/example.com
sudo chown -R $USER:$USER /var/www/example.com
sudo chmod -R 755 /var/www
```
`index.html` 內容範例:
```html
<!DOCTYPE html>
<html>
<head>
<title>Example.com</title>
</head>
<body>
<h1>歡迎來到 example.com</h1>
<p>這是基於域名的虛擬主機範例</p>
</body>
</html>
```
#### 步驟 3:新增虛擬主機設定檔
建議複製一份預設設定檔作為基礎,然後進行修改。
```bash
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com
sudo vim /etc/nginx/sites-available/example.com
```
修改內容:
```nginx
server {
listen 80; # 監聽 IPv4 的 80 Port
listen [::]:80; # 監聽 IPv6 的 80 Port
server_name example.com www.example.com; # 設定 Domain Name
root /var/www/example.com; # 網站根目錄
index index.html; # 預設首頁檔案
location / {
try_files $uri $uri/ =404; # 嘗試尋找檔案,找不到回傳 404
}
}
```
:::info
完整的 example.com
```nginx
##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##
# Default server configuration
#
server {
listen 80 ;
listen [::]:80 ;
# SSL configuration
#
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;
root /var/www/example.com www.example.com;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name example.com;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
# pass PHP scripts to FastCGI server
#
#location ~ \.php$ {
# include snippets/fastcgi-php.conf;
#
# # With php-fpm (or other unix sockets):
# fastcgi_pass unix:/run/php/php7.4-fpm.sock;
# # With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
# listen 80;
# listen [::]:80;
#
# server_name example.com;
#
# root /var/www/example.com;
# index index.html;
#
# location / {
# try_files $uri $uri/ =404;
# }
#}
```
:::
#### 步驟 4:建立軟連結啟用設定檔
```bash
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
```
#### 步驟 5:新增主機紀錄到 /etc/hosts
為了讓本機電腦能夠解析 `example.com` 到 `127.0.0.1`:
```bash
sudo vim /etc/hosts
```
新增一行:
```
127.0.0.1 example.com www.example.com
```
#### 步驟 6:測試 Nginx 設定檔語法
```bash
sudo nginx -t
```
如果沒有問題,會顯示:
```
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
```
#### 步驟 7:重啟 Nginx 服務
```bash
sudo systemctl restart nginx
```
#### 步驟 8:測試
**方法 1:使用瀏覽器**
存取 `http://example.com`
**方法 2:使用 curl**
```bash
curl http://example.com
```
---
## 代理 (Proxy) 與負載平衡 (Load Balance)
### 正向代理 (Forward Proxy)
**目的**:位於客戶端和伺服器之間,客戶端透過代理伺服器向外部伺服器發送請求。
```mermaid
flowchart LR
A[Client] --> B[Forward Proxy]
B --> C[Internet]
C --> D[目標伺服器]
```
**功能**:
1. **隱藏客戶端身份**:保護客戶端隱私,外部伺服器只知道代理伺服器的 IP
2. **突破網路限制**:幫助客戶端存取原本受限制的網站
3. **流量節省與快取**:代理伺服器可以快取經常存取的內容,減少對外網的頻寬使用
4. **網路存取控制**:機構可以限制內部成員對特定網站的存取
**特性**:伺服器只知道代理發出的請求,不知道實際的客戶端是誰。
### 反向代理 (Reverse Proxy)
**目的**:位於伺服器之前,代表後端伺服器接收客戶端的請求。
```mermaid
flowchart LR
A[Client] --> B[Reverse Proxy<br/>Nginx]
B --> C[後端伺服器 1]
B --> D[後端伺服器 2]
B --> E[後端伺服器 3]
```
**功能**:
1. **隱藏後端伺服器**:客戶端不知道真正處理請求的伺服器是哪一台,只與反向代理互動
2. **負載平衡**:將客戶端請求分發到多個後端伺服器,提高資源使用率和可用性
3. **安全性強化**:作為額外的防護層,隱藏內部網路結構
4. **SSL 加密**:可以在代理層處理 SSL 加密/解密,減輕後端伺服器的負擔
5. **快取**:代理伺服器本身可以快取內容,減少對後端伺服器的請求
6. **靈活性**:當後端伺服器的 IP 位址或 Port 更改時,只需向代理伺服器更改設定,無需通知客戶端
**特性**:客戶端只知道反向代理,不知道實際提供服務的後端伺服器。
### 負載平衡 (Load Balance)
負載平衡是反向代理的一個重要功能,旨在將流量合理分配到多個後端伺服器,以提升各伺服器的資源使用率及可用性。這可以有效避免流量集中在同一台伺服器造成超載,導致服務無法正常運行,同時也避免伺服器閒置造成的資源浪費。
```mermaid
flowchart TD
A[Client 請求] --> B[Nginx<br/>負載平衡器]
B -->|分配| C[伺服器 1]
B -->|分配| D[伺服器 2]
B -->|分配| E[伺服器 3]
```
:::info
#### 負載平衡演算法
Nginx 支援多種負載平衡演算法:
1. **Round Robin (輪流)**
- 預設方式
- 依序將請求分配給各伺服器
- 適合後端伺服器性能相近的情況
2. **Least Connections (最少連線)**
- 將請求分配給當前連線數最少的伺服器
- 適合請求處理時間差異大的情況
```nginx
upstream backend {
least_conn;
server backend1.example.com;
server backend2.example.com;
}
```
3. **IP Hash**
- 根據客戶端 IP 進行 hash,同一 IP 會被分配到同一台伺服器
- 適合需要會話保持 (Session Persistence) 的應用
```nginx
upstream backend {
ip_hash;
server backend1.example.com;
server backend2.example.com;
}
```
##### 為什麼需要 IP Hash?
當應用程式需要「會話保持」(Session Persistence)時,必須確保同一個使用者的請求都被分配到同一台伺服器,否則:
- 使用者的登入狀態可能會丟失
- 購物車內容可能會消失
- 檔案上傳可能會失敗
4. **Weight (權重)**
- 為伺服器分配不同的權重
- 性能好的伺服器可以分配更多請求
```nginx
upstream backend {
server backend1.example.com weight=3;
server backend2.example.com weight=1;
}
```
:::
### Nginx 反向代理實作範例
假設我們有兩個後端服務:
- 服務 A 運行在 `127.0.0.1:3000`
- 服務 B 運行在 `127.0.0.1:3001`
我們使用 Nginx 作為反向代理,讓外部只需要存取 Port 80。
#### 步驟 1:建立反向代理設定檔
```bash
sudo vim /etc/nginx/sites-available/reverse-proxy
```
內容:
```nginx
server {
listen 80;
server_name proxy.lsa.moli;
# 代理到服務 A
location /service-a/ {
proxy_pass http://${s110213027}.lsa.moli;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 代理到服務 B
location /service-b/ {
proxy_pass http://regchien.lsa.moli;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
**重要參數說明**:
- `proxy_pass`:指定要轉發到的後端伺服器位址
- `proxy_set_header Host $host`:保留原始請求的 Host 標頭
- `proxy_set_header X-Real-IP $remote_addr`:傳遞客戶端真實 IP
- `proxy_set_header X-Forwarded-For`:記錄代理鏈中的所有 IP
- `proxy_set_header X-Forwarded-Proto`:告知後端使用的協定(http/https)
#### 步驟 2:啟用設定檔
```bash
sudo ln -s /etc/nginx/sites-available/reverse-proxy /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
#### 步驟 3:測試
存取以下網址:
- `http://proxy.example.com/service-a/`
- `http://proxy.example.com/service-b/`
#### Nginx 負載平衡實作範例
```bash
sudo vim /etc/nginx/sites-available/load-balancer
```
內容:
```nginx
# 定義後端伺服器群組
upstream backend_servers {
# 預設使用 Round Robin
server example.com;
server example01.com;
# 或使用其他演算法:
# least_conn; # 最少連線
# ip_hash; # IP Hash
}
server {
listen 80;
server_name lb.example.com;
location / {
proxy_pass http://backend_servers;
#proxy_set_header Host $host;
#proxy_set_header X-Real-IP $remote_addr;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
:::info
**進階功能**:
Backup
```nginx
upstream backend_servers {
# 健康檢查設定
# max_fails=3: 連續失敗 3 次後標記為不可用
# fail_timeout=30s: 標記為不可用後,30 秒內不會再嘗試連線
server example.com max_fails=3 fail_timeout=30s;
server example01.com max_fails=3 fail_timeout=30s;
# 備用伺服器:只有所有主要伺服器都失敗時才使用
server example02.com backup;
}
```
:::
---
## 實用工具與除錯技巧
:::info
### Nginx Log 存放位置 (Ubuntu 24.04 - apt 安裝)
**預設 Log 目錄**:
- 主要 Log 目錄: `/var/log/nginx/`
- Access Log (存取記錄): `/var/log/nginx/access.log`
- Error Log (錯誤記錄): `/var/log/nginx/error.log`
**查看 Log 的常用指令**:
```bash
# 即時監看 access log
sudo tail -f /var/log/nginx/access.log
# 即時監看 error log
sudo tail -f /var/log/nginx/error.log
# 查看最後 50 行 error log
sudo tail -n 50 /var/log/nginx/error.log
# 搜尋特定錯誤 (例如 502 錯誤)
sudo grep "502" /var/log/nginx/error.log
# 統計不同 HTTP 狀態碼的數量
sudo awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 查看最常訪問的 IP
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
```
**自訂 Log 位置** (在 Nginx 設定檔中):
```nginx
server {
listen 80;
server_name example.com;
# 自訂 access log 位置
access_log /var/log/nginx/example.com-access.log;
# 自訂 error log 位置與記錄等級
error_log /var/log/nginx/example.com-error.log warn;
# 關閉 access log (不建議在正式環境使用)
# access_log off;
}
```
**Log 記錄等級** (由低到高):
- `debug`: 除錯訊息 (最詳細)
- `info`: 資訊訊息
- `notice`: 一般通知
- `warn`: 警告訊息
- `error`: 錯誤訊息 (預設)
- `crit`: 嚴重錯誤
- `alert`: 需立即處理的問題
- `emerg`: 系統無法使用
**Log 輪替設定**:
- Log 輪替設定檔: `/etc/logrotate.d/nginx`
- 預設會自動壓縮與輪替舊的 log 檔案
- 避免 log 檔案過大佔用磁碟空間
```bash
# 查看 log 輪替設定
cat /etc/logrotate.d/nginx
# 手動執行 log 輪替 (測試用)
sudo logrotate -f /etc/logrotate.d/nginx
```
:::
### w3m - 文字界面瀏覽器
`w3m` 是一個基於終端機的網頁瀏覽工具,特別適合在 SSH 遠端環境中測試網頁。
#### 安裝
```bash
sudo apt install w3m
```
#### 使用方法
```bash
# 瀏覽本地網頁
w3m http://localhost
# 瀏覽遠端網頁
w3m https://www.google.com
# 查看本地 HTML 檔案
w3m /var/www/html/index.html
```
#### 完整快捷鍵列表
**基本導航**
- `q`:退出
- `Q`:不詢問直接退出
- `B`:返回上一頁
- `U`:輸入新網址
- `Tab`:跳到下一個連結
- `Shift+Tab`:跳到上一個連結
- `Enter`:開啟連結
- `ESC+Tab`:在新分頁開啟連結
**頁面操作**
- `Space` 或 `PageDown`:向下捲動一頁
- `b` 或 `PageUp`:向上捲動一頁
- `<` 或 `>`:左右捲動
- `/`:搜尋
- `n`:搜尋下一個
- `N`:搜尋上一個
**分頁管理**
- `T`:開啟新分頁
- `}`:切換到下一個分頁
- `{`:切換到上一個分頁
- `ESC+t`:顯示所有分頁列表
**檢視選項**
- `H`:顯示說明文件
- `o`:開啟選項設定面板
- `V`:顯示 HTML 原始碼
- `s`:切換搜尋模式
- `R`:重新載入頁面
**其他功能**
- `ESC+M`:開啟外部瀏覽器
- `ESC+e`:編輯目前網址
- `ESC+s`:儲存網頁
- `c`:顯示目前頁面的 URL
#### 實用場景
```bash
# 快速檢查 Nginx 是否正常運作
w3m http://localhost
# 測試虛擬主機設定
w3m http://example.com
# 檢查反向代理是否生效
w3m http://proxy.example.com/service-a/
```
#### Nginx 快取控制
在 Nginx 設定中控制快取:
```nginx
location ~* \.(jpg|jpeg|png|gif|ico)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
location ~* \.(css|js)$ {
expires 7d;
add_header Cache-Control "public";
}
location / {
# 禁用快取(適合開發環境)
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
```