nginx 常用配置
大白话解释: nginx 就像"门卫"。用户访问你的网站时,先经过 nginx,nginx 决定把请求转发给谁:
- 静态文件(HTML、CSS、图片):nginx 直接返回
- API 请求:转发给后端服务器(Node.js、Python、Java)
- 多个后端服务器:nginx 分配请求,实现负载均衡
为什么需要 nginx?
- 反向代理:隐藏后端服务器的真实地址,更安全
- 负载均衡:多个服务器分担压力,提高性能
- 静态资源服务:nginx 处理静态文件比 Node.js 快得多
- HTTPS:配置 SSL 证书,支持 HTTPS 访问
什么时候用 nginx?
- 生产环境部署前端项目
- 需要反向代理 API 请求
- 需要负载均衡多个后端服务
- 需要配置 HTTPS
nginx 是高性能的 HTTP 和反向代理服务器,常用于静态资源服务、反向代理、负载均衡。
安装
# Ubuntu/Debian
sudo apt update
sudo apt install nginx
# CentOS/RHEL
sudo yum install nginx
# macOS
brew install nginx
# 验证
nginx -v基本命令
# 启动
sudo systemctl start nginx
# 停止
sudo systemctl stop nginx
# 重启
sudo systemctl restart nginx
# 重新加载配置(不中断服务)
sudo systemctl reload nginx
# 查看状态
sudo systemctl status nginx
# 测试配置文件
sudo nginx -t配置文件结构
# /etc/nginx/nginx.conf 主配置文件
# nginx 配置由多个"块"组成,层级关系:main → http → server → location
# ---- 全局块:影响 nginx 整体运行 ----
user nginx; # worker 进程运行的用户(生产环境建议用 nginx 或 www-data)
worker_processes auto; # worker 进程数,auto = 自动匹配 CPU 核心数
error_log /var/log/nginx/error.log; # 错误日志路径
# ---- events 块:连接处理相关配置 ----
events {
worker_connections 1024; # 每个 worker 进程的最大并发连接数
}
# ---- http 块:HTTP 服务器相关配置(最常用的配置区域)----
http {
include /etc/nginx/mime.types; # 引入 MIME 类型映射(让浏览器正确识别文件类型)
default_type application/octet-stream; # 未识别类型的默认 MIME
# 自定义日志格式:记录客户端 IP、时间、请求、状态码等
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer"';
# 引入 server 配置(每个 .conf 文件定义一个或多个虚拟主机)
include /etc/nginx/conf.d/*.conf;
}配置字段详解
全局块
全局块定义影响 nginx 整体运行的参数,位于 nginx.conf 最外层。
| 字段 | 说明 | 语法 | 默认值 | 示例 |
|---|---|---|---|---|
user | 设置 worker 进程运行的用户和用户组 | user username [groupname]; | nobody nobody | user nginx nginx; |
worker_processes | worker 进程数,auto 表示自动检测 CPU 核心数 | worker_processes number | auto; | 1 | worker_processes auto; |
worker_cpu_affinity | 绑定 worker 进程到指定 CPU 核心 | worker_cpu_affinity cpumask ...; | 无绑定 | worker_cpu_affinity 01 10; |
worker_rlimit_nofile | 每个 worker 进程可打开的最大文件描述符数 | worker_rlimit_nofile number; | 操作系统限制 | worker_rlimit_nofile 65535; |
error_log | 错误日志路径和级别 | error_log file [level]; | logs/error.log error | error_log /var/log/nginx/error.log warn; |
pid | PID 文件路径 | pid file; | logs/nginx.pid | pid /run/nginx.pid; |
daemon | 是否以守护进程方式运行 | daemon on | off; | on | daemon off; |
error_log 日志级别(从低到高):
| 级别 | 说明 |
|---|---|
debug | 调试信息,需要编译时开启 --with-debug |
info | 一般信息 |
notice | 正常但值得注意的信息 |
warn | 警告信息 |
error | 错误信息 |
crit | 严重错误 |
alert | 需要立即处理 |
emerg | 系统不可用 |
💡 生产环境建议使用
warn或error级别,debug会产生大量日志影响性能。
user nginx nginx;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 65535;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
daemon on;events 块
events 块配置 nginx 的事件处理模型和连接相关参数。
| 字段 | 说明 | 语法 | 默认值 | 示例 |
|---|---|---|---|---|
worker_connections | 每个 worker 进程的最大并发连接数 | worker_connections number; | 512 | worker_connections 65535; |
use | 指定事件驱动模型 | use method; | 自动选择最优 | use epoll; |
multi_accept | 是否允许一个 worker 进程同时接受多个新连接 | multi_accept on | off; | off | multi_accept on; |
accept_mutex | 是否启用互斥锁接受新连接(避免惊群效应) | accept_mutex on | off; | off | accept_mutex on; |
事件驱动模型对比:
| 模型 | 平台 | 特点 |
|---|---|---|
epoll | Linux 2.6+ | 高性能,适合大量并发连接,推荐 |
kqueue | FreeBSD/macOS | BSD 系统的高性能模型 |
select | 所有平台 | 兼容性最好,性能最差,连接数受限(默认 1024) |
poll | 大多数 Unix | 类似 select,无连接数硬限制 |
💡 最大并发连接数 =
worker_processes×worker_connections。作为反向代理时实际为worker_processes×worker_connections÷ 2(因为浏览器和后端各占一个连接)。
events {
worker_connections 65535;
use epoll;
multi_accept on;
accept_mutex off;
}http 块
http 块是配置最密集的部分,包含 HTTP 服务器相关的全局设置。
基础设置
| 字段 | 说明 | 语法 | 默认值 | 示例 |
|---|---|---|---|---|
include | 引入其他配置文件 | include file | mask; | - | include /etc/nginx/mime.types; |
default_type | 默认 MIME 类型(未匹配到类型时使用) | default_type mime/type; | text/plain | default_type application/octet-stream; |
sendfile | 是否启用 sendfile 系统调用(零拷贝传输) | sendfile on | off; | off | sendfile on; |
tcp_nopush | 在 sendfile 开启时,合并数据包发送(需配合 sendfile) | tcp_nopush on | off; | off | tcp_nopush on; |
tcp_nodelay | 禁用 Nagle 算法,减少小数据包延迟 | tcp_nodelay on | off; | on | tcp_nodelay on; |
keepalive_timeout | 客户端长连接超时时间 | keepalive_timeout timeout [header_timeout]; | 75s | keepalive_timeout 65s; |
keepalive_requests | 单个长连接最大请求数 | keepalive_requests number; | 1000 | keepalive_requests 1000; |
open_file_cache | 文件描述符缓存 | open_file_cache max=N [inactive=time]; | off | open_file_cache max=10000 inactive=60s; |
open_file_cache_valid | 缓存有效性检查间隔 | open_file_cache_valid time; | 60s | open_file_cache_valid 30s; |
open_file_cache_min_uses | 在 inactive 时间内最少被访问几次才保留在缓存中 | open_file_cache_min_uses number; | 1 | open_file_cache_min_uses 2; |
open_file_cache_errors | 是否缓存文件查找错误 | open_file_cache_errors on | off; | off | open_file_cache_errors on; |
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65s;
keepalive_requests 1000;
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
}客户端请求限制
| 字段 | 说明 | 语法 | 默认值 | 示例 |
|---|---|---|---|---|
client_max_body_size | 客户端请求体最大值(文件上传大小限制) | client_max_body_size size; | 1m | client_max_body_size 50m; |
client_body_buffer_size | 请求体缓冲区大小,超过此值会写入临时文件 | client_body_buffer_size size; | 8k/16k | client_body_buffer_size 128k; |
client_header_timeout | 读取请求头超时时间 | client_header_timeout time; | 60s | client_header_timeout 60s; |
client_body_timeout | 读取请求体超时时间 | client_body_timeout time; | 60s | client_body_timeout 60s; |
large_client_header_buffers | 大请求头缓冲区数量和大小 | large_client_header_buffers number size; | 4 8k | large_client_header_buffers 4 32k; |
⚠️
client_max_body_size为1m时,上传大于 1MB 的文件会返回413 Request Entity Too Large错误。
安全与日志
| 字段 | 说明 | 语法 | 默认值 | 示例 |
|---|---|---|---|---|
server_tokens | 是否在响应头中显示 nginx 版本号 | server_tokens on | off | build; | on | server_tokens off; |
log_format | 定义日志格式 | log_format name string ...; | combined | 见下方示例 |
access_log | 访问日志路径和格式 | access_log file [format [buffer=size]]; | logs/access.log combined | access_log /var/log/nginx/access.log main; |
log_format 常用变量:
| 变量 | 说明 |
|---|---|
$remote_addr | 客户端 IP 地址 |
$remote_user | 客户端用户名(HTTP Basic Auth) |
$time_local | 访问时间(本地格式) |
$time_iso8601 | 访问时间(ISO 8601 格式) |
$request | 请求行(方法 + URI + 协议) |
$status | 响应状态码 |
$body_bytes_sent | 响应体字节数 |
$http_referer | 来源页面 |
$http_user_agent | 客户端 User-Agent |
$http_x_forwarded_for | 经过多级代理后的客户端真实 IP |
$request_time | 请求处理时间(秒) |
$upstream_response_time | 后端响应时间(秒) |
$upstream_addr | 后端服务器地址 |
$request_uri | 完整请求 URI(含参数) |
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$request_time';
log_format json escape=json '{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"http_user_agent":"$http_user_agent"'
'}';
access_log /var/log/nginx/access.log main;完整 http 块示例
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65s;
keepalive_requests 1000;
client_max_body_size 50m;
client_body_buffer_size 128k;
client_header_timeout 60s;
client_body_timeout 60s;
large_client_header_buffers 4 32k;
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
server_tokens off;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $request_time';
access_log /var/log/nginx/access.log main;
include /etc/nginx/conf.d/*.conf;
}server 块
server 块定义一个虚拟主机,可配置多个 server 块来服务不同域名。
listen 指令
| 语法 | 说明 |
|---|---|
listen 80; | 监听 80 端口(IPv4 + IPv6) |
listen 0.0.0.0:80; | 仅监听 IPv4 所有地址 |
listen [::]:80; | 仅监听 IPv6 所有地址 |
listen 443 ssl; | 启用 SSL |
listen 443 ssl http2; | 启用 SSL + HTTP/2(nginx 1.9.5+,需编译时带 --with-http_v2_module) |
listen 80 default_server; | 设为默认服务器(未匹配到其他 server 时使用) |
listen 80 backlog=1024; | 设置连接队列长度 |
listen 443 ssl http2 reuseport; | 启用端口复用(多 worker 共享端口,提升性能) |
server_name 指令
匹配优先级从高到低:
| 类型 | 语法 | 示例 | 说明 |
|---|---|---|---|
| 精确匹配 | server_name name; | server_name example.com; | 完全匹配 |
| 前缀通配符 | server_name *.name; | server_name *.example.com; | 匹配子域名 |
| 后缀通配符 | server_name name.*; | server_name example.*; | 匹配所有后缀 |
| 正则匹配 | server_name ~pattern; | server_name ~^www\d+\.example\.com$; | 正则表达式匹配 |
server {
listen 80 default_server;
server_name example.com www.example.com;
root /var/www/html;
index index.html;
charset utf-8;
}SSL 相关字段
| 字段 | 说明 | 语法 | 示例 |
|---|---|---|---|
ssl_certificate | SSL 证书文件路径(PEM 格式) | ssl_certificate file; | ssl_certificate /etc/nginx/ssl/cert.pem; |
ssl_certificate_key | SSL 私钥文件路径 | ssl_certificate_key file; | ssl_certificate_key /etc/nginx/ssl/key.pem; |
ssl_protocols | 允许的 SSL/TLS 协议版本 | ssl_protocols ...; | ssl_protocols TLSv1.2 TLSv1.3; |
ssl_ciphers | 加密套件配置 | ssl_ciphers string; | ssl_ciphers HIGH:!aNULL:!MD5; |
ssl_prefer_server_ciphers | 是否优先使用服务器端加密套件 | ssl_prefer_server_ciphers on | off; | ssl_prefer_server_ciphers on; |
ssl_session_cache | SSL 会话缓存 | ssl_session_cache off | none | [builtin[:size]]; | ssl_session_cache shared:SSL:10m; |
ssl_session_timeout | SSL 会话超时时间 | ssl_session_timeout time; | ssl_session_timeout 10m; |
ssl_session_tickets | 是否启用 SSL 会话票据 | ssl_session_tickets on | off; | ssl_session_tickets off; |
ssl_stapling | 是否启用 OCSP Stapling | ssl_stapling on | off; | ssl_stapling on; |
ssl_stapling_verify | 是否验证 OCSP 响应 | ssl_stapling_verify on | off; | ssl_stapling_verify on; |
生成自签名证书(仅用于开发/测试环境):
# 生成自签名证书(有效期 365 天)
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout /etc/nginx/ssl/key.pem \
-out /etc/nginx/ssl/cert.pem \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Dev/CN=example.com"
# 生成 DH 参数(增强安全性)
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048# 使用自签名证书
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
}HTTP/2 配置
HTTP/2 需要 SSL,nginx 1.9.5+ 支持,编译时需带 --with-http_v2_module。http2_* 系列指令需 nginx 1.19.7+。
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
http2_max_concurrent_streams 128;
http2_max_field_size 16k;
http2_max_header_size 32k;
http2_recv_timeout 30s;
http2_idle_timeout 180s;
}| 字段 | 说明 | 默认值 | 示例 |
|---|---|---|---|
http2_max_concurrent_streams | 单连接最大并发流数 | 128 | http2_max_concurrent_streams 256; |
http2_max_field_size | 单个 HPACK 头字段最大值 | 4k | http2_max_field_size 8k; |
http2_max_header_size | 所有 HPACK 头字段总大小上限 | 16k | http2_max_header_size 24k; |
http2_recv_timeout | 读取客户端数据超时 | 30s | http2_recv_timeout 30s; |
http2_idle_timeout | 空闲连接超时 | 180s | http2_idle_timeout 300s; |
💡 HTTP/2 使用单连接多路复用,不再需要
keepalive优化多个 TCP 连接。HTTP/2 下浏览器对同域名的并发连接限制不再适用。
安全响应头
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.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;
ssl_stapling on;
ssl_stapling_verify on;
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 Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}| 安全头 | 说明 |
|---|---|
X-Frame-Options | 防止页面被嵌入 iframe(DENY 禁止 / SAMEORIGIN 同源允许) |
X-Content-Type-Options | 禁止浏览器嗅探 MIME 类型(nosniff) |
X-XSS-Protection | 启用浏览器 XSS 过滤 |
Strict-Transport-Security | 强制浏览器使用 HTTPS(HSTS) |
Content-Security-Policy | 限制页面可加载的资源来源(CSP) |
Referrer-Policy | 控制 Referer 头发送策略 |
location 块
location 块用于匹配请求 URI,并对匹配的请求执行特定处理。
匹配规则优先级
| 优先级 | 语法 | 说明 | 是否停止搜索 |
|---|---|---|---|
| 1(最高) | location = /path | 精确匹配 | 是 |
| 2 | location ^~ /path | 前缀匹配,不再检查正则 | 是 |
| 3 | location ~ pattern | 正则匹配(区分大小写) | 是(按顺序首个匹配) |
| 4 | location ~* pattern | 正则匹配(不区分大小写) | 是(按顺序首个匹配) |
| 5(最低) | location /path | 前缀匹配 | 否(继续搜索正则) |
| 默认 | location / | 通用匹配(兜底) | 否 |
匹配流程:
- 先检查精确匹配(
=),命中则立即返回 - 所有前缀匹配,记住最长匹配结果;如果最长匹配带
^~,则直接返回 - 按配置顺序检查正则匹配(
~/~*),首个命中则返回 - 如果正则都未命中,返回步骤 2 记住的最长前缀匹配
# 精确匹配首页(优先级最高,仅匹配 "/")
location = / {
return 200 "homepage";
}
# 前缀匹配,带 ^~ 表示优先级高于正则(匹配 /static/ 下所有路径)
location ^~ /static/ {
root /var/www; # 实际查找 /var/www/static/...
}
# 正则匹配(区分大小写):以 .php 结尾的请求
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000; # 转发给 PHP-FPM 处理
}
# 正则匹配(不区分大小写):图片文件设置 30 天缓存
location ~* \.(jpg|png|gif)$ {
expires 30d; # 浏览器缓存 30 天
}
# 通用前缀匹配(兜底规则):SPA 应用的 history 路由
location / {
try_files $uri $uri/ /index.html; # 先找文件 → 再找目录 → 回退到 index.html
}try_files 指令
| 语法 | 说明 |
|---|---|
try_files file1 file2 ... uri; | 依次检查文件是否存在,最后一个参数为内部重定向 URI |
try_files $uri $uri/ =404; | 检查文件和目录,不存在返回 404 |
try_files $uri $uri/ /index.html; | SPA 应用常用,所有未匹配路由回退到 index.html |
alias vs root 区别
| 指令 | 行为 | 示例(请求 /img/logo.png) |
|---|---|---|
root /var/www; | 将 URI 追加到 root 路径后面 | 实际路径:/var/www/img/logo.png |
alias /data/img; | 用 alias 路径替换 location 匹配部分 | 实际路径:/data/img/logo.png |
⚠️
alias后面的路径必须以/结尾(location 带/时)。alias不能在正则 location 中使用(除非用捕获组)。
# root 示例
location /img/ {
root /var/www; # 实际查找 /var/www/img/...
}
# alias 示例
location /img/ {
alias /data/images/; # 实际查找 /data/images/...
}
# 正则 location 中使用 alias
location ~ ^/download/(.*)$ {
alias /data/files/$1;
}return 与 rewrite
return 指令:
| 语法 | 说明 | 示例 |
|---|---|---|
return code; | 返回指定状态码 | return 403; |
return code URL; | 返回重定向 | return 301 https://new.example.com; |
return code "text"; | 返回文本响应 | return 200 "OK"; |
常用状态码:301(永久重定向)、302(临时重定向)、403(禁止)、404(未找到)、444(关闭连接不返回响应)。
rewrite 指令:
| 语法 | 说明 |
|---|---|
rewrite regex replacement [flag]; | 按正则重写 URI |
| flag | 说明 |
|---|---|
last | 停止当前 rewrite,重新发起 location 匹配(内部跳转) |
break | 停止当前 rewrite,在当前 location 内继续处理 |
redirect | 返回 302 临时重定向 |
permanent | 返回 301 永久重定向 |
# 去掉 www
if ($host = 'www.example.com') {
return 301 https://example.com$request_uri;
}
# 强制 HTTPS
if ($scheme = 'http') {
return 301 https://$server_name$request_uri;
}
# URL 重写
rewrite ^/old/(.*)$ /new/$1 permanent;💡 能用
return实现的重定向,尽量不用if+rewrite,性能更好。
set 与 map 指令
set 指令:定义变量,可在 server/location/if 块中使用。
# 定义变量
set $backend "http://localhost:3000";
set $proto $scheme;
# 在 proxy_pass 中使用变量
location / {
proxy_pass $backend;
}map 指令:在 http 块中根据源变量值映射出新变量。
| 语法 | 说明 |
|---|---|
map $source $target { ... } | 根据 $source 的值映射出 $target |
default | 默认值 |
include | 引入映射规则文件 |
hostnames | 支持通配符域名匹配 |
# 根据 User-Agent 映射移动端标识
map $http_user_agent $is_mobile {
default 0;
"~*mobile" 1;
"~*android" 1;
"~*iphone" 1;
}
# 根据来源限制 CORS
map $http_origin $cors_origin {
default "";
"https://example.com" $http_origin;
"https://admin.example.com" $http_origin;
}
server {
location / {
if ($is_mobile) {
rewrite ^ /mobile$uri;
}
add_header Access-Control-Allow-Origin $cors_origin;
}
}⚠️
map必须放在 http 块中,不能放在 server/location 内。映射结果可作为proxy_pass、add_header等指令的参数。
if 指令注意事项("if is evil")
nginx 官方建议尽量避免使用 if,因为它在某些上下文中行为不可预测。
安全用法(仅限 return 和 rewrite):
# ✅ 安全:if + return
if ($scheme = 'http') {
return 301 https://$server_name$request_uri;
}
# ✅ 安全:if + rewrite
if ($request_uri ~* "/old/") {
rewrite ^ /new$request_uri? permanent;
}危险用法(避免在 if 块中使用非 return/rewrite 指令):
# ❌ 危险:if 块中的 proxy_pass 可能不生效
location / {
if ($arg_debug) {
proxy_pass http://debug_backend;
}
proxy_pass http://backend;
}
# ❌ 危险:if 块中的 add_header 可能被忽略
location / {
if ($arg_version = '2') {
add_header X-API-Version 2;
}
}替代方案:
# 用 map + try_files 替代 if 判断
map $arg_env $backend {
default "http://production:3000";
"staging" "http://staging:3000";
"dev" "http://dev:3000";
}
location / {
proxy_pass $backend;
}💡 原则:能用
map、try_files、return、error_page实现的逻辑,不要用if。if仅用于简单的return或rewrite。
proxy_pass 末尾斜杠区别
| 配置 | 请求 /api/users 实际代理到 |
|---|---|
proxy_pass http://backend; | http://backend/api/users(保留完整 URI) |
proxy_pass http://backend/; | http://backend/users(去掉 location 匹配部分) |
proxy_pass http://backend/v1; | http://backend/v1users(⚠️ 可能不符合预期) |
proxy_pass http://backend/v1/; | http://backend/v1/users(✅ 正确替换) |
⚠️ 当
proxy_pass包含 URI 部分(如/v1/),nginx 会用它替换 location 匹配的部分。如果不包含 URI,则保留完整的原始请求 URI。
proxy_set_header 详解
| 变量 | 说明 |
|---|---|
Host $host | 将原始请求的 Host 头传给后端(默认传的是 proxy_pass 的地址) |
X-Real-IP $remote_addr | 客户端真实 IP 地址 |
X-Forwarded-For $proxy_add_x_forwarded_for | 代理链上所有 IP(逗号分隔) |
X-Forwarded-Proto $scheme | 客户端原始协议(http/https) |
X-Forwarded-Host $host | 客户端原始 Host |
X-Forwarded-Port $server_port | 客户端原始端口 |
Upgrade $http_upgrade | WebSocket 升级头 |
Connection "upgrade" | WebSocket 连接升级 |
location /api/ {
proxy_pass http://backend/;
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 X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
}proxy 超时、缓冲与缓存
超时配置:
| 字段 | 说明 | 默认值 | 推荐值 |
|---|---|---|---|
proxy_connect_timeout | 与后端建立连接的超时时间 | 60s | 60s |
proxy_send_timeout | 向后端发送请求的超时时间 | 60s | 60s |
proxy_read_timeout | 从后端读取响应的超时时间 | 60s | 120s |
缓冲配置:
| 字段 | 说明 | 默认值 | 推荐值 |
|---|---|---|---|
proxy_buffering | 是否启用响应缓冲 | on | on |
proxy_buffer_size | 读取响应头的缓冲区大小 | 4k/8k | 4k |
proxy_buffers | 读取响应体的缓冲区数量和大小 | 8 4k/8k | 8 16k |
proxy_busy_buffers_size | 在缓冲未全部写完时可向客户端发送的数据量 | 8k/16k | 32k |
缓存配置:
| 字段 | 说明 | 示例 |
|---|---|---|
proxy_cache_path | 定义缓存路径和参数 | proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m; |
proxy_cache | 使用已定义的缓存区域 | proxy_cache my_cache; |
proxy_cache_valid | 不同状态码的缓存有效期 | proxy_cache_valid 200 302 10m; |
proxy_cache_key | 缓存 key 的组成 | proxy_cache_key $host$uri$is_args$args; |
proxy_cache_use_stale | 后端出错时是否使用过期缓存 | proxy_cache_use_stale error timeout; |
proxy_cache_bypass | 跳过缓存的条件 | proxy_cache_bypass $cookie_nocache $arg_nocache; |
proxy_no_cache | 不缓存的条件 | proxy_no_cache $cookie_nocache $arg_nocache; |
location /api/ {
proxy_pass http://backend/;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 120s;
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
}expires 指令
用于设置响应头中的 Cache-Control 和 Expires,控制浏览器缓存。
| 语法 | 说明 |
|---|---|
expires 30d; | 缓存 30 天 |
expires modified +1h; | 基于文件修改时间缓存 1 小时 |
expires epoch; | 禁用缓存(设为过期时间 0) |
expires off; | 不添加 Expires/Cache-Control 头 |
expires max; | 缓存到 2037 年 12 月 31 日 |
upstream 块
upstream 块定义后端服务器组,用于负载均衡。
server 参数
| 参数 | 说明 | 语法 | 示例 |
|---|---|---|---|
weight | 权重,数值越大分配请求越多 | weight=n | server 10.0.0.1:3000 weight=5; |
max_fails | 允许的最大失败次数,超过后标记为不可用 | max_fails=n | server 10.0.0.1:3000 max_fails=3; |
fail_timeout | 标记不可用的持续时间;同时是失败检测的时间窗口 | fail_timeout=time | server 10.0.0.1:3000 fail_timeout=30s; |
backup | 备用服务器,仅在主服务器全部不可用时启用 | backup | server 10.0.0.3:3000 backup; |
down | 标记服务器永久不可用 | down | server 10.0.0.4:3000 down; |
负载均衡策略
| 策略 | 说明 | 配置方式 |
|---|---|---|
| 轮询(Round Robin) | 默认策略,按顺序依次分配 | 不需要额外指令 |
| 加权轮询 | 按 weight 比例分配 | server ... weight=n; |
| IP Hash | 同一客户端 IP 始终分配到同一服务器 | ip_hash; |
| 最少连接 | 将请求分配到当前连接数最少的服务器 | least_conn; |
| 随机 | 随机选择服务器 | random two least_conn; |
| Hash | 按自定义 key 的 hash 值分配 | hash $request_uri consistent; |
keepalive 连接池
| 字段 | 说明 | 语法 | 示例 |
|---|---|---|---|
keepalive | 每个 worker 进程与后端保持的空闲长连接数 | keepalive connections; | keepalive 32; |
keepalive_requests | 每个长连接最大请求数 | keepalive_requests number; | keepalive_requests 100; |
keepalive_time | 长连接最大存活时间 | keepalive_time time; | keepalive_time 1h; |
keepalive_timeout | 空闲长连接超时时间 | keepalive_timeout time; | keepalive_timeout 60s; |
⚠️ 使用
keepalive时,proxy_pass必须使用域名(不能带 URI),且需要设置proxy_http_version 1.1和清除Connection头。
⚠️ 当
proxy_pass或upstream中使用域名(如proxy_pass http://api.example.com)时,nginx 启动时会尝试解析域名。如果域名无法解析,nginx 会启动失败。需在http块中配置resolver:nginxhttp { resolver 8.8.8.8 valid=30s; # 使用 Google DNS,缓存 30 秒 # 或使用系统 DNS # resolver 127.0.0.53 valid=10s; }
upstream backend {
least_conn; # 负载均衡策略:最少连接(将请求分配到连接数最少的服务器)
server 10.0.0.1:3000 weight=3 max_fails=3 fail_timeout=30s; # 主服务器1:权重 3(分到更多请求),失败 3 次后暂停 30 秒
server 10.0.0.2:3000 weight=2; # 主服务器2:权重 2
server 10.0.0.3:3000 backup; # 备用服务器:仅在主服务器全部不可用时启用
keepalive 32; # 保持 32 个空闲长连接到后端(减少 TCP 握手开销)
keepalive_requests 100; # 每个长连接最多处理 100 个请求后关闭
keepalive_timeout 60s; # 空闲 60 秒后关闭长连接
}
server {
listen 80; # 监听 80 端口
server_name example.com; # 域名
location / {
proxy_pass http://backend; # 转发到 upstream 定义的 backend 组
proxy_http_version 1.1; # 使用 HTTP/1.1(keepalive 必需)
proxy_set_header Connection ""; # 清除 Connection 头(让 keepalive 生效)
}
}Gzip 压缩
| 字段 | 说明 | 语法 | 默认值 | 示例 |
|---|---|---|---|---|
gzip | 是否启用 gzip 压缩 | gzip on | off; | off | gzip on; |
gzip_types | 需要压缩的 MIME 类型 | gzip_types type ...; | text/html | gzip_types text/css application/javascript application/json; |
gzip_min_length | 启用压缩的最小响应体大小 | gzip_min_length length; | 20 | gzip_min_length 1024; |
gzip_comp_level | 压缩级别(1-9),越高压缩比越大但 CPU 开销越大 | gzip_comp_level level; | 1 | gzip_comp_level 5; |
gzip_vary | 是否添加 Vary: Accept-Encoding 响应头 | gzip_vary on | off; | off | gzip_vary on; |
gzip_proxied | 对代理请求是否启用压缩 | gzip_proxied off | expired | no-cache | ...; | off | gzip_proxied any; |
gzip_static | 是否优先使用预压缩的 .gz 文件(需 ngx_http_gzip_static_module) | gzip_static on | off; | off | gzip_static on; |
💡 压缩级别建议设为
5(性能和压缩比的平衡点)。不要压缩图片、视频等已经压缩过的格式。gzip_min_length建议设为1024,过小的响应体压缩后可能反而变大。
# Gzip 压缩配置:减少传输体积,加快页面加载
gzip on; # 启用 Gzip 压缩
gzip_vary on; # 添加 Vary: Accept-Encoding 头(CDN 缓存区分压缩/非压缩版本)
gzip_proxied any; # 对所有代理请求启用压缩
gzip_comp_level 5; # 压缩级别 5(1-9,5 是性能和压缩比的平衡点)
gzip_min_length 1024; # 小于 1KB 的响应不压缩(压缩后可能更大)
gzip_static on; # 优先使用预压缩的 .gz 文件(需 ngx_http_gzip_static_module)
# 需要压缩的文件类型(图片/视频已经是压缩格式,不需要再压缩)
gzip_types
text/plain
text/css
text/javascript
text/xml
application/json
application/javascript
application/xml
application/rss+xml
image/svg+xml;限流配置
请求速率限制
limit_req_zone 在 http 块定义,limit_req 在 location/server 块引用。
| 字段 | 说明 | 语法 | 示例 |
|---|---|---|---|
limit_req_zone | 定义请求速率限制区域 | limit_req_zone key zone=name:size rate=rate; | limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; |
limit_req | 应用请求速率限制 | limit_req zone=name [burst=n] [nodelay]; | limit_req zone=api burst=20 nodelay; |
| 参数 | 说明 |
|---|---|
key | 限流维度($binary_remote_addr 按 IP,$server_name 按域名) |
zone | 共享内存区域名和大小(1m 约存储 16000 个 IP) |
rate | 请求速率(r/s 每秒,r/m 每分钟) |
burst | 突发容量,超出 rate 的排队请求数上限 |
nodelay | 突发请求不排队,直接处理(但仍受 burst 限制) |
并发连接限制
limit_conn_zone 在 http 块定义,limit_conn 在 location/server 块引用。
| 字段 | 说明 | 语法 | 示例 |
|---|---|---|---|
limit_conn_zone | 定义并发连接限制区域 | limit_conn_zone key zone=name:size; | limit_conn_zone $binary_remote_addr zone=addr:10m; |
limit_conn | 应用并发连接限制 | limit_conn zone number; | limit_conn addr 10; |
http {
# 按 IP 限制请求速率:每秒 10 个请求
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# 按 IP 限制并发连接:最多 10 个
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
listen 80;
server_name example.com;
# API 接口限流:允许突发 20 个请求
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
limit_conn conn_limit 10;
proxy_pass http://localhost:3000;
}
# 静态资源不限流
location /static/ {
root /var/www;
}
}
}⚠️ 限流触发后默认返回
503 Service Temporarily Unavailable。可通过limit_req_status 429;自定义状态码。
常用内置变量
| 变量 | 说明 | 示例值 |
|---|---|---|
$remote_addr | 客户端 IP 地址 | 192.168.1.100 |
$host | 请求头中的 Host 字段(不含端口) | example.com |
$server_name | 匹配的 server 块的 server_name | example.com |
$server_port | 服务器监听端口 | 80 |
$request_uri | 完整请求 URI(含参数,不含域名) | /api/users?page=1 |
$uri | 当前 URI(不含参数,经过 rewrite 后可能改变) | /api/users |
$args | 查询参数字符串 | page=1 |
$arg_name | 获取指定查询参数的值(name 替换为参数名) | $arg_page → 1 |
$request_method | 请求方法 | GET、POST |
$status | 响应状态码 | 200 |
$body_bytes_sent | 响应体字节数 | 1234 |
$scheme | 请求协议 | http 或 https |
$request_time | 请求处理时间(秒,毫秒精度) | 0.032 |
$upstream_addr | 后端服务器地址 | 10.0.0.1:3000 |
$upstream_status | 后端返回的状态码 | 200 |
$upstream_response_time | 后端响应时间(秒) | 0.025 |
$http_referer | 来源页面 URL | https://google.com |
$http_user_agent | 客户端 User-Agent | Mozilla/5.0 ... |
$http_x_forwarded_for | 多级代理链 IP | client, proxy1, proxy2 |
$cookie_name | 获取指定 Cookie 的值 | $cookie_session |
$request_filename | 请求对应的文件路径 | /var/www/html/index.html |
$document_root | 当前 server 的 root 路径 | /var/www/html |
$limit_req_status | 限流触发后的状态码 | 503 |
# 使用变量实现条件判断
if ($request_method !~ ^(GET|HEAD|POST)$) {
return 405;
}
# 使用 $arg_ 变量获取查询参数
if ($arg_token = '') {
return 401;
}常用配置
静态资源服务
server {
listen 80; # 监听 80 端口
server_name example.com; # 你的域名
root /var/www/html; # 静态文件根目录
index index.html; # 默认首页文件
# SPA 路由:所有未匹配的请求回退到 index.html
location / {
try_files $uri $uri/ /index.html; # 先找文件 → 再找目录 → 回退到 index.html
}
# 静态资源缓存:js/css/图片等设置 1 年长期缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y; # 缓存 1 年
add_header Cache-Control "public, immutable"; # 公共缓存,内容不变
}
}反向代理
server {
listen 80; # 监听 80 端口
server_name api.example.com; # API 域名
location / {
proxy_pass http://localhost:3000; # 转发到本地 3000 端口的后端服务
proxy_set_header Host $host; # 显式传递原始 Host 头(确保后端收到的是请求域名而非 localhost)
proxy_set_header X-Real-IP $remote_addr; # 传递客户端真实 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 传递代理链上所有 IP
proxy_set_header X-Forwarded-Proto $scheme; # 传递原始协议(http/https)
}
}WebSocket 代理
# WebSocket 代理:支持 ws:// 或 wss:// 协议的实时双向通信
location /ws {
proxy_pass http://localhost:3000; # 转发到后端 WebSocket 服务
proxy_http_version 1.1; # WebSocket 必须使用 HTTP/1.1
proxy_set_header Upgrade $http_upgrade; # 告诉后端要升级协议(HTTP → WebSocket)
proxy_set_header Connection "upgrade"; # 升级连接类型
proxy_set_header Host $host; # 传递原始 Host
}负载均衡
upstream backend {
# 轮询(默认)
server 192.168.1.10:3000;
server 192.168.1.11:3000;
# 权重
server 192.168.1.10:3000 weight=3;
server 192.168.1.11:3000 weight=1;
# ip_hash(同一 IP 固定到同一服务器)
# ip_hash;
# least_conn(最少连接)
# least_conn;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
}
}HTTPS 配置
# HTTPS 服务器配置
server {
listen 443 ssl http2; # 监听 443 端口,启用 SSL 和 HTTP/2
server_name example.com; # 你的域名
# SSL 证书文件(使用 Let's Encrypt 或其他 CA 颁发的证书)
ssl_certificate /etc/nginx/ssl/cert.pem; # 证书文件(PEM 格式,含中间证书)
ssl_certificate_key /etc/nginx/ssl/key.pem; # 私钥文件
# SSL 安全优化
ssl_protocols TLSv1.2 TLSv1.3; # 只允许 TLS 1.2 和 1.3(禁用不安全的旧版本)
ssl_ciphers HIGH:!aNULL:!MD5; # 使用高强度加密套件
ssl_prefer_server_ciphers on; # 优先使用服务器端的加密套件
location / {
root /var/www/html; # 静态文件目录
try_files $uri $uri/ /index.html; # SPA history 路由
}
}
# HTTP 自动跳转到 HTTPS(301 永久重定向)
server {
listen 80; # 监听 80 端口
server_name example.com; # 你的域名
return 301 https://$server_name$request_uri; # 永久重定向到 HTTPS(保留原始路径)
}跨域配置
location /api {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://localhost:3000;
}Vue/React 项目部署
💡 Vue Router 和 React Router 的 history 模式需要 nginx 配置
try_files,将所有未匹配的路由回退到index.html,否则刷新页面会 404。
server {
listen 80; # 监听 80 端口
server_name example.com; # 替换为你的域名
root /var/www/dist; # 构建产物目录
index index.html; # 默认首页
# ---- 前端路由(SPA history 模式)----
# Vue Router / React Router 的 history 模式必须配置此项
# 用户访问 /about、/user/123 等路径时,实际返回 index.html
# 由前端 JS 接管路由,从 URL 解析出对应页面
location / {
try_files $uri $uri/ /index.html; # 先找文件 → 再找目录 → 回退到 index.html
}
# ---- API 反向代理 ----
# 将 /api 开头的请求转发给后端 Node.js/Java/Python 服务
location /api {
proxy_pass http://localhost:3000; # 转发到后端(地址根据实际修改)
proxy_set_header Host $host; # 传递原始 Host 头
proxy_set_header X-Real-IP $remote_addr; # 传递客户端真实 IP
}
# ---- 静态资源长期缓存 ----
# js/css/图片等静态资源文件名通常带 hash(如 app.a1b2c3.js)
# 内容变化时文件名也变,所以可以放心缓存 1 年
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y; # 缓存 1 年
add_header Cache-Control "public, immutable"; # 公共缓存,内容不变
}
# ---- HTML 不缓存 ----
# HTML 文件是入口,必须每次获取最新版本
location ~* \.html$ {
expires -1; # 立即过期
add_header Cache-Control "no-cache, no-store, must-revalidate"; # 不缓存
}
}常见问题
403 Forbidden
# 检查文件权限
ls -la /var/www/html
# 设置正确权限
sudo chown -R nginx:nginx /var/www/html
sudo chmod -R 755 /var/www/html502 Bad Gateway
# 检查后端服务是否运行
curl http://localhost:3000
# 查看错误日志
tail -f /var/log/nginx/error.log配置不生效
# 测试配置
sudo nginx -t
# 重新加载
sudo systemctl reload nginx504 Gateway Timeout
504 表示 nginx 作为反向代理时,在规定时间内未收到后端响应。
# 1. 检查后端服务是否正常
curl -v http://localhost:3000
# 2. 查看错误日志
tail -50 /var/log/nginx/error.log
# 3. 增大代理超时时间location /api/ {
proxy_pass http://backend;
proxy_connect_timeout 60s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
}| 排查方向 | 检查项 |
|---|---|
| 后端服务 | 后端进程是否存活、端口是否监听 |
| 超时配置 | proxy_read_timeout 是否过短 |
| 资源瓶颈 | 后端 CPU/内存/数据库连接池是否耗尽 |
| 慢查询 | 后端是否有慢 SQL 或长时间计算 |
| 网络 | nginx 到后端之间的网络是否通畅 |
HTTPS 混合内容
页面通过 HTTPS 加载,但部分资源(图片、JS、CSS)仍使用 HTTP,浏览器会阻止或警告。
# 方案一:使用 CSP 头强制升级
add_header Content-Security-Policy "upgrade-insecure-requests" always;
# 方案二:用反向代理统一资源路径
location /assets/ {
proxy_pass http://internal-cdn/assets/;
}# 在浏览器控制台检查混合内容
# Chrome: F12 → Console → 筛选 "Mixed Content"
# 或在 Network 面板中查看哪些请求使用了 http://💡 前端代码中避免硬编码
http://,使用//或/开头的相对协议/路径。
自定义 404 页面
server {
listen 80;
server_name example.com;
root /var/www/html;
# 自定义 404 页面
error_page 404 /404.html;
location = /404.html {
internal;
}
# 自定义 50x 页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
internal;
}
}⚠️ 自定义错误页面的 location 必须加
internal指令,防止用户直接访问该页面。
域名重定向
# 旧域名跳转到新域名(301 永久重定向)
server {
listen 80;
server_name old.example.com;
return 301 https://new.example.com$request_uri;
}
# 多个域名统一跳转到主域名
server {
listen 80;
server_name www.example.com example.com;
return 301 https://example.com$request_uri;
}
# 带路径的重定向
server {
listen 80;
server_name old.example.com;
location /blog {
return 301 https://new.example.com/articles;
}
location / {
return 301 https://new.example.com$request_uri;
}
}限制 IP 访问
# 只允许特定 IP 访问
location /admin {
allow 192.168.1.0/24;
allow 10.0.0.1;
deny all;
proxy_pass http://localhost:3000;
}
# 禁止特定 IP 访问
location / {
deny 192.168.1.100;
deny 10.0.0.0/8;
allow all;
proxy_pass http://localhost:3000;
}⚠️ 如果有多层代理,需使用
set_real_ip_from和real_ip_header获取真实客户端 IP,否则限制的是代理服务器的 IP。
# 从 Cloudflare 等 CDN 获取真实 IP
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
real_ip_header CF-Connecting-IP;查看并发连接数
# 启用 stub_status 模块
server {
listen 80;
server_name localhost;
location /nginx_status {
stub_status;
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
}
}# 访问状态页
curl http://127.0.0.1/nginx_status
# 输出示例:
# Active connections: 291
# server accepts handled requests
# 16630948 16630948 31070465
# Reading: 6 Writing: 179 Waiting: 106| 字段 | 说明 |
|---|---|
| Active connections | 当前活跃连接数 |
| accepts | 已接受的总连接数 |
| handled | 已处理的总连接数 |
| requests | 已处理的总请求数 |
| Reading | 正在读取请求头的连接数 |
| Writing | 正在发送响应的连接数 |
| Waiting | 等待请求的空闲长连接数 |
# 统计当前各状态连接数
ss -s
# 或
netstat -ant | awk '{print $6}' | sort | uniq -c | sort -rn