Nginx 从入门到实战:一篇搞定所有常用场景
·
> 写在前面:Nginx 是全世界用得最多的 Web 服务器和反向代理。不管你是前端、后端还是运维,不会 Nginx 简直寸步难行。这篇文章从零开始,用大白话+实战配置,带你把 Nginx 彻底搞懂。
一、Nginx 是什么?用一句话说清楚
想象你开了一家餐厅:
- 没有 Nginx:顾客直接冲进后厨找厨师点菜,厨房乱成一锅粥
- 有了 Nginx:门口有个专业的服务员,负责接待、分流、记录订单,厨房只需要专心做菜
Nginx 就是那个「超级服务员」,它站在用户和你的应用服务器之间,帮你处理:
- 📋 请求分发:把顾客引导到对应的窗口
- 🛡️ 安全防护:不让坏人冲进后厨
- ⚡ 性能加速:把常用的菜提前备好,不用每次现做
- 🔒 加密通信:确保顾客的点菜信息不被偷听
二、Nginx 整体架构图
2.1 进程架构
┌─────────────────────────┐
│ Master 进程 │
│ (大管家:读配置、管工人) │
└────────────┬─────────────┘
│ 管理
┌──────────┬───────────┼───────────┬──────────┐
▼ ▼ ▼ ▼ ▼
┌──────────┐┌──────────┐┌──────────┐┌──────────┐┌──────────┐
│ Worker 1 ││ Worker 2 ││ Worker 3 ││ Worker 4 ││ Worker N │
│(干活工人) ││(干活工人) ││(干活工人) ││(干活工人) ││(干活工人) │
└──────────┘└──────────┘└──────────┘└──────────┘└──────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
══════════════════════════════════════════════════════════
所有 Worker 共享监听端口
(epoll/kqueue 事件驱动)
通俗解释:
- Master 进程:老板,负责读取配置文件、启动和管理 Worker 进程
- Worker 进程:打工人,实际处理用户的请求(通常数量 = CPU 核心数)
- 事件驱动:一个 Worker 能同时处理成千上万个连接(不用一个连接开一个线程)
2.2 请求处理流程
用户请求
│
▼
┌──────────────────────────────────────────────┐
│ Nginx 请求处理流水线 │
│ │
│ ┌─────────┐ ┌─────────┐ ┌──────────────┐ │
│ │ 接收请求 │→│ 匹配规则 │→│ 选择处理方式 │ │
│ └─────────┘ └─────────┘ └──────┬───────┘ │
│ │ │
│ ┌──────────────┼────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌──────┤
│ │ 静态文件 │ │ 反向代理 │ │缓存等│
│ │ 直接返回 │ │ 转发后端 │ │处理 │
│ └─────────┘ └─────────┘ └──────┘
│ │ │
│ ▼ │
│ ┌───────────┐ │
│ │ 返回响应 │ │
│ └───────────┘ │
└──────────────────────────────────────────────┘
2.3 目录结构(安装后)
/etc/nginx/
├── nginx.conf # 🏠 主配置文件(总入口)
├── conf.d/ # 📁 子配置目录(推荐方式)
│ ├── app1.conf # 各个站点独立配置
│ └── app2.conf
├── sites-available/ # 📁 可用站点(Debian/Ubuntu)
│ └── default
├── sites-enabled/ # 📁 已启用站点(软链接)
│ └── default -> ../sites-available/default
├── snippets/ # 📁 可复用配置片段
│ ├── ssl-params.conf # SSL 通用参数
│ └── proxy-params.conf # 代理通用参数
├── modules-available/ # 📁 可用模块
└── modules-enabled/ # 📁 已启用模块
/var/log/nginx/
├── access.log # 📝 访问日志
└── error.log # ❌ 错误日志
/var/www/html/ # 📂 默认网站根目录
三、配置文件结构:先看全貌
Nginx 的配置就像套娃,一层套一层:
# ===== 全局块(影响整个 Nginx)=====
user nginx; # Worker 进程以什么用户运行
worker_processes auto; # Worker 数量(auto = CPU 核心数)
error_log /var/log/nginx/error.log; # 错误日志位置
pid /var/run/nginx.pid; # 进程 ID 文件
# ===== 事件块(影响连接处理)=====
events {
worker_connections 1024; # 每个 Worker 最大连接数
# 总并发 = worker_processes × worker_connections
}
# ===== HTTP 块(HTTP 相关的所有配置)=====
http {
include mime.types; # 文件类型映射
default_type application/octet-stream;
# --- 日志格式 ---
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;
# --- 性能优化 ---
sendfile on; # 零拷贝,提升文件传输性能
tcp_nopush on; # 优化数据包发送
tcp_nodelay on; # 禁用 Nagle 算法
keepalive_timeout 65; # 长连接超时时间
gzip on; # 开启压缩
gzip_min_length 1024; # 超过 1KB 才压缩
# --- 各种 server(虚拟主机)---
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
# ===== STREAM 块(TCP/UDP 四层代理)=====
# stream { ... }
配置层级关系:
nginx.conf
├── main(全局)
├── events(事件)
├── http(HTTP 服务)
│ ├── upstream(负载均衡池)
│ ├── server(虚拟主机)
│ │ ├── listen(监听端口/域名)
│ │ ├── location(URL 匹配规则)
│ │ │ ├── 静态文件处理
│ │ │ ├── 反向代理
│ │ │ ├── FastCGI
│ │ │ └── 其他...
│ │ └── ...
│ └── ...
└── stream(TCP/UDP 代理)
四、location 匹配规则:最容易搞混的部分
这是 Nginx 配置的核心,很多人在这里踩坑。
4.1 匹配优先级(从高到低)
优先级从高到低:
① = 精确匹配 location = /api/login { } # URL 完全等于 /api/login
② ^~ 前缀优先 location ^~ /images/ { } # 以 /images/ 开头,匹配后不检查正则
③ ~ 正则(区分大小写) location ~ \.php$ { } # 以 .php 结尾(区分大小写)
④ ~* 正则(不区分) location ~* \.(jpg|png) { } # .jpg 或 .png 结尾(不区分大小写)
⑤ 普通前缀匹配 location /api/ { } # 以 /api/ 开头(最长匹配获胜)
4.2 实战示例
server {
listen 80;
server_name example.com;
# ① 精确匹配:只有 / 才命中
location = / {
root /var/www/homepage;
}
# ② 前缀优先:/static/ 开头的请求不走正则
location ^~ /static/ {
root /var/www/assets;
expires 30d; # 缓存 30 天
}
# ③ 正则匹配:所有图片请求
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
root /var/www/images;
expires 7d;
}
# ④ 普通前缀:API 请求转发到后端
location /api/ {
proxy_pass http://backend_server;
}
# ⑤ 兜底:其他所有请求
location / {
try_files $uri $uri/ /index.html; # SPA 前端路由兜底
}
}
4.3 location 匹配流程图
请求到来
│
▼
检查所有「精确匹配」(=)
│
├─ 命中 → 直接使用,结束
│
▼
检查所有「前缀匹配」(记住最长的那个)
│
├─ 最长匹配是 ^~ → 直接使用,结束
│
▼
检查所有「正则匹配」(~/~*)
│
├─ 命中 → 使用正则匹配的配置,结束
│
▼
使用之前记住的最长前缀匹配
(如果没有任何前缀匹配 → 返回 404)
五、场景一:静态文件服务器
最简单的用法——直接托管 HTML、CSS、JS、图片等文件。
5.1 基础配置
server {
listen 80;
server_name static.example.com;
# 网站根目录
root /var/www/static;
index index.html index.htm;
# 默认处理
location / {
try_files $uri $uri/ =404;
}
# 图片缓存
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# CSS/JS 缓存
location ~* \.(css|js)$ {
expires 7d;
add_header Cache-Control "public";
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
return 404;
}
}
5.2 带目录权限的文件服务器
server {
listen 80;
server_name files.example.com;
location /downloads/ {
alias /data/files/; # alias 和 root 的区别见下方说明
autoindex on; # 开启目录列表
autoindex_exact_size off; # 显示友好文件大小(KB/MB)
autoindex_localtime on; # 显示本地时间
# 限制下载速度
limit_rate 500k;
# 需要认证
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
}
# 💡 root vs alias 区别:
# location /images/ { root /var/www; } → 实际路径: /var/www/images/xxx.jpg
# location /images/ { alias /data/pics/; } → 实际路径: /data/pics/xxx.jpg
# root 会拼接 location 路径,alias 不会
六、场景二:反向代理(最常用!)
6.1 什么是反向代理?
正向代理(代理客户端):
你 → 代理服务器 → Google(Google 不知道是你访问的)
反向代理(代理服务端):
用户 → Nginx → 你的后端服务器(用户不知道后端是谁)
为什么需要反向代理?
- 🛡️ 隐藏后端服务器真实 IP
- ⚖️ 负载均衡,把请求分给多台机器
- 🔒 统一处理 SSL 加密
- 📊 统一记录日志
- 🚀 缓存静态资源
6.2 基础反向代理配置
# 定义后端服务器组
upstream backend_api {
# 方式一:简单轮询(默认)
server 127.0.0.1:3000;
# 方式二:权重分配
# server 192.168.1.101:3000 weight=3; # 60% 请求
# server 192.168.1.102:3000 weight=2; # 40% 请求
# 方式三:IP 哈希(同一用户总是打到同一台机器)
# ip_hash;
# server 192.168.1.101:3000;
# server 192.168.1.102:3000;
# 备用服务器(其他全挂了才启用)
# server 192.168.1.103:3000 backup;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend_api;
# --- 传递真实客户端信息 ---
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_connect_timeout 60s; # 连接后端超时
proxy_send_timeout 60s; # 发送请求超时
proxy_read_timeout 120s; # 读取响应超时(长接口要调大)
# --- 缓冲设置 ---
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
}
6.3 不同后端转发示例
server {
listen 80;
server_name example.com;
# Node.js 应用
location /api/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # WebSocket 支持
proxy_set_header Connection "upgrade";
}
# Python/Django 应用(通过 uWSGI)
location /django/ {
include uwsgi_params;
uwsgi_pass 127.0.0.1:8000;
}
# PHP 应用(通过 PHP-FPM)
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Java/Spring Boot 应用
location /spring/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
}
}
七、场景三:负载均衡
7.1 负载均衡策略对比
┌─────────────────────────────────────────────────────────────┐
│ 负载均衡策略选择指南 │
├───────────────┬─────────────────────────────────────────────┤
│ 策略 │ 适用场景 │
├───────────────┼─────────────────────────────────────────────┤
│ 轮询(默认) │ 后端机器性能一致,无状态服务 │
│ weight │ 机器性能不同,强的多分点 │
│ ip_hash │ 需要会话保持(Session 粘滞) │
│ least_conn │ 请求处理时间差异大(长连接场景) │
│ fair(第三方) │ 按响应时间分配(需安装模块) │
│ url_hash(第三方)│ 同一 URL 总是打到同一台(缓存友好) │
└───────────────┴─────────────────────────────────────────────┘
7.2 完整负载均衡配置
upstream web_app {
# 最少连接数策略
least_conn;
server 192.168.1.101:8080 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.103:8080 weight=2 max_fails=3 fail_timeout=30s;
# 备用节点
server 192.168.1.104:8080 backup;
# 保持长连接(减少 TCP 握手开销)
keepalive 32;
}
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://web_app;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# 💡 参数解释:
# weight:权重,数字越大分到的请求越多
# max_fails:在 fail_timeout 时间内失败这么多次,就认为挂了
# fail_timeout:两次失败检查之间的时间间隔
# backup:只有其他服务器全挂了才启用
八、场景四:HTTPS/SSL 配置
8.1 HTTPS 原理简图
HTTPS 握手过程(简化版)
客户端 服务器
│ │
│──── 1. ClientHello(支持的加密套件列表)──────→│
│ │
│←─── 2. ServerHello + 证书 + 公钥 ────────────│
│ │
│ 3. 验证证书(是否可信?域名是否匹配?) │
│ 4. 生成随机密钥,用公钥加密 │
│ │
│──── 5. 加密的密钥 ──────────────────────────→│
│ │
│←─── 6. 确认,开始加密通信 ────────────────────│
│ │
│═════════ 双方用对称密钥加密通信 ═══════════════│
8.4 🎯 实战场景:从 HTTP 全站迁移到 HTTPS
# 场景:你的个人博客 blog.myname.com 原来是 HTTP
# 现在要全站升级 HTTPS,要求:
# 1. 所有 HTTP 自动跳转 HTTPS
# 2. 自动申请和续期证书(Let's Encrypt)
# 3. 安全等级达到 A+
# 第一步:申请证书(使用 certbot 一键申请)
# $ sudo certbot certonly --nginx -d blog.myname.com -d www.myname.com
# 证书自动保存在 /etc/letsencrypt/live/blog.myname.com/
# 第二步:配置 Nginx
# HTTP → 全部 301 跳转到 HTTPS
server {
listen 80;
server_name blog.myname.com www.myname.com;
# Let's Encrypt 续期验证(必须在跳转之前)
location ^~ /.well-known/acme-challenge/ {
root /var/www/html;
}
# 其他所有请求跳转 HTTPS
location / {
return 301 https://blog.myname.com$request_uri;
}
}
# HTTPS 主站
server {
listen 443 ssl http2;
server_name blog.myname.com;
# --- 证书配置 ---
ssl_certificate /etc/letsencrypt/live/blog.myname.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.myname.com/privkey.pem;
# --- 安全参数 ---
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# --- HSTS(告诉浏览器以后直接用 HTTPS 访问)---
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# --- 安全头 ---
include snippets/security-headers.conf;
# --- 博客内容 ---
root /var/www/blog;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
# 第三步:设置自动续期(certbot 自动添加了 cron)
# $ sudo certbot renew --dry-run # 测试续期是否正常
# 证书 90 天有效,certbot 会在到期前 30 天自动续期
# 💡 验证安全等级:
# 访问 https://www.ssllabs.com/ssltest/ 输入你的域名
# 目标:拿到 A 或 A+
8.2 基础 HTTPS 配置
# HTTP 自动跳转 HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
# HTTPS 服务器
server {
listen 443 ssl http2; # 开启 HTTP/2
server_name example.com www.example.com;
# --- SSL 证书 ---
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# --- SSL 协议版本 ---
ssl_protocols TLSv1.2 TLSv1.3; # 禁用不安全的 TLS 1.0/1.1
# --- 加密套件 ---
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:
ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
# --- SSL 会话优化 ---
ssl_session_cache shared:SSL:10m; # 会话缓存(10MB ≈ 40000 个会话)
ssl_session_timeout 1d; # 会话保持 1 天
ssl_session_tickets off; # 禁用 session tickets(提升前向安全性)
# --- OCSP Stapling(加速证书验证)---
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# --- HSTS(强制 HTTPS)---
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# --- 安全头 ---
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# --- 网站内容 ---
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
8.3 使用 Let's Encrypt 免费证书
# 安装 Certbot
sudo apt install certbot python3-certbot-nginx # Ubuntu/Debian
sudo yum install certbot python3-certbot-nginx # CentOS/RHEL
# 自动获取并配置证书(一条命令搞定!)
sudo certbot --nginx -d example.com -d www.example.com
# 自动续期(Certbot 会自动添加定时任务)
sudo certbot renew --dry-run # 测试续期
sudo certbot renew # 手动续期
sudo nginx -s reload
8.4 自签名证书(开发环境用)
# 生成自签名证书
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/selfsigned.key \
-out /etc/nginx/ssl/selfsigned.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Dev/CN=localhost"
8.5 SSL 安全等级对比
┌─────────────────────────────────────────────────────────────┐
│ SSL 安全等级速查表 │
├──────────┬──────────────────────────────────────────────────┤
│ 等级 │ 配置要点 │
├──────────┼──────────────────────────────────────────────────┤
│ ⭐ 基础 │ TLSv1.2,基本加密套件 │
│ ⭐⭐ 标准 │ TLSv1.2+TLSv1.3,ECDHE 套件,HSTS 1年 │
│ ⭐⭐⭐ 安全│ + OCSP Stapling,禁 session tickets │
│ ⭐⭐⭐⭐ │ + Content-Security-Policy,全面安全头 │
│ │ + 0-RTT 禁用(TLS 1.3 防重放攻击) │
└──────────┴──────────────────────────────────────────────────┘
九、场景五:前后端分离部署
9.1 前后端分离架构图
前后端分离架构
浏览器 ────→ Nginx(:80/:443) ────→ 静态资源(前端)
│
├── / → 前端 HTML/JS/CSS
├── /api/ → 后端 API 服务(:3000)
├── /ws/ → WebSocket 服务(:3001)
└── /admin/ → 管理后台(:8080)
9.2 完整前后端分离配置
server {
listen 80;
server_name www.example.com;
root /var/www/frontend/dist; # Vue/React 打包后的静态文件
index index.html;
# ========== 前端页面 ==========
# SPA 路由:所有未匹配的路径都返回 index.html,交给前端路由处理
location / {
try_files $uri $uri/ /index.html;
}
# 前端静态资源(设置强缓存)
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# ========== 后端 API ==========
location /api/ {
proxy_pass http://127.0.0.1:3000;
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;
# CORS 跨域(如果前后端域名不同才需要)
# add_header Access-Control-Allow-Origin "https://www.example.com" always;
# add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
# add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
# add_header Access-Control-Max-Age 3600 always;
# OPTIONS 预检请求直接返回
# if ($request_method = OPTIONS) {
# return 204;
# }
}
# ========== WebSocket ==========
location /ws/ {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400s; # WebSocket 长连接,超时设长
}
# ========== 文件上传 ==========
location /api/upload {
proxy_pass http://127.0.0.1:3000;
client_max_body_size 50m; # 允许上传 50MB 文件
proxy_connect_timeout 300s;
proxy_read_timeout 300s;
}
# ========== 禁止访问隐藏文件 ==========
location ~ /\. {
deny all;
return 404;
}
}
9.4 🎯 实战场景:Vue + Spring Boot 项目部署
# 场景:你是一个全栈开发者,前端用 Vue3,后端用 Spring Boot
# 需要在同一台服务器上部署,只用一个域名 myapp.com
server {
listen 80;
server_name myapp.com;
return 301 https://myapp.com$request_uri;
}
server {
listen 443 ssl http2;
server_name myapp.com;
ssl_certificate /etc/ssl/myapp.com.crt;
ssl_certificate_key /etc/ssl/myapp.com.key;
# ========== 前端:Vue3 打包后的静态文件 ==========
root /home/app/frontend/dist;
index index.html;
# 所有页面路由都返回 index.html(Vue Router 的 history 模式)
location / {
try_files $uri $uri/ /index.html;
# HTML 文件不缓存(每次发版后用户能立即看到最新版)
if ($request_filename ~* \.html$) {
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}
# JS/CSS/图片/字体(文件名带 hash,设置永久缓存)
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# ========== 后端:Spring Boot API ==========
location /api/ {
proxy_pass http://127.0.0.1:8080;
include snippets/proxy-params.conf;
proxy_connect_timeout 10s;
proxy_read_timeout 30s;
}
# 文件上传接口(Spring Boot 处理)
location /api/file/upload {
proxy_pass http://127.0.0.1:8080;
client_max_body_size 100m; # 允许上传 100MB
}
# ========== WebSocket(实时通知)==========
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
# ========== 错误页面 ==========
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /home/app/frontend/dist;
}
}
# 💡 开发时(npm run dev)怎么联调?
# 在 vue.config.js 或 vite.config.ts 中配置代理:
# server: {
# proxy: {
# '/api': { target: 'http://localhost:8080' }
# }
# }
# 这样开发时前端也能访问后端接口,无需 CORS
9.3 多环境配置(开发/测试/生产)
# 使用 map 指令根据域名切换后端
map $host $backend_env {
dev.example.com "http://127.0.0.1:3000"; # 开发环境
test.example.com "http://127.0.0.1:3001"; # 测试环境
www.example.com "http://upstream_prod"; # 生产环境
default "http://127.0.0.1:3000";
}
server {
listen 80;
server_name dev.example.com test.example.com www.example.com;
location /api/ {
proxy_pass $backend_env;
}
}
十、场景六:缓存配置(大幅提升性能)
10.1 缓存类型对比
┌─────────────────────────────────────────────────────────────┐
│ Nginx 缓存体系全景 │
├──────────────────┬──────────────────────────────────────────┤
│ 缓存类型 │ 说明 │
├──────────────────┼──────────────────────────────────────────┤
│ 浏览器缓存 │ 通过 Expires/Cache-Control 头控制 │
│ 代理缓存(Proxy) │ Nginx 把后端响应缓存到本地磁盘 │
│ FastCGI 缓存 │ 缓存 PHP/Python 等动态内容 │
│ 静态文件缓存 │ open_file_cache 缓存文件描述符 │
│ Gzip 压缩 │ 不是缓存但能大幅减少传输量 │
└──────────────────┴──────────────────────────────────────────┘
10.2 代理缓存配置(缓存后端 API 响应)
http {
# --- 代理缓存设置 ---
proxy_cache_path /var/cache/nginx levels=1:2
keys_zone=api_cache:10m # 缓存索引区 10MB
max_size=1g # 缓存数据最大 1GB
inactive=60m # 60 分钟没人访问就清除
use_temp_path=off; # 临时文件和缓存放同一分区
server {
listen 80;
server_name api.example.com;
# 对 GET 请求启用缓存
location /api/public/ {
proxy_pass http://backend;
proxy_cache api_cache;
proxy_cache_valid 200 302 10m; # 200/302 缓存 10 分钟
proxy_cache_valid 404 1m; # 404 缓存 1 分钟
# 添加缓存命中状态头(调试用)
add_header X-Cache-Status $upstream_cache_status;
# 只缓存 GET 和 HEAD
proxy_cache_methods GET HEAD;
# 缓存 key(相同 URL + 参数 = 相同缓存)
proxy_cache_key "$scheme$request_method$host$request_uri";
}
# 不缓存 POST 等写操作
location /api/private/ {
proxy_pass http://backend;
proxy_cache off; # 明确关闭缓存
}
}
}
10.4 🎯 实战场景:新闻网站的性能优化
# 场景:你运营一个新闻网站 news.daily.com
# 特点:首页流量大、新闻内容读多写少、图片多
# 目标:QPS 从 500 提升到 5000,响应时间从 800ms 降到 50ms
proxy_cache_path /var/cache/news levels=1:2
keys_zone=news_zone:50m
max_size=5g
inactive=2h;
server {
listen 80;
server_name news.daily.com;
# --- 首页:缓存 1 分钟(新闻更新频率约 1 分钟)---
location = / {
proxy_pass http://news_backend;
proxy_cache news_zone;
proxy_cache_valid 200 1m;
add_header X-Cache-Status $upstream_cache_status;
}
# --- 新闻详情页:缓存 10 分钟(内容不常变)---
location /article/ {
proxy_pass http://news_backend;
proxy_cache news_zone;
proxy_cache_valid 200 10m;
proxy_cache_key "$scheme$host$request_uri";
add_header X-Cache-Status $upstream_cache_status;
}
# --- 新闻图片:CDN 回源缓存 ---
location /images/ {
proxy_pass http://news_backend;
proxy_cache news_zone;
proxy_cache_valid 200 7d; # 图片几乎不变,缓存 7 天
expires 30d; # 浏览器缓存 30 天
add_header Cache-Control "public";
access_log off; # 图片请求不记日志,减少 IO
}
# --- API:不缓存(实时数据)---
location /api/ {
proxy_pass http://news_backend;
proxy_cache off;
}
# --- 静态资源 ---
location /static/ {
root /var/www/news;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
}
# 💡 效果对比:
# 优化前:首页 800ms,QPS 500
# 优化后:首页 50ms(命中缓存时 5ms),QPS 5000+
# 后端服务器压力降低 90%(大部分请求被 Nginx 缓存挡住了)
10.3 Gzip 压缩配置
http {
# --- Gzip 压缩 ---
gzip on;
gzip_vary on; # 告诉代理:这个响应有压缩版和未压缩版
gzip_proxied any; # 代理请求也压缩
gzip_comp_level 6; # 压缩级别 1-9,6 是性价比最高的
gzip_min_length 1000; # 小于 1KB 不压缩(压缩反而更大)
gzip_buffers 16 8k;
gzip_http_version 1.1;
# 需要压缩的文件类型
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml
application/xml+rss
application/atom+xml
image/svg+xml
font/woff2;
# 禁止对 IE6 压缩(IE6 有 bug)
gzip_disable "msie6";
}
# 💡 效果:HTML/JS/CSS 通常能压缩 60-70%,传输量大幅减少!
十一、场景七:安全防护
11.1 防止常见攻击
server {
listen 80;
server_name example.com;
# ====== 防 SQL 注入 / XSS / 路径遍历 ======
# 检测到恶意请求直接返回 403
if ($args ~* "union.*select.*from") { return 403; }
if ($args ~* "concat.*\(") { return 403; }
if ($args ~* "base64_") { return 403; }
if ($args ~* "(<|%3C).*script") { return 403; }
if ($args ~* "\.\.\/") { return 403; }
# ====== 隐藏版本号 ======
# 在 http 块中设置
# server_tokens off;
# ====== 防盗链 ======
location /images/ {
valid_referers none blocked server_names
*.example.com example.com
*.google.com *.baidu.com;
if ($invalid_referer) {
return 403; # 或返回一张防盗链提示图片
# return 301 https://example.com/hotlink-denied.png;
}
}
# ====== IP 黑白名单 ======
# 白名单(只允许这些 IP 访问管理后台)
location /admin/ {
allow 192.168.1.0/24; # 公司内网
allow 10.0.0.0/8; # VPN
deny all; # 其他全部拒绝
proxy_pass http://127.0.0.1:8080;
}
# ====== 禁止访问敏感文件 ======
location ~* \.(env|git|svn|bak|sql|log|conf)$ {
deny all;
return 404;
}
}
# http 块级别设置
http {
server_tokens off; # 隐藏 Nginx 版本号
# server_names_hash_max_size 512; # 大量域名时调大
}
11.3 🎯 实战场景:保护一个 SaaS 平台的后台 API
# 场景:你开发了一个 SaaS 平台 saas.company.com
# 问题:最近频繁遭遇恶意攻击
# - 有人用爬虫疯狂抓取用户数据
# - 有人用字典暴力破解用户密码
# - 有人用 CC 攻击打瘫了支付接口
# 目标:用 Nginx 层面挡住这些攻击
http {
# --- 限流区域定义 ---
# 全局限流:每IP每秒 20 个请求
limit_req_zone $binary_remote_addr zone=global:20m rate=20r/s;
# 登录限流:每IP每分钟 5 次尝试(防暴力破解)
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# 注册限流:每IP每小时 3 次(防批量注册)
limit_req_zone $binary_remote_addr zone=register:10m rate=3r/h;
# API限流:每IP每秒 10 个请求
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# 并发连接限制:每IP最多 50 个连接
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
listen 443 ssl http2;
server_name saas.company.com;
# --- 全局限流(兜底保护)---
limit_req zone=global burst=40 nodelay;
limit_conn conn_limit 50;
# --- 登录接口:严格限流 ---
location /api/auth/login {
limit_req zone=login burst=2 nodelay;
limit_req_status 429;
proxy_pass http://auth_service:3001;
}
# --- 注册接口:更严格 ---
location /api/auth/register {
limit_req zone=register burst=1 nodelay;
limit_req_status 429;
proxy_pass http://auth_service:3001;
}
# --- 支付接口:防 CC 攻击 ---
location /api/payment/ {
limit_req zone=api burst=5 nodelay;
limit_conn conn_limit 3; # 每IP最多3个并发支付请求
# 同时限制下游速度
limit_rate 500k;
proxy_pass http://payment_service:3002;
}
# --- 数据导出接口:防爬虫大量抓取 ---
location /api/export/ {
limit_req zone=api burst=3 nodelay;
limit_rate 200k; # 限制下载速度
proxy_pass http://data_service:3003;
}
# --- 禁止境外可疑 IP(配合防火墙)---
# 方式1:Nginx 层面 deny(适合少量IP)
# deny 103.21.244.0/22; # 已知攻击来源IP段
# deny 141.101.64.0/18;
# 方式2:使用 geo 模块(推荐,支持白名单)
geo $blocked {
default 0;
103.21.244.0/22 1; # 已知攻击来源
141.101.64.0/18 1; # 已知攻击来源
}
# 如果命中黑名单,返回 403
if ($blocked) {
return 403;
}
# --- 限流友好提示 ---
error_page 429 @rate_limit;
location @rate_limit {
default_type application/json;
return 429 '{"code":429,"message":"请求过于频繁,请稍后再试","retry_after":60}';
}
}
}
# 💡 监控限流效果:
# grep "429" /var/log/nginx/access.log | wc -l
# 可以看到有多少恶意请求被拦截了
11.2 请求限流(防止 DDoS / 暴力破解)
http {
# --- 限流区域定义 ---
# 基于IP的请求速率限制(每秒10个请求)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# 基于IP的并发连接数限制(每个IP最多20个并发连接)
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
# 基于URI的请求速率限制(下载接口限制)
limit_req_zone $binary_remote_addr zone=download_limit:10m rate=2r/s;
server {
listen 80;
server_name api.example.com;
# 普通API限流
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
# burst=20: 允许突发20个请求排队
# nodelay: 突发请求不延迟处理,超出直接拒绝
limit_req_status 429; # 超限返回429而非503
proxy_pass http://backend;
}
# 登录接口更严格的限流
location /api/login {
limit_req zone=api_limit burst=5 nodelay;
limit_conn conn_limit 3; # 每IP最多3个并发连接
proxy_pass http://backend;
}
# 下载接口限流
location /download/ {
limit_req zone=download_limit burst=3 nodelay;
limit_rate 1m; # 下载速度限制1MB/s
proxy_pass http://backend;
}
# 限流时的友好提示
error_page 429 /429.html;
location = /429.html {
return 429 '{"code":429,"msg":"请求太频繁,请稍后再试"}';
add_header Content-Type application/json;
}
}
}
# 💡 $binary_remote_addr 比 $remote_addr 更省内存(IPv4只占4字节 vs 15字节)
十二、日志管理与监控
12.1 自定义日志格式
http {
# --- 默认格式 ---
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'"$request_time"';
# --- JSON 格式(方便 ELK / Loki 等日志系统解析)---
log_format json_log escape=json
'{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request_method":"$request_method",'
'"request_uri":"$request_uri",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"http_x_forwarded_for":"$http_x_forwarded_for"'
'}';
# --- 按日期自动切割日志 ---
# 方法一:使用 logrotate(推荐)
# /etc/logrotate.d/nginx
# /var/log/nginx/*.log {
# daily
# missingok
# rotate 30
# compress
# delaycompress
# notifempty
# create 0640 www-data adm
# sharedscripts
# postrotate
# [ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
# endscript
# }
# 方法二:按时间命名(需要定时任务配合)
map $time_iso8601 $logdate {
default 'untagged';
'~^(\d{4}-\d{2}-\d{2})' $1;
}
access_log /var/log/nginx/access-$logdate.log json_log;
error_log /var/log/nginx/error.log warn;
server {
# 某些路径不记录日志(如健康检查)
location /health {
access_log off;
return 200 'OK';
add_header Content-Type text/plain;
}
# 静态资源只记录错误
location /static/ {
access_log off;
}
}
}
12.2 常用日志变量速查
┌─────────────────────────────────────────────────────────────┐
│ 日志常用变量速查表 │
├─────────────────────┬───────────────────────────────────────┤
│ 变量 │ 含义 │
├─────────────────────┼───────────────────────────────────────┤
│ $remote_addr │ 客户端 IP │
│ $http_x_forwarded_for│ 代理链中的原始 IP │
│ $time_iso8601 │ ISO 格式时间 │
│ $request_method │ GET/POST/PUT/DELETE │
│ $request_uri │ 完整请求 URI(含参数) │
│ $status │ HTTP 状态码 │
│ $body_bytes_sent │ 发送给客户端的字节数 │
│ $request_time │ 请求总处理时间(秒) │
│ $upstream_response_time│ 后端响应时间(秒) │
│ $http_referer │ 来源页面 │
│ $http_user_agent │ 客户端浏览器信息 │
└─────────────────────┴───────────────────────────────────────┘
十三、生产环境配置规范
13.1 推荐的目录结构
/etc/nginx/
├── nginx.conf # 主配置文件(尽量简洁)
├── conf.d/ # 各站点配置(推荐方式)
│ ├── example.com.conf
│ ├── api.example.com.conf
│ └── admin.example.com.conf
├── snippets/ # 可复用的配置片段
│ ├── ssl-params.conf # SSL 通用参数
│ ├── proxy-params.conf # 反向代理通用参数
│ └── security-headers.conf # 安全头
├── ssl/ # SSL 证书
│ ├── example.com.crt
│ └── example.com.key
└── sites-available/ # 可用站点(可选,Debian/Ubuntu 风格)
└── sites-enabled/ # 已启用站点(软链接)
13.2 主配置文件模板(生产级)
# /etc/nginx/nginx.conf
user www-data;
worker_processes auto; # 自动匹配 CPU 核心数
worker_rlimit_nofile 65535; # 每个worker最大打开文件数
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 4096; # 每个worker最大连接数
use epoll; # Linux 使用 epoll
multi_accept on; # 一次性接受所有新连接
}
http {
include mime.types;
default_type application/octet-stream;
# --- 性能优化 ---
sendfile on; # 零拷贝,直接内核态传文件
tcp_nopush on; # 优化数据包发送
tcp_nodelay on; # 禁用 Nagle 算法(低延迟)
keepalive_timeout 65; # 长连接超时
types_hash_max_size 2048;
client_max_body_size 20m; # 默认最大上传 20MB
# --- 隐藏版本号 ---
server_tokens off;
# --- Gzip 压缩 ---
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types text/plain text/css application/json
application/javascript text/xml application/xml;
# --- 日志格式 ---
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time';
access_log /var/log/nginx/access.log main;
# --- 包含站点配置 ---
include /etc/nginx/conf.d/*.conf;
# include /etc/nginx/sites-enabled/*;
}
13.3 可复用配置片段
# /etc/nginx/snippets/proxy-params.conf
# 反向代理通用参数(所有代理 location 引用即可)
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_http_version 1.1;
proxy_set_header Connection "";
# 使用时:
# location /api/ {
# proxy_pass http://backend;
# include snippets/proxy-params.conf;
# }
# /etc/nginx/snippets/security-headers.conf
# 安全头(所有 server 块引用)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
13.4 Nginx 配置检查清单
✅ 生产环境上线前必查项:
基础检查:
□ worker_processes auto(匹配CPU核心)
□ worker_connections >= 1024(并发需求大则调大)
□ server_tokens off(隐藏版本号)
□ client_max_body_size 按业务设置
性能检查:
□ sendfile on / tcp_nopush on / tcp_nodelay on
□ gzip 已开启并覆盖主要类型
□ 静态资源设置了缓存头(Expires/Cache-Control)
□ keepalive_timeout 合理(通常60-120s)
安全检查:
□ 敏感路径有访问控制(/admin、/.env等)
□ 限流配置已开启(防止暴力攻击)
□ 安全响应头已添加
□ HTTPS 配置正确(TLS 1.2+,HSTS)
□ 日志中不包含敏感信息
高可用检查:
□ upstream 健康检查(max_fails / fail_timeout)
□ 负载均衡策略适合业务场景
□ 错误页面配置(50x 优雅降级)
□ 日志切割(logrotate 配置正确)
十四、常见故障排查
14.1 排查流程图
Nginx 出问题了?
│
┌──── 1. 配置语法对吗? ────┐
│ nginx -t │
│ 有错误 → 修改配置 │ 没问题 ↓
└───────────────────────────┘
│
┌──── 2. 端口被占用? ──────┐
│ netstat -tlnp | grep 80 │
│ 被占用 → 杀进程或换端口 │ 没占用 ↓
└───────────────────────────┘
│
┌──── 3. 防火墙放行了? ────┐
│ iptables -L 或 ufw status │
│ 没放行 → 添加规则 │ 已放行 ↓
└───────────────────────────┘
│
┌──── 4. SELinux 限制了? ──┐
│ getenforce │
│ Enforcing → 调整策略 │ Permissive ↓
└───────────────────────────┘
│
┌──── 5. 后端服务正常? ────┐
│ curl http://127.0.0.1:端口 │
│ 不通 → 先修后端 │ 通 ↓
└───────────────────────────┘
│
✅ 检查日志!
tail -f /var/log/nginx/error.log
14.2 常见错误速查表
┌─────────────────────────────────────────────────────────────┐
│ 常见错误及解决方案 │
├───────────┬─────────────────────────────────────────────────┤
│ 错误 │ 原因和解决方案 │
├───────────┼─────────────────────────────────────────────────┤
│ 403 │ ①文件权限不足 → chmod 755 目录, 644 文件 │
│ │ ②缺少索引文件 → 添加 index.html 或 index 指令 │
│ │ ③autoindex off → 设为 on 可列目录(不推荐生产) │
├───────────┼─────────────────────────────────────────────────┤
│ 404 │ ①root 路径错误 → 检查文件是否在对应目录 │
│ │ ②location 匹配错误 → 检查优先级和正则 │
│ │ ③try_files 配置错误 → 检查文件查找顺序 │
├───────────┼─────────────────────────────────────────────────┤
│ 502 │ ①后端服务没启动 → 启动后端服务 │
│ │ ②后端端口错误 → 检查 proxy_pass 地址 │
│ │ ③后端超时 → 调大 proxy_read_timeout │
├───────────┼─────────────────────────────────────────────────┤
│ 504 │ ①后端处理太慢 → 优化后端性能 │
│ │ ②网络问题 → 检查 Nginx 和后端之间的网络连通性 │
│ │ ③超时设置太小 → 调大超时参数 │
├───────────┼─────────────────────────────────────────────────┤
│ 413 │ 上传文件太大 → 调大 client_max_body_size │
├───────────┼─────────────────────────────────────────────────┤
│ 429 │ 触发限流 → 调整 limit_req 参数或检查是否被攻击 │
├───────────┼─────────────────────────────────────────────────┤
│ 连接重置 │ ①keepalive 配置不匹配 │
│ │ ②upstream 未设置 keepalive │
│ │ ③proxy_set_header Connection "" 缺失 │
└───────────┴─────────────────────────────────────────────────┘
14.3 常用排查命令
# 检查配置语法
sudo nginx -t
# 重新加载配置(不中断服务)
sudo nginx -s reload
# 查看进程状态
ps aux | grep nginx
# 实时查看错误日志
tail -f /var/log/nginx/error.log
# 查看访问日志中的慢请求(超过2秒)
awk '$NF > 2 {print $0}' /var/log/nginx/access.log
# 统计各状态码数量
awk '{print $9}' access.log | sort | uniq -c | sort -rn
# 统计访问量 Top 10 的 IP
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# 查看 Nginx 监听端口
ss -tlnp | grep nginx
# 测试后端连通性
curl -I http://127.0.0.1:3000/health
十五、Nginx vs 其他服务器对比
┌─────────────────────────────────────────────────────────────┐
│ Nginx vs Apache vs Caddy vs OpenResty │
├──────────┬──────────┬──────────┬──────────┬─────────────────┤
│ 特性 │ Nginx │ Apache │ Caddy │ OpenResty │
├──────────┼──────────┼──────────┼──────────┼─────────────────┤
│ 并发模型 │ 事件驱动 │ 进程/线程│ 事件驱动 │ 事件驱动 │
│ 高并发 │ ⭐⭐⭐⭐⭐ │ ⭐⭐⭐ │ ⭐⭐⭐⭐ │ ⭐⭐⭐⭐⭐ │
│ 配置难度 │ 中等 │ 复杂 │ 简单 │ 中等+Lua │
│ HTTPS │ 手动配置 │ 手动配置 │ 自动申请 │ 手动配置 │
│ 动态功能 │ 有限 │ 丰富 │ 有限 │ Lua 脚本极强 │
│ 模块生态 │ 丰富 │ 最丰富 │ 一般 │ Lua生态 │
│ 适用场景 │ 通用首选 │ 传统项目 │ 快速搭建 │ 高性能网关/WAF │
└──────────┴──────────┴──────────┴──────────┴─────────────────┘
💡 选择建议:
- 大多数情况 → Nginx(行业标杆,资料最多)
- 快速搭建个人站 → Caddy(自动 HTTPS,零配置)
- 需要 WAF/复杂网关逻辑 → OpenResty(Nginx + Lua)
- 已有 Apache 项目 → 继续用 Apache(别没事找事)
十六、总结与学习路线
Nginx 学习路线图
入门(1-2天)
│
├── 安装和基本命令(start/stop/reload)
├── 理解配置文件结构(http → server → location)
├── 搭建静态文件服务器
└── 配置反向代理
│
进阶(1-2周)
│
├── 掌握 location 匹配规则
├── 负载均衡配置
├── HTTPS/SSL 配置
├── 前后端分离部署
└── 日志分析和排错
│
高级(持续积累)
│
├── 性能调优(缓存、压缩、连接池)
├── 安全加固(限流、WAF、安全头)
├── 高可用方案(Keepalived + Nginx)
├── 容器化部署(Docker + Nginx)
└── OpenResty / Lua 扩展
│
专家
│
├── 内核参数调优(TCP 拥塞控制)
├── 自定义模块开发(C 模块)
├── 源码级问题排查
└── 架构设计(百万级并发方案)
推荐学习资源:
- 📖 官方文档:nginx.org/en/docs/
- 📖 Nginx Cookbook(O'Reilly)
- 📖 《深入理解 Nginx:模块开发与架构解析》(陶辉 著)
- 🔧 在线配置生成器:nginxconfig.io(一键生成生产级配置)
- 🔧 Mozilla SSL 配置生成器:ssl-config.mozilla.org
如果你觉得这篇文章对你有帮助,欢迎点赞收藏!有问题可以在评论区讨论,我会尽量回复~
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)