Nginx 是什麼意思:能做什麼、和 Apache 怎麼選
Nginx(讀作 engine x)是一款開源的 Web 伺服器,同時承擔反向代理、負載均衡、HTTPS 終結和快取。它通常放在網站最前面:靜態檔案自己返回,動態請求轉給後端程式。事件驅動的架構讓它用很少的程序維持大量連線,高併發下比傳統的 Apache 省記憶體。
Nginx 是什麼意思?Nginx 讀作“engine x”,是一款開源的 Web 伺服器,同時也是反向代理、負載均衡器和 HTTP 快取。它由 Igor Sysoev 開發,2004 年公開發布,最初就是為了解決單台伺服器同時處理上萬個連線的問題。今天它最常見的位置是網站的最前面:圖片、CSS、JS 這些靜態檔案由它直接返回,PHP、Java、Node.js、Python 寫的動態請求由它轉給後端程式,HTTPS 證書也在它這裡處理。與 Apache 相比,它用事件驅動模型處理連線,少數幾個程序就能維持成千上萬個連線,所以高併發場景下更省記憶體。
Nginx 能做什麼:五種常見用法
| 用法 | 做什麼 | 關鍵配置指令 |
|---|---|---|
| 靜態網站與檔案服務 | 直接返回 HTML、圖片、CSS、JS 和下載檔案 | root、index、try_files、expires |
| 反向代理 | 接收客戶端請求,轉給後端程式,客戶端看不到後端位址 | proxy_pass、proxy_set_header |
| 負載均衡 | 把請求分給多台後端,連續失敗的後端暫時摘除 | upstream、least_conn、max_fails |
| HTTPS 終結 | 在 Nginx 上完成 TLS 握手,後端只跑普通 HTTP | ssl_certificate、ssl_protocols |
| 快取 | 把後端的回應存到本地,重複請求不再打到後端 | proxy_cache_path、proxy_cache |
此外還能做 TCP/UDP 四層轉發(stream 模組,例如轉發 MySQL 的 3306 連接埠)、gzip 壓縮和按 IP 限流。
下面一份配置把五種用法放在一起:一個商城站點,靜態檔案由 Nginx 返回,介面轉給 3 台應用伺服器,商品列表介面快取 10 分鐘。放在 /etc/nginx/conf.d/shop.conf,這個目錄裡的檔案會被包含進主配置的 http 塊:
# 快取目錄:1 MB 的 keys_zone 大約能存 8000 個快取鍵
proxy_cache_path /var/cache/nginx/shop levels=1:2 keys_zone=shop_cache:50m
max_size=2g inactive=60m use_temp_path=off;
# 負載均衡:請求優先給當前連線數最少的後端,30 秒內失敗 3 次就暫停使用 30 秒
upstream shop_app {
least_conn;
server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.13:8080 backup; # 前兩台都不可用時才啟用
}
# HTTP 統一跳轉到 HTTPS
server {
listen 80;
server_name shop.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on; # 1.25.1 起的寫法,舊版本改為 listen 443 ssl http2;
server_name shop.example.com;
ssl_certificate /etc/nginx/ssl/shop.example.com.fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/shop.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
# 靜態檔案:由 Nginx 直接讀磁碟返回,瀏覽器快取 7 天
location /static/ {
root /var/www/shop; # 實際讀取 /var/www/shop/static/...
expires 7d;
}
# 商品列表介面:快取 10 分鐘,後端出錯時先返回舊快取
location /api/products/ {
proxy_cache shop_cache;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://shop_app;
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;
}
# 其餘請求:反向代理到應用伺服器
location / {
proxy_pass http://shop_app;
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_set_header 不能省。不傳 Host,後端拿到的是 upstream 名稱而不是域名;不傳 X-Forwarded-For 和 X-Real-IP,後端日誌裡所有訪客的 IP 都是 Nginx 自己;不傳 X-Forwarded-Proto,後端以為使用者走的是 HTTP,可能反覆跳轉到 HTTPS。
- 開源版只有被動健康檢查。
max_fails和fail_timeout靠真實請求失敗來判斷後端是否可用;定時主動探測後端屬於商業版 NGINX Plus 的功能,開源版需要第三方模組或在後端前面另加檢測。 - 驗證快取是否生效。連續請求兩次
curl -sI https://shop.example.com/api/products/ | grep -i x-cache-status,第一次是 MISS,第二次是 HIT(走 HTTP/2 時回應頭名稱是小寫,所以 grep 要加 -i)。如果一直是 MISS,先看後端回應是否帶了 Set-Cookie 或禁止快取的 Cache-Control,預設情況下 Nginx 不快取這類回應。 - PHP 程式走 FastCGI。Nginx 不執行 PHP,要把
.php請求交給 PHP-FPM:在location ~ \.php$裡寫include fastcgi.conf;和fastcgi_pass unix:/run/php/php8.3-fpm.sock;。注意fastcgi.conf比fastcgi_params多一行 SCRIPT_FILENAME,只 include 後者又沒補這一行,PHP-FPM 會找不到指令碼;Debian、Ubuntu 上也可以直接用include snippets/fastcgi-php.conf;。socket 路徑以伺服器上實際安裝的 PHP 版本為準。
為什麼 Nginx 在高併發下更省資源
Nginx 啟動後是一個 master 程序加若干 worker 程序。master 只負責讀配置、管理 worker;真正處理請求的是 worker,數量通常設為 worker_processes auto;,即與 CPU 核數相同。每個 worker 是單執行緒的事件迴圈:它把幾千個連線都交給核心的 epoll(BSD 上是 kqueue)監視,哪個連線有資料可讀、可寫,核心就通知它去處理哪個,處理完立刻轉向下一個,從不停下來等某一個慢客戶端。
傳統的 Apache prefork 模式則是一個連線佔一個程序。使用者的瀏覽器開著 keep-alive 什麼也不發,這個程序也只能乾等。兩種模型面對 1 萬個併發連線時的差別:
| 對比 | Nginx | Apache prefork |
|---|---|---|
| 處理 1 萬個連線需要 | 4 核伺服器上 4 個 worker,每個維持約 2500 個連線(worker_connections 要相應調高) | 理論上 1 萬個程序;預設 MaxRequestWorkers 只有 256,超出的連線排隊等待 |
| 空閒 keep-alive 連線的開銷 | 很小:Nginx 官網的說法是 1 萬個空閒 keep-alive 連線約佔 2.5 MB 記憶體 | 每個連線佔一個完整程序,載入了 mod_php 時每個程序常有幾十 MB |
| 慢客戶端的影響 | 只佔一個連線的少量記憶體 | 長時間佔住一個程序 |
Nginx 能維持的連線上限大約是 worker_processes × worker_connections。發行版自帶的配置裡 worker_connections 常見 768 或 1024,做反向代理時每個請求要佔用客戶端和後端兩個連線,實際能服務的併發約為上限的一半;要調高時,同時用 worker_rlimit_nofile 提高每個程序能開啟的檔案數。
事件驅動也有代價:如果某個操作阻塞了 worker(例如在很慢的磁碟上讀大檔案,或者第三方模組裡有同步呼叫),這個 worker 上的所有連線都會跟著卡住。Nginx 為此提供了執行緒池(aio threads),把讀磁碟這類阻塞操作交給後台執行緒。另外,Apache 2.4 的 event 模式也把空閒的 keep-alive 連線交給專門的監聽執行緒管理,差距已經比 prefork 時代小,但每個正在處理的請求仍然要佔用一個執行緒。
在伺服器上可以直接看到這兩種結構:
ps -o pid,ppid,user,rss,cmd -C nginx # 1 個 master(root)加若干 worker,RSS 單位是 KB
grep -E 'worker_processes|worker_connections' /etc/nginx/nginx.conf
nproc # CPU 核數,worker_processes auto 時與它相同
apachectl -V | grep -i mpm # Apache 當前用的 MPM:prefork、worker 或 event
Nginx 和 Apache 的區別
| 對比項 | Nginx | Apache httpd |
|---|---|---|
| 連線處理模型 | 事件驅動,少數 worker 程序各自處理大量連線 | 由 MPM 決定:prefork 一連線一程序,worker 和 event 為多程序多執行緒 |
| 靜態檔案、高併發 | 記憶體佔用低,適合做前端入口 | 並發上去後程序或執行緒數隨之增長 |
| 執行 PHP | 交給 PHP-FPM | 可以用 mod_php 內嵌執行,也可以接 PHP-FPM |
| 目錄級配置 | 不支援 .htaccess,規則寫在主配置裡,改完要 reload | 支援 .htaccess,網站目錄裡放檔案即可生效 |
| 偽靜態規則 | rewrite、try_files |
mod_rewrite,很多 PHP 程式自帶 .htaccess 規則 |
| 模組 | 多數編譯程序序;動態模組要和 Nginx 版本匹配 | 模組動態載入,Debian、Ubuntu 上用 a2enmod 一條命令啟用 |
| 反向代理與負載均衡 | 核心功能,配置簡潔 | 用 mod_proxy、mod_proxy_balancer 實現 |
| 許可證 | BSD 類許可證 | Apache License 2.0 |
| 典型位置 | 網站入口、反向代理、靜態資源、負載均衡 | 共享虛擬主機、依賴 .htaccess 的老程式 |
兩者的分界其實就在兩點:要不要 .htaccess、併發有多高。共享虛擬主機讓每個使用者在自己目錄裡改規則,離不開 .htaccess,所以多用 Apache 或相容它的 LiteSpeed;自己有整台伺服器、能改主配置的,用 Nginx 的限制就不存在了。
還有一種常見的混合方案:Nginx 在前面處理 HTTPS、靜態檔案和連線,Apache 在後面跑依賴 .htaccess 的老程式。這時 Apache 看到的來源 IP 全是 Nginx 的位址,要啟用 mod_remoteip 並信任 Nginx 傳來的 X-Forwarded-For,日誌和程式裡才能拿到訪客的真實 IP。
租伺服器建站,什麼情況選 Nginx
| 場景 | 選擇 | 理由 |
|---|---|---|
| 1–2 GB 記憶體的雲伺服器或 VPS 跑 WordPress 等 PHP 網站 | Nginx 加 PHP-FPM | 靜態檔案和空閒連線不佔 PHP 程序,記憶體更夠用 |
| Node.js、Java、Python、Go 寫的應用 | Nginx 做反向代理 | 應用只監聽 127.0.0.1,由 Nginx 對外處理 80、443 和證書 |
| 多台後端伺服器 | Nginx 的 upstream | 負載均衡和故障摘除開箱即用 |
| 圖片站、下載站、純靜態網站 | Nginx | 靜態檔案吞吐高,配置簡單 |
| 程式大量依賴 .htaccess,且不想改寫規則 | Apache,或前 Nginx 後 Apache | 免去把規則逐條翻譯成 Nginx 寫法 |
| 用寶塔等面板建站 | 兩者都可以 | 面板裡切換,按上面的規則選 |
小記憶體伺服器上,Nginx 的優勢可以算出來。假設一台 2 GB 記憶體的伺服器,系統和 MySQL 佔去約 1 GB,剩下約 1000 MB 給 PHP,用下面的命令實測每個 PHP 程序平均佔 40 MB:
ps -o rss= -C php-fpm8.3 | awk '{s+=$1; n++} END {printf "%d 個程序,平均 %.1f MB\n", n, s/n/1024}'
那麼 PHP 最多開 1000 ÷ 40 = 25 個程序,PHP-FPM 的 pm.max_children 就設為 25。區別在於這 25 個程序被誰佔用:用 Apache prefork 加 mod_php 時,每個連線都佔一個帶 PHP 的程序,包括下載一張圖片、甚至什麼都不發的 keep-alive 連線,一個頁面帶 30 個靜態資源,幾個訪客同時開啟就可能把 25 個程序佔滿,後來的人只能排隊;用 Nginx 時,靜態檔案和空閒連線由 Nginx 處理,25 個 PHP-FPM 程序只服務真正需要執行 PHP 的請求。
裝好 Nginx 後,記得放行 80 和 443 連接埠,Ubuntu 上用 ufw:
sudo ufw allow 22/tcp # 先放行 SSH,否則啟用防火牆後可能再也連不上
sudo ufw allow 80,443/tcp
sudo ufw enable
sudo ufw status
第一行最容易忘:啟用 ufw 前沒有放行 22 連接埠,當前會話可能還連著,退出後就再也登入不上 SSH。真遇到這種情況,用服務商控制面板裡的 VNC 控制檯登入修改,例如基於 Toplink DCIM 的 客戶自助端 就能直接在瀏覽器裡開啟伺服器控制檯,不依賴 SSH。雲伺服器還要在安全組裡放行同樣的連接埠。
常用命令與配置檔案位置
| 專案 | Debian、Ubuntu 軟體源 | RHEL、Rocky、AlmaLinux 及 nginx.org 官方包 |
|---|---|---|
| 安裝 | apt install nginx |
dnf install nginx |
| 主配置 | /etc/nginx/nginx.conf | /etc/nginx/nginx.conf |
| 站點配置 | /etc/nginx/sites-available/,軟連結到 sites-enabled/ 生效;也讀取 conf.d/ | /etc/nginx/conf.d/*.conf |
| 預設網站目錄 | /var/www/html | /usr/share/nginx/html |
| 日誌 | /var/log/nginx/access.log、error.log | 同左 |
| worker 執行使用者 | www-data | nginx |
nginx -v # 版本號:偶數小版本(如 1.26)是 stable,奇數是 mainline
nginx -V 2>&1 | tr ' ' '\n' | grep -E '^--with' # 編譯了哪些模組
sudo nginx -t # 檢查配置語法,改完配置先執行它
sudo nginx -T | less # 列印合並後的完整配置,排查“改了不生效”
sudo systemctl reload nginx # 平滑載入新配置
sudo tail -f /var/log/nginx/error.log
改配置的固定順序是:編輯 → nginx -t → systemctl reload nginx → 看 error.log。改配置不需要 restart:reload 時 master 程序用新配置啟動新的 worker,舊 worker 處理完手頭的請求再退出,訪客不會斷開;新配置有錯時 reload 不會生效,線上仍按舊配置執行。跳過檢查直接 restart,配置一錯 Nginx 就起不來了。
常見問題
Nginx 和 Tomcat 有什麼區別?
Tomcat 是執行 Java Web 應用的容器,負責執行 Servlet 和 JSP;Nginx 不執行 Java 程式碼。常見搭配是 Nginx 在前面處理 HTTPS、靜態檔案和負載均衡,把動態請求用 proxy_pass 轉給後面的一台或多台 Tomcat。
Nginx 免費嗎?可以商用嗎?
開源版 Nginx 以 BSD 類許可證釋出,免費使用,包括商業用途。F5 另外提供商業版 NGINX Plus,增加主動健康檢查、動態配置介面和官方支援。國內常見的 OpenResty、Tengine 是基於 Nginx 的衍生版本。
Nginx 報 502 和 504 是什麼原因?
502 Bad Gateway 表示 Nginx 連不上後端,或後端返回了無效回應,常見於後端程式沒啟動、proxy_pass 或 fastcgi_pass 的位址寫錯、PHP-FPM 程序崩潰;504 Gateway Timeout 表示後端在超時時間內沒有回應,proxy_read_timeout 預設 60 秒。先看 error.log 裡帶 upstream 字樣的報錯,再去查後端。