Nginx
本文范围
这篇文章讲 Nginx 作为普通 Linux / 云服务器入口层时的部署运维主线:安装、目录结构、静态资源托管、server 和 location、root / alias / try_files、反向代理、upstream、HTTPS、gzip、缓存、限流、日志、Docker 运行、常见 403 / 404 / 502 / 504 排障和上线模板。
本文不展开 OpenResty / Lua 开发、Nginx C 模块开发、K8S Ingress Controller 全量用法、CDN 厂商能力和四层网关内核调优。需要 K8S、CDN、网关灰度时,本文只讲它们和 Nginx 入口层的边界。
读完以后你应该能把一个前后端分离项目稳定挂到 Nginx 上,知道配置为什么这样写,出错时先看哪条日志、哪条命令、哪段 upstream。
前置条件
建议先准备:
- 一台 Linux 服务器,能使用
sudo安装软件、管理 systemd 服务和查看日志。 - 一个测试域名或能通过
curl -H 'Host: ...'验证的入口 IP。 - 一个静态站点目录,例如
/var/www/app,以及一个后端服务,例如127.0.0.1:8080。 - 云安全组和系统防火墙已能按需放通 80 / 443;证书章节需要准备可用域名和证书申请方式。
- 本文命令以开源 Nginx 为准,使用发行版仓库或官方仓库都可以;生产升级前必须回到当前官方文档确认版本、模块和配置兼容性。
先把边界说清楚
很多同学第一次接触 Nginx,最容易把它理解成“放一个配置文件,然后重启一下”。真到线上就会发现,问题不在这一行 proxy_pass,而在域名有没有解析、安全组有没有放通、静态资源路径是不是拼错、证书链是不是完整、后端端口到底能不能从 Nginx 机器访问。
所以这里不按官方指令表来背概念,我们按一次真实上线的顺序走:先把 Nginx 装起来,再看配置文件放哪儿,然后跑一个静态页面,接着加反向代理、HTTPS、缓存、限流,最后按 403、404、502、504 这些现场故障一步步查。
这篇文章适合三类读者:
- 后端开发:能把前端包、后端服务和域名入口接起来。
- 运维或 DevOps:能把 Nginx 放进发布、验证、回滚和排障流程。
- 架构师或技术负责人:能判断入口层该承担什么,不该承担什么,怎么在生产环境治理配置、证书、安全组、日志和缓存。
这里不展开 K8S Ingress,不展开 OpenResty / Lua 深度开发,也不把 CDN 全套体系拉进来。我们就盯住开源 Nginx 在普通 Linux 服务器、云服务器、Docker 和企业内网里的落地做法。
后面的命令以 Linux 为主。为了方便对照,先约定几个示例条件:
- Ubuntu 22.04 / 24.04,或 RHEL 系、Rocky Linux、AlmaLinux、CentOS Stream。
- Nginx 使用发行版仓库或官方仓库安装。
- Web 入口使用 80 和 443 端口。
- 后端服务示例监听
127.0.0.1:8080或内网地址。
截至 2026-07-10,Nginx 官方下载页显示 stable 版本为 1.30.3,mainline 版本为 1.31.2。这里要注意,生产环境不要只看版本号大小。发行版仓库可能会回补安全补丁,官方仓库可能版本更新更快,但升级前要确认模块、配置语法和回滚方案。没有验证能力时,盲目追新也是事故来源。
先把 Nginx 的执行骨架看清楚
Nginx 不只是一个 nginx.conf 文件。配置文件只是入口,真正运行时会落到 master 进程、worker 进程、事件循环、HTTP 模块、过滤器和 upstream 模块上。理解这层关系以后,再看 worker_connections、location、proxy_pass、gzip、access_log 这些指令,就不会觉得它们只是散落的参数。
这张图要配合后面的配置一起看:main 和 events 影响进程与连接模型,http 里的 server / location 决定请求怎么被匹配,proxy_pass 会把请求交给 upstream,gzip、缓存和日志则属于请求处理链路里的过滤、缓存和观测能力。线上排障时也按这个方向反推:端口不通看监听和事件层,404 看 server / location / 静态路径,502 / 504 看代理和 upstream,日志字段缺失看 log_format。
中国大陆、内网和离线环境先说清
Nginx 是入口层组件,真正上线前不要只问“配置怎么写”。公网、内网、离线环境的限制不一样,先把外部条件确认掉,后面排障会省很多时间。
中国大陆公网
如果网站部署在中国大陆节点,先确认下面几件事。这里不是走流程,而是这些点任何一个没通,Nginx 配得再漂亮也访问不了:
- 域名是否完成 ICP 备案或接入备案。ICP备案不按端口规避,只要域名解析到中国内地服务器,通常都要按云厂商和监管要求处理。
- 云服务器安全组是否放通
80/tcp和443/tcp。 - 操作系统防火墙是否放通
80/tcp和443/tcp。 - 域名 A / CNAME 记录是否解析到正确公网入口。
- 证书申请方式是否能走通。HTTP-01 验证要求 80 端口入站可达;不能开放 80 时,优先考虑 DNS-01。
云服务器上最常见的现象是:Nginx 配置没问题,本机 curl http://127.0.0.1 正常,但公网访问超时。这时不要急着改 nginx.conf,先查安全组、系统防火墙、DNS 和备案/域名接入状态。很多“疑难杂症”,最后只是 80/443 没放通。
企业内网
内网环境通常没有公网域名,或者公网 CA 无法直接验证域名所有权。一般这样处理:
- 内部 DNS 解析业务域名,例如
app.example.internal。 - 使用企业内部 CA 签发证书,把根证书下发到办公终端、服务器、浏览器或系统信任区。
- 如果有公网域名但服务在内网,可以用 DNS-01 申请公网可信证书。
- Nginx 只监听内网网卡地址,安全组或防火墙只允许办公网、网关、负载均衡或上游代理访问。
内网排障重点不是公网连通,而是 DNS、路由、ACL、防火墙、证书信任链和客户端代理。
离线环境
离线环境不要临时在服务器上到处找包。真正交付前,一般需要提前准备好这些东西:
- Nginx 安装包及依赖包,例如
.deb、.rpm。 - 操作系统版本、CPU 架构、Nginx 版本、OpenSSL 版本清单。
- 安装包校验和、签名或内网制品仓库记录。
- 证书文件、私钥文件、内部 CA 根证书和证书续期流程。
- 标准配置模板、回滚配置、静态资源包、后端服务地址清单。
离线部署的核心不是“能装上”,而是下一台机器、下一次验收、下一次续证还能按同样步骤复现。这里要把安装包、校验和、证书和回滚配置当成一个交付包来管。
安装 Nginx
第一步先把 Nginx 跑起来。不要一上来就写复杂反代配置,先确认服务能安装、能启动、能通过 nginx -t,这样后面出问题才能把范围缩小到配置本身。
Ubuntu / Debian
如果使用发行版仓库,命令最简单:
sudo apt update
sudo apt install -y nginx
nginx -v
systemctl status nginx --no-pager执行完以后,systemctl status nginx 里看到 active (running),说明服务已经起来了。如果这里失败,先看包管理器输出和 journalctl -u nginx,不要继续往下写配置。
如果使用 Nginx 官方仓库,先导入官方签名 key,再添加源。以 Ubuntu 为例:
sudo apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
gpg --dry-run --quiet --no-keyring --import --import-options import-show \
/usr/share/keyrings/nginx-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
https://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update
sudo apt install -y nginx安装完以后再验证一次:
nginx -v
nginx -V
sudo nginx -t
systemctl is-enabled nginx
systemctl status nginx --no-pager这里几个命令的作用不一样:nginx -v 看版本,nginx -V 看编译参数和模块,nginx -t 检查配置语法并尝试打开配置里引用的文件。以后每次改配置,至少要先跑 sudo nginx -t。
RHEL / Rocky / AlmaLinux / CentOS Stream
RHEL 系也一样,先用系统仓库把服务跑起来:
sudo dnf install -y nginx
nginx -v
sudo systemctl enable --now nginx
systemctl status nginx --no-pager如果需要使用 Nginx 官方仓库,再创建 repo 文件:
sudo tee /etc/yum.repos.d/nginx.repo >/dev/null <<'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=https://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
[nginx-mainline]
name=nginx mainline repo
baseurl=https://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF
sudo dnf install -y nginx
sudo systemctl enable --now nginx如果系统还是 yum,命令类似:
sudo yum install -y yum-utils
sudo yum install -y nginx防火墙和安全组
服务启动只是第一步,外部访问还要过防火墙。先在系统里放通 Web 端口:
# firewalld
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
# ufw
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status numbered执行完以后看 --list-all 或 ufw status numbered,确认 80 和 443 已经在规则里。云服务器还要在控制台安全组放通入方向:
80/tcp:HTTP、证书 HTTP-01 验证、HTTP 到 HTTPS 跳转。443/tcp:HTTPS。22/tcp:SSH 只允许办公出口、堡垒机或 VPN 来源,不要长期对0.0.0.0/0开放。
这里要注意,安全组和系统防火墙是两层控制。本机能访问,不代表公网能访问;安全组放通,也不代表系统服务已经监听。公网不通时,按“服务监听 -> 系统防火墙 -> 云安全组 -> DNS/备案”这个顺序查,别一上来就怀疑 Nginx。
目录结构与配置文件
装完以后先不要急着改文件。先看清楚 Nginx 到底读哪些配置、日志写哪里、静态资源默认放哪儿。不同安装方式的目录会有差异,但常见路径如下:
/etc/nginx/nginx.conf # 主配置
/etc/nginx/conf.d/*.conf # 站点配置或虚拟主机配置
/etc/nginx/sites-available/ # Debian/Ubuntu 常见站点配置目录
/etc/nginx/sites-enabled/ # Debian/Ubuntu 常见启用目录
/usr/share/nginx/html/ # 默认静态资源目录
/var/log/nginx/access.log # 访问日志
/var/log/nginx/error.log # 错误日志
/var/cache/nginx/ # 缓存目录,按配置决定
/run/nginx.pid # 运行时 PID,按发行版可能不同先看主配置和最终生效配置:
sudo nginx -T | lessnginx -T 会先测试配置,再把最终加载的配置全部输出。排查 include、重复 server、旧配置残留时,它比只看某个文件可靠。执行后如果直接报错,就先根据报错里的文件名和行号修,不要 reload。
一个简化的主配置结构如下:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/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" "$http_x_forwarded_for" '
'rt=$request_time uct=$upstream_connect_time '
'urt=$upstream_response_time uaddr=$upstream_addr';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}配置上下文先看层级
Nginx 指令不是想写哪儿就写哪儿。worker_processes 写在全局层,worker_connections 写在 events,server 和 upstream 通常写在 http 里,location 写在 server 里。很多配置不生效,根因不是值错,而是写错了上下文。
这里特别容易误解的是 upstream。它不是某个 server 的子配置,而是 http 上下文里的后端池定义;真正把请求转过去的是 location 里的 proxy_pass http://app_backend。所以排查 502 时,不要只盯 location,还要看同一个 http 上下文下有没有对应的 upstream、节点是否可达、DNS 是否能解析。
一般建议这样分层:
nginx.conf只放全局、事件、HTTP 公共配置。- 每个域名或应用一个独立
.conf。 - 公共片段可以放到
snippets或includes目录,再用include引入。 - 生产环境不要在一个巨大文件里混所有域名、证书、缓存和转发规则。
配置拆分可以按下面这个落点理解:
团队里最好约定:主配置只放公共能力;业务域名、upstream、证书路径和缓存策略分文件管理;公共片段变更要按影响面 review,因为一个 snippet 可能被很多站点引用。每次改完都用 sudo nginx -T 看最终配置,确认 include 真的加载进来了。
启停、检查和 reload
Nginx 日常操作不复杂,但线上最怕“改完直接 restart”。先把常用命令记住:
nginx -v
nginx -V
sudo nginx -t
sudo nginx -T
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx
sudo systemctl status nginx --no-pager
sudo nginx -s reload
sudo nginx -s reopen生产配置变更建议固定成下面这个流程:
sudo cp /etc/nginx/conf.d/app.conf /etc/nginx/conf.d/app.conf.bak.$(date +%Y%m%d%H%M%S)
sudo nginx -t
sudo systemctl reload nginx
curl -I http://127.0.0.1/
curl -I https://example.com/
tail -n 50 /var/log/nginx/error.log这里要注意,不要把 restart 当作默认动作。reload 会让 master 进程校验新配置,成功后启动新 worker,并让旧 worker 优雅退出;如果新配置无法应用,会继续使用旧配置。restart 会直接重启服务,配置错误时可能把入口打断。
日志切割后让 Nginx 重新打开日志文件:
sudo nginx -s reopen最小可运行静态站点
先做一个最小站点。这个动作的目的不是为了展示 HTML,而是验证 Nginx 能读配置、能读静态目录、80 端口能返回响应。
创建站点目录:
sudo mkdir -p /var/www/app
echo '<h1>Nginx OK</h1>' | sudo tee /var/www/app/index.html如果没有目录就新建,如果已有业务目录,就换成你自己的路径。然后创建配置:
sudo tee /etc/nginx/conf.d/app.conf >/dev/null <<'EOF'
server {
listen 80;
server_name example.com;
root /var/www/app;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
EOF检查并加载:
sudo nginx -t
sudo systemctl reload nginx
curl -I http://127.0.0.1/nginx -t 看到 syntax is ok 和 test is successful,再 reload。curl -I 正常应该看到 HTTP/1.1 200 OK 或发行版默认返回的 200/301。如果域名还没解析,可以本机指定 Host 验证:
curl -I -H 'Host: example.com' http://127.0.0.1/如果本机正常、公网不通,按这个顺序查:
ss -lntp | grep ':80'
sudo firewall-cmd --list-all
curl -I http://服务器公网IP/
dig +short example.comss 能看到 80 监听,curl 本机也通,但公网 IP 不通时,再去云控制台查安全组和备案/域名接入状态。这个顺序很重要,能避免在配置文件里来回乱改。
server 怎么选中
你可以把 server 理解成“一个入口规则”。同一台机器上可以跑多个域名,Nginx 收到请求后,先按 listen 的地址和端口找候选,再按 server_name 匹配域名。如果没有匹配到域名,就用这个地址端口上的默认 server。
示例:
server {
listen 80 default_server;
server_name _;
return 444;
}
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example;
}这里要注意,生产环境建议显式写一个默认 server,处理未知 Host,避免乱七八糟的 Host 请求落到业务站点:
server {
listen 80 default_server;
server_name _;
return 404;
}如果访问结果不符合预期,先用下面的命令指定 Host 测一下:
curl -I -H 'Host: example.com' http://127.0.0.1/
curl -I -H 'Host: unknown.example.com' http://127.0.0.1/第一个应该进入业务 server,第二个应该落到默认 server。如果是 HTTPS,还要考虑 SNI。多个 HTTPS 域名共用一个 IP 时,客户端通过 SNI 告诉服务器自己访问哪个域名,Nginx 才能选择对应证书。
location 匹配规则
location 决定一个 URI 由哪段配置处理。这里不要先背规则,先看几种最常用的写法:
location = /health {
return 200 "ok\n";
}
location ^~ /static/ {
root /var/www/app;
}
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
}
location / {
try_files $uri $uri/ /index.html;
}常用优先级可以这样记,排障时按这个顺序往下对:
=精确匹配优先级最高。- 普通前缀匹配会先找最长前缀。
^~命中后不再进入正则匹配。~区分大小写正则,~*不区分大小写正则。- 如果正则没有命中,使用最长前缀匹配结果。
location 优先级决策图
这组规则用文字背很容易混。排查时可以按下面这个决策链走:
排查 location 命中问题时,不要只看肉眼感觉。用不同路径跑:
curl -I http://example.com/
curl -I http://example.com/static/app.js
curl -I http://example.com/api/users
curl -I http://example.com/health访问后应该看到不同路径返回不同状态和响应头。再配合 access log 里的 $request_uri、$status、$request_time、$upstream_addr,就能判断请求最终走到静态资源、反向代理还是健康检查。
Nginx 入口层请求处理链路
location 只是入口处理链路里的一个决策点。排查 404、403、502、静态资源路径错误时,先确认请求是否进了正确的 server,再看命中了哪个 location。
这张图的排查顺序也对应日志观察顺序:先看请求有没有进 Nginx,再看落到哪个虚拟主机和路径规则,最后判断问题在静态文件、代理转发还是后端服务。
root、alias 和 try_files
这是 Nginx 静态资源里最容易出错的一组。很多 404 不是文件不存在,而是你以为 Nginx 去找 A 路径,实际上它拼到了 B 路径。
root
root 是把配置的目录和请求 URI 拼起来。比如下面这个配置:
location /static/ {
root /var/www/app;
}请求:
/static/app.js实际文件:
/var/www/app/static/app.jsalias
alias 是用配置的目录替换掉 location 前缀。它不是拼接,而是替换:
location /static/ {
alias /var/www/app/assets/;
}请求:
/static/app.js实际文件:
/var/www/app/assets/app.js这里要注意两个细节:
location /static/搭配alias /path/时,结尾斜杠最好保持一致。alias下用try_files时要特别小心路径关系,不要把root的思路套过来。
用图看会更直观:
所以遇到静态资源 404 时,不要先怀疑浏览器缓存。先把请求 URI、命中的 location、root 或 alias 配置和实际文件路径逐个写出来,通常很快就能看出路径是多拼了一段,还是替换时少了一层目录。
try_files
try_files 按顺序检查文件是否存在,命中就处理,否则走最后一个参数。它特别适合做“静态文件优先,找不到再兜底”的场景。
普通静态站点:
location / {
root /var/www/app;
try_files $uri $uri/ =404;
}SPA 前端站点:
location / {
root /var/www/app;
try_files $uri $uri/ /index.html;
}静态优先,缺失时转后端:
location / {
root /var/www/app;
try_files $uri $uri/ @backend;
}
location @backend {
proxy_pass http://127.0.0.1:8080;
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;
}遇到问题时可以这样判断:
- 浏览器刷新前端路由返回 404:SPA 缺少
/index.html兜底。 - 静态文件 404:
root拼接结果和实际文件路径不一致。 - 静态文件 403:目录没有执行权限、文件没有读权限、SELinux 拦截、没有 index 且禁止目录列表。
前端静态资源部署
前端单页应用上线时,一般会遇到两个问题:刷新路由 404,以及用户拿到旧的 JS/CSS。下面这个配置可以直接作为起点:
server {
listen 80;
server_name example.com;
root /var/www/app;
index index.html;
location = /index.html {
add_header Cache-Control "no-cache";
}
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(?:css|js|mjs|png|jpg|jpeg|gif|ico|svg|webp|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
try_files $uri =404;
}
}访问后应该看到:
curl -I http://example.com/index.html
curl -I http://example.com/static/app.jsindex.html 应该是 no-cache,带 hash 的静态资源应该是长缓存。如果这两个反了,发布后很容易出现“页面引用旧资源”或者“资源缓存不住”的问题。
前端资源常见发布约束:
- 构建产物里的 JS/CSS 文件名要带 hash,才能长缓存。
index.html不要长缓存,否则用户可能长期拿到旧资源引用。- 资源包替换后先
nginx -t,再reload,最后用curl验证关键页面和静态文件。 - 发布前保留上一个版本目录或压缩包,回滚时能快速切换。
发布脚本里至少要有这些动作。这里用软链接切版本,失败时也方便回滚:
set -euo pipefail
release_dir="/var/www/releases/app-$(date +%Y%m%d%H%M%S)"
current_link="/var/www/app"
sudo mkdir -p "$release_dir"
sudo tar -zxf dist.tar.gz -C "$release_dir"
sudo ln -sfn "$release_dir" "$current_link"
sudo nginx -t
sudo systemctl reload nginx
curl -fsS -I http://127.0.0.1/ >/dev/null
curl -fsS -I http://127.0.0.1/index.html >/dev/null如果使用软链接切换版本,注意 Nginx worker 用户必须有权限访问真实目录的每一级父目录。访问 403 时,先用 namei -l /var/www/app/index.html 看每一级目录权限。
gzip 压缩
gzip 适合压缩文本资源,例如 HTML、CSS、JS、JSON、XML。图片、视频、压缩包通常已经压缩过,再 gzip 收益有限,还会浪费 CPU。它解决的是“文本资源传输太大”的问题,不是解决后端接口慢。
基础配置可以先这样写:
http {
gzip on;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_vary on;
gzip_types
text/plain
text/css
text/xml
application/json
application/javascript
application/xml
image/svg+xml;
}配置完不要凭感觉,直接验证:
curl -H 'Accept-Encoding: gzip' -I http://example.com/static/app.js看响应头里有没有:
Content-Encoding: gzip
Vary: Accept-Encoding生产里注意:
- 压缩级别不是越高越好,级别太高会增加 CPU 消耗。
- 对 HTTPS 下包含敏感反射内容的动态响应,不要无脑开启压缩,避免压缩侧信道风险。
- 如果前端构建已经生成
.gz文件,可以进一步评估gzip_static,但要确认模块是否可用。
反向代理
反向代理的典型场景是:后端服务只监听内网或本机端口,公网只暴露 Nginx。先写一个最小配置:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}访问后先看:
curl -I http://api.example.com/
tail -n 50 /var/log/nginx/error.log正常情况下,curl 应该拿到后端返回的状态码。如果是 502,先不要改一堆 header,先确认 127.0.0.1:8080 这个后端地址从 Nginx 所在机器上能不能访问。
proxy_set_header 的目的:
Host:让后端知道原始域名。X-Real-IP:传递客户端 IP。X-Forwarded-For:追加代理链路上的客户端 IP。X-Forwarded-Proto:告诉后端原始协议是 HTTP 还是 HTTPS。
如果后端需要生成回调地址、跳转地址、OAuth 地址,Host 和 X-Forwarded-Proto 很关键。少了它们,常见现象是登录跳回 HTTP、回调域名变成内网地址、网关后面拿不到真实协议。
proxy_pass 的斜杠问题
这是反向代理里最高频的坑。很多接口 404,不是后端没接口,而是 Nginx 把路径转发变了。
配置一:
location /api/ {
proxy_pass http://127.0.0.1:8080/;
}请求:
/api/users转发到后端:
/users配置二:
location /api/ {
proxy_pass http://127.0.0.1:8080;
}请求:
/api/users转发到后端:
/api/users建议团队统一约定:如果后端服务本身就以 /api 为上下文路径,proxy_pass 不带尾部 URI;如果希望 Nginx 去掉 /api/ 前缀,proxy_pass 写成带 / 的形式,并在配置注释里写清楚。改完以后用一个真实接口验证,不要只看首页。
超时与缓冲
接口偶发慢、上传下载大文件、SSE 或 WebSocket,都可能涉及超时和缓冲。常用配置如下:
location /api/ {
proxy_pass http://backend;
proxy_connect_timeout 3s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 16 16k;
proxy_busy_buffers_size 64k;
}这些参数解决的问题不一样:
proxy_connect_timeout:Nginx 连接后端的超时时间。proxy_send_timeout:向后端发送请求的超时时间。proxy_read_timeout:等待后端响应的超时时间。proxy_buffering:是否缓冲后端响应。
接口慢导致 504 时,不要第一反应就是把 proxy_read_timeout 加到几百秒。先查后端为什么慢、数据库是否慢、线程池是否耗尽、是否存在大文件或长轮询。如果确实是大文件下载、SSE、WebSocket,再按场景单独调,别全站放大超时。
WebSocket
WebSocket 需要升级连接,不加这些 header,经常表现为“连上马上断”:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name ws.example.com;
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 $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 3600s;
}
}如果 WebSocket 一连就断,优先查:
curl -i -N \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Host: ws.example.com" \
http://127.0.0.1/ws/实际项目更常用浏览器开发者工具、后端日志和 Nginx error log 联合排查。这里要注意,curl 只能帮你确认升级请求有没有打到入口,不能替代完整 WebSocket 客户端测试。
upstream 与负载均衡
当一个后端节点扛不住,或者需要发布时逐台摘挂节点,就会用到 upstream。它做的事情很直接:给一组后端起个名字,然后在 proxy_pass 里引用这个名字。
upstream app_backend {
server app1.internal.example:8080 weight=3 max_fails=3 fail_timeout=10s;
server app2.internal.example:8080 weight=1 max_fails=3 fail_timeout=10s;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app_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;
}
}这条链路可以这样读:
日志里一定要带 upstream 信息,否则 502 / 504 发生时你只能看到“入口失败”,分不清是 Nginx 连不上后端、后端拒绝连接、后端太慢,还是某个节点单独异常。
常用负载方式如下。先不要上来就追求复杂算法,普通无状态服务默认轮询通常就够用:
# 默认:轮询,可配合 weight
upstream backend_round_robin {
server app1.internal.example:8080 weight=2;
server app2.internal.example:8080 weight=1;
}
# 最少连接
upstream backend_least_conn {
least_conn;
server app1.internal.example:8080;
server app2.internal.example:8080;
}
# 按客户端 IP 粘滞
upstream backend_ip_hash {
ip_hash;
server app1.internal.example:8080;
server app2.internal.example:8080;
}
# 按 key hash,适合缓存类后端或固定路由
upstream backend_hash {
hash $request_uri consistent;
server app1.internal.example:8080;
server app2.internal.example:8080;
}生产里一般这样判断:
- 普通无状态服务:默认轮询加权重就够用。
- 长连接或请求耗时差异大:考虑
least_conn。 - 服务依赖本地会话:优先改造成无状态;短期可用
ip_hash兜底,但 NAT、代理和移动网络会让效果打折。 - 缓存命中敏感:考虑一致性 hash。
如果发布时要临时摘除一个节点,可以先这样标记:
upstream app_backend {
server app1.internal.example:8080;
server app2.internal.example:8080 down;
}upstream 失败摘挂要按窗口理解:
这里要注意:开源 Nginx 的主动健康检查能力有限,常见做法是依赖被动失败判断、发布系统摘挂节点、云负载均衡健康检查,或者用服务注册发现体系治理。不要以为写了 upstream 就自动拥有完整的服务治理能力。
HTTPS 和证书
证书文件
HTTPS 最容易把人卡住的地方不是配置,而是证书文件到底哪个给 Nginx、私钥和证书是否匹配、证书链是否完整。Nginx 常用两个文件:
- 证书链文件:给
ssl_certificate。 - 私钥文件:给
ssl_certificate_key。
Let's Encrypt 通常使用:
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;云厂商证书通常会下载到适配 Nginx 的证书包,里面包含类似 .crt、.pem、.key 的文件。以实际下载说明为准,关键是三件事:证书链完整、私钥匹配、Nginx master 进程可读。
先检查证书和私钥是否匹配。两条命令输出的 md5 一样,才说明它们是一对:
openssl x509 -noout -modulus -in fullchain.pem | openssl md5
openssl rsa -noout -modulus -in privkey.pem | openssl md5再检查证书有效期:
openssl x509 -in fullchain.pem -noout -subject -issuer -dates最后远程检查 HTTPS:
openssl s_client -connect example.com:443 -servername example.com </dev/null
curl -Iv https://example.com/Nginx HTTPS 配置
基础配置如下,第一段负责 HTTP 跳 HTTPS,第二段负责真正的 HTTPS 服务:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/certs/example.com.fullchain.pem;
ssl_certificate_key /etc/nginx/certs/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root /var/www/app;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}这里要注意:
- 新版本不要写
ssl on;,使用listen 443 ssl;。 - 新版本不要继续依赖
listen 443 ssl http2;,优先使用http2 on;。 - 私钥权限要收紧,例如
600或640,不要放到静态资源目录。 - 证书链不完整时,部分客户端会报
unable to get local issuer certificate。
证书申请方式
证书申请一般有这几种方式:
- 云厂商 SSL 证书:适合中国大陆云服务器、控制台托管、手工下载或自动部署。
- Let's Encrypt HTTP-01:适合公网 80 可达的普通网站。
- Let's Encrypt DNS-01:适合通配符证书、内网服务、80 端口不可达、多个入口节点。
- 企业内部 CA:适合完全内网、离线、专有终端。
Let's Encrypt 的 HTTP-01 验证只能使用 80 端口。实践上建议 80 端口保持开放并跳转到 HTTPS,这不只方便证书续期,也能给误输入 http:// 的用户正常升级到 HTTPS。如果企业安全策略不允许开放 80,就提前走 DNS-01,别等续期失败后再补救。
缓存
Nginx 缓存分两类:浏览器缓存和反向代理缓存。前者让浏览器少下载静态文件,后者让 Nginx 少打后端。它们解决的问题不同,千万不要把接口缓存和静态资源缓存混在一起想。
浏览器缓存
浏览器缓存适合带 hash 的静态资源:
location ~* \.(?:css|js|mjs|png|jpg|jpeg|gif|ico|svg|webp|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
try_files $uri =404;
}
location = /index.html {
add_header Cache-Control "no-cache";
}判断规则很简单:
- 带 hash 的静态资源可以长缓存。
index.html、接口响应、权限相关页面不要随便长缓存。- 缓存策略必须和发布策略配套,否则用户会拿到旧页面或旧资源。
改完以后直接看响应头:
curl -I http://example.com/static/app.abc123.js
curl -I http://example.com/index.htmlproxy_cache
反向代理缓存适合读多写少、响应可复用、后端压力大的场景。例如公开配置、非登录态列表、图片缩略图、公共资源接口。它不是万能加速器,用户态接口不要随便缓存。
proxy_cache_path /var/cache/nginx/app
levels=1:2
keys_zone=app_cache:100m
max_size=5g
inactive=60m
use_temp_path=off;
server {
listen 80;
server_name api.example.com;
location /public/ {
proxy_pass http://app_backend;
proxy_cache app_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_lock on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status always;
}
}验证时连续请求两次:
curl -I http://api.example.com/public/config
curl -I http://api.example.com/public/config第一次一般是 MISS,第二次命中后应该看到 HIT:
X-Cache-Status: MISS
X-Cache-Status: HIT生产里要注意:
- 登录态、用户态、权限相关接口默认不缓存。
- 有
Authorization、用户 Cookie、个性化响应时必须谨慎。 - 缓存目录要单独规划容量,避免占满根分区。
- 缓存命中率、后端响应时间、缓存目录大小要纳入监控。
限流
Nginx 的 limit_req 用漏桶算法限制请求处理速率。它适合入口层粗粒度保护,比如登录、短信、搜索、导出这些容易被打爆的接口。但它不替代业务层风控、验证码、登录保护和用户级限流。
先看一个基础配置:
http {
limit_req_zone $binary_remote_addr zone=per_ip:10m rate=10r/s;
server {
listen 80;
server_name api.example.com;
location /api/ {
limit_req zone=per_ip burst=20 nodelay;
limit_req_status 429;
proxy_pass http://app_backend;
}
}
}参数含义如下:
$binary_remote_addr:按客户端 IP 作为 key,比字符串 IP 更节省空间。zone=per_ip:10m:共享内存区域名称和大小。rate=10r/s:平均每秒 10 个请求。burst=20:允许短时突发。nodelay:突发请求不排队延迟,超过容量直接拒绝。limit_req_status 429:被限流时返回 429,比默认 503 更符合语义。
上线时建议这样做:
- 先在测试环境用压测验证。
- 先对高风险接口加,例如登录、短信、搜索、导出。
- 办公网、网关、反向代理多层转发时,要先处理真实客户端 IP,否则所有请求可能都被识别为同一个代理 IP。
- 观察 error log 中限流日志,确认不是误伤正常流量。
- 先小范围打开,确认 429 分布正常后再扩大范围。
日志
Nginx 排障主要看两类日志。access log 看“谁访问了什么,状态码和耗时是多少”;error log 看“为什么失败”:
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log如果有反向代理,建议 access log 增加 upstream 信息。不加这些字段,502/504 时你很难分清是 Nginx 慢还是后端慢:
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'host=$host uri=$request_uri '
'rt=$request_time '
'uaddr=$upstream_addr '
'ustatus=$upstream_status '
'uct=$upstream_connect_time '
'uht=$upstream_header_time '
'urt=$upstream_response_time';
access_log /var/log/nginx/access.log main;常用分析命令如下。现场排障时不要直接 cat 大文件,先用这些命令把范围缩小:
# 看状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
# 看 5xx 请求
awk '($9 ~ /^5/)' /var/log/nginx/access.log | tail -n 50
# 看访问最多的 URI
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
# 看错误日志
grep -iE 'error|crit|upstream|permission|denied|timed out|refused' /var/log/nginx/error.log | tail -n 100如果日志太多,用 tail、grep、awk、less,必要时按日期切割和归档。生产上还要把 5xx、耗时、证书过期、磁盘使用率接入告警,不要等用户截图才知道入口坏了。
Docker 里运行 Nginx
Docker 里跑 Nginx 很适合本地验证配置,但边界一定要分清:容器内的文件系统、网络和宿主机不是一回事。先跑一个静态站点:
docker run -d --name nginx-demo \
-p 8080:80 \
-v "$PWD/html:/usr/share/nginx/html:ro" \
nginx:1.30.3访问 http://127.0.0.1:8080/,应该能看到挂载目录里的页面。如果看不到,先查容器是否启动、端口是否映射、html 目录是否真的有 index.html。
挂载配置:
docker run -d --name nginx-demo \
-p 8080:80 \
-v "$PWD/conf.d:/etc/nginx/conf.d:ro" \
-v "$PWD/html:/usr/share/nginx/html:ro" \
-v "$PWD/logs:/var/log/nginx" \
nginx:1.30.3挂载配置后,检查容器内 Nginx 读到的到底是什么:
docker exec nginx-demo nginx -t
docker exec nginx-demo nginx -T
docker logs nginx-demoDocker 边界要记牢:
- 容器里的
127.0.0.1是容器自己,不是宿主机。 - Nginx 容器要访问另一个容器,优先使用同一个 Docker network 下的服务名。
- Nginx 容器访问宿主机服务,在 Docker Desktop 可用
host.docker.internal;Linux 上要按网桥网关、host 网络或显式地址处理。 - 配置和静态资源用 bind mount 或镜像构建固化,不建议进容器手改后
docker commit作为生产流程。 - 生产镜像固定版本,不使用裸
latest。 - 中国大陆或企业内网环境,不要把生产发布建立在 Docker Hub 直连上;一般需要提前同步到云厂商镜像仓库、企业 Harbor 或离线镜像包。
Compose 示例:
services:
nginx:
image: nginx:1.30.3
ports:
- "80:80"
- "443:443"
volumes:
- ./conf.d:/etc/nginx/conf.d:ro
- ./html:/usr/share/nginx/html:ro
- ./certs:/etc/nginx/certs:ro
- ./logs:/var/log/nginx
depends_on:
- app
app:
image: registry.example.com/team/app:1.0.0
expose:
- "8080"对应代理:
upstream app_backend {
server app:8080;
}
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://app_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;
}
}常见故障排查
先别急着改配置。入口层故障要按现象分流:访问不到、访问到了但返回错误、返回慢、HTTPS 报错,排查入口完全不同。
这个图不是替代排障步骤,而是帮你决定第一刀切在哪里。比如 404 优先看路径匹配,别先重启后端;502 优先看 upstream 和后端端口,别先改 root;公网超时优先看安全组和监听,别在 location 里来回试。
403 Forbidden
403 的意思是“请求进来了,但 Nginx 不让访问”。先看现象:
403 Forbidden常见原因一般有这些:
- 文件或目录权限不够。
- 父目录没有执行权限。
- SELinux 拦截。
- 目录没有
index.html,且未开启目录列表。 allow/deny、return 403、限流或安全规则命中。
排查时按路径权限、worker 用户、错误日志三步走:
namei -l /var/www/app/index.html
ls -ld /var /var/www /var/www/app
ls -l /var/www/app/index.html
ps -eo user,group,comm | grep nginx
tail -n 100 /var/log/nginx/error.log如果是 SELinux 环境,再看:
getenforce
sudo ausearch -m avc -ts recent
sudo restorecon -Rv /var/www/app修复方向:
sudo chown -R root:nginx /var/www/app
sudo find /var/www/app -type d -exec chmod 755 {} \;
sudo find /var/www/app -type f -exec chmod 644 {} \;
sudo nginx -t && sudo systemctl reload nginx修完以后验证:
curl -I http://example.com/
tail -n 20 /var/log/nginx/error.log404 Not Found
404 的意思是“没找到资源”。这里要先分清:是 Nginx 没找到静态文件,还是路径被代理到后端以后后端返回 404。常见原因:
root拼接结果不对。alias替换路径不对。- SPA 没有
try_files ... /index.html。 location被正则或更长前缀抢走。- 反向代理时
/api前缀被错误保留或错误去掉。
排查:
sudo nginx -T | sed -n '/server_name example.com/,/}/p'
curl -I http://example.com/static/app.js
curl -I http://example.com/some/front/route
tail -n 50 /var/log/nginx/access.log
tail -n 50 /var/log/nginx/error.log如果是前端路由刷新 404:
location / {
try_files $uri $uri/ /index.html;
}如果是静态目录:
sudo -u nginx test -r /var/www/app/static/app.js && echo ok有些发行版的 worker 用户是 www-data 或 nginx,以实际 ps 输出为准。这里不要照抄用户名,先查再改权限。
502 Bad Gateway
502 代表 Nginx 作为代理时没有从后端拿到有效响应。读者现场最容易慌,其实排查顺序很固定:先证明后端活着,再证明 Nginx 能连到后端,最后再看代理配置。常见原因:
- 后端服务没启动。
- 后端端口写错。
- 后端只监听
127.0.0.1,但 Nginx 在另一个容器或另一台机器。 - DNS 解析失败。
- 后端连接被拒绝、连接重置。
- upstream 没有可用节点。
- HTTPS 代理到后端时 SNI、证书或协议不匹配。
排查顺序:
tail -n 100 /var/log/nginx/error.log
grep -iE 'connect\\(\\) failed|connection refused|upstream prematurely|no live upstreams|host not found' /var/log/nginx/error.log | tail -n 50
ss -lntp | grep 8080
curl -I http://127.0.0.1:8080/health
curl -I http://app1.internal.example:8080/health
sudo nginx -T | grep -nE 'upstream|proxy_pass|server 'Docker 场景额外查:
docker ps
docker logs app
docker exec nginx-demo getent hosts app
docker exec nginx-demo curl -I http://app:8080/health修复方向:
- 先让后端健康检查能直接访问。
- 再让 Nginx 所在机器或容器能访问后端。
- 最后修
proxy_pass和upstream。
504 Gateway Timeout
504 代表 Nginx 等后端响应超时。它和 502 不一样,504 通常说明连接上了,但后端太久没给结果。常见原因:
- 后端接口慢。
- 数据库或外部依赖慢。
- 后端线程池、连接池耗尽。
- Nginx
proxy_read_timeout太短。 - 大文件上传下载、导出任务、长轮询没有单独配置。
排查:
tail -n 100 /var/log/nginx/error.log
grep -i 'upstream timed out' /var/log/nginx/error.log | tail -n 50
awk '($9 == 504)' /var/log/nginx/access.log | tail -n 50
curl -w 'time_connect=%{time_connect} time_starttransfer=%{time_starttransfer} time_total=%{time_total}\n' \
-o /dev/null -s http://127.0.0.1:8080/slow-api修复判断:
- 如果后端直接访问也慢,先修后端。
- 如果只有经过 Nginx 慢,查代理缓冲、超时、网络链路。
- 如果是导出、大文件或长连接,给单独 location 配置更长超时,不要全站放大。
示例:
location /api/export/ {
proxy_pass http://app_backend;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}HTTPS 访问失败
HTTPS 失败先别急着重签证书,先把现象分清:
- 浏览器提示证书不受信任。
- 浏览器提示证书过期。
curl报证书链错误。https://超时。- HTTP 能访问,HTTPS 不能访问。
排查:
ss -lntp | grep ':443'
sudo nginx -t
tail -n 100 /var/log/nginx/error.log
openssl s_client -connect example.com:443 -servername example.com </dev/null
openssl x509 -in /etc/nginx/certs/example.com.fullchain.pem -noout -dates -subject -issuer
curl -Iv https://example.com/判断:
- 443 超时:优先查安全组、防火墙、Nginx listen。
- 证书域名不匹配:查
server_name、SNI、证书 CN/SAN。 - 证书链不完整:使用完整链文件。
- 私钥不匹配:重新下载或重新签发证书。
- 自动续期失败:查 ACME 客户端日志、DNS API 权限、80 端口入站或 DNS-01 记录。
生产化建议
配置治理
Nginx 配置不要靠“服务器上手工改一下”长期维护。生产环境至少守住这些规则:
- 站点配置按域名或应用拆分。
- 每次改配置都走版本管理、评审、备份、
nginx -t、reload、验证。 - 不在生产服务器上手工改完不记录。
- 敏感配置、证书、私钥不进公开仓库。
nginx -T输出可用于排查,但不要直接发到外部群里,里面可能包含域名、路径和证书配置。
权限治理
- Nginx worker 使用低权限用户。
- 静态资源只读发布。
- 证书私钥最小权限。
- SSH 不对全网开放。
- 后端服务端口只对 Nginx 或内网开放,不直接暴露公网。
日志治理
- access log 加上 upstream 耗时字段。
- error log 保持
warn或按场景调整。 - 配置 logrotate,避免日志占满磁盘。
- 关键入口日志接入集中采集。
- 5xx、证书过期、磁盘使用率、Nginx 进程状态纳入告警。
发布和回滚
发布前先确认配置能过、站点能在本机按 Host 命中:
nginx -t
curl -I -H 'Host: example.com' http://127.0.0.1/发布后再从公网入口验证:
systemctl status nginx --no-pager
curl -I https://example.com/
curl -I https://example.com/static/app.js
curl -I https://example.com/api/health
tail -n 50 /var/log/nginx/error.log回滚不要临时想,提前准备好:
- 配置回滚:恢复上一份
.conf,nginx -t && reload。 - 静态资源回滚:软链接切回上一版本目录,
reload或直接验证。 - 证书回滚:恢复旧证书文件前确认未过期、域名匹配。
- 后端回滚:先确认 upstream 指向和健康检查,再切流量。
落地工程深水区
Nginx 在生产里常常是所有请求的第一层入口。它配置简单,但真正拖垮方案的往往是配置治理、证书、日志、缓存、限流和团队协作。
| 深水问题 | 现场表现 | 落地约束 |
|---|---|---|
| 配置散落 | 多个 include 文件里重复 server_name,实际命中的不是你改的那份 | 变更前后都执行 nginx -T,配置放进版本库,禁止直接在生产机临时编辑后不回收 |
| reload 风险 | nginx -s reload 后旧 worker 还在处理请求,新配置部分生效,排障混乱 | 先 nginx -t,再 reload;高风险变更先灰度入口或只切一小部分域名 |
| upstream 容量不匹配 | Nginx 并发上来了,后端连接池、线程池、数据库扛不住 | upstream、应用线程池、连接池、超时、限流阈值一起压测,不能只调 Nginx |
| 缓存误伤 | index.html 被长缓存,用户一直拿旧入口;API 被意外缓存 | HTML 短缓存,hash 静态资源长缓存,API 默认不缓存;CDN 和 Nginx 缓存策略一起评审 |
| 证书续期断链 | 证书文件已更新但 Nginx 没 reload,或证书链不完整 | 续期脚本必须执行 nginx -t && systemctl reload nginx,并用 openssl s_client 验证线上证书 |
| 日志爆盘 | access log 长期不轮转,磁盘满后 Nginx 或应用异常 | logrotate、日志采集、保留周期和磁盘告警必须上线前配置 |
| 限流误杀 | 全站统一按 IP 限流,办公网、NAT 网关、CDN 节点被误伤 | 限流按接口和真实客户端 IP 设计,确认 X-Forwarded-For 可信边界,不把 CDN 节点当用户 |
| 安全边界不清 | Nginx 既做入口、又放管理接口、又代理内网高危端口 | 公网 server 只暴露业务入口,管理接口走内网或堡垒机;默认 server 返回固定拒绝响应 |
团队落地时,Nginx 配置最好按域名、业务、环境分目录管理,并要求每次变更附带验证命令和回滚文件。入口层越靠前,越不能靠“重启试试”。
常用配置模板
下面几段模板不是让你复制完就直接上生产,而是给你一个可靠起点。真正落地时,要把域名、证书路径、后端地址、缓存策略和限流阈值换成自己的,并且先 nginx -t,再灰度验证。
前后端分离模板
upstream app_backend {
server 127.0.0.1:8080 max_fails=3 fail_timeout=10s;
}
server {
listen 80;
server_name example.com;
root /var/www/app;
index index.html;
location = /index.html {
add_header Cache-Control "no-cache";
}
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://app_backend;
proxy_connect_timeout 3s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
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 ~* \.(?:css|js|mjs|png|jpg|jpeg|gif|ico|svg|webp|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
try_files $uri =404;
}
}HTTPS 模板
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/certs/example.com.fullchain.pem;
ssl_certificate_key /etc/nginx/certs/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root /var/www/app;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}限流模板
limit_req_zone $binary_remote_addr zone=login_ip:10m rate=5r/m;
server {
listen 80;
server_name api.example.com;
location /api/login {
limit_req zone=login_ip burst=5 nodelay;
limit_req_status 429;
proxy_pass http://app_backend;
}
}上线检查清单
上线前最后再按清单走一遍。很多线上问题不是技术点不会,而是少做了一步验证。
发布前检查:
- 域名解析正确。
- 安全组放通 80/443,SSH 限定来源。
- 系统防火墙放通 80/443。
nginx -t通过。nginx -T中没有旧 server 抢占域名。- 静态资源目录权限正确。
- 后端健康检查从 Nginx 所在机器可访问。
- HTTPS 证书未过期、域名匹配、证书链完整。
- access log 和 error log 可写。
- 回滚配置和上一版静态资源可用。
上线后验证:
curl -I http://example.com/
curl -I https://example.com/
curl -I https://example.com/index.html
curl -I https://example.com/static/app.js
curl -I https://example.com/api/health
tail -n 100 /var/log/nginx/access.log
tail -n 100 /var/log/nginx/error.log故障时先看:
- 状态码:403、404、502、504 分别走不同路径。
- error log:权限、文件不存在、连接失败、超时。
- access log:请求 URI、上游地址、上游状态码、耗时。
ss -lntp:端口是否监听。curl:从本机、内网、公网分别验证。- 安全组、防火墙、DNS、证书。
常用命令速查
# 版本和模块
nginx -v
nginx -V
# 配置检查和完整配置输出
sudo nginx -t
sudo nginx -T
# 服务管理
sudo systemctl status nginx --no-pager
sudo systemctl reload nginx
sudo systemctl restart nginx
# 端口
ss -lntp | grep -E ':80|:443'
# 日志
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log
# 本机按 Host 验证
curl -I -H 'Host: example.com' http://127.0.0.1/
# HTTPS 证书
openssl s_client -connect example.com:443 -servername example.com </dev/null
openssl x509 -in fullchain.pem -noout -dates -subject -issuer
# Docker Nginx
docker exec nginx-demo nginx -t
docker exec nginx-demo nginx -T
docker logs nginx-demo官方文档入口
写配置时优先以官方文档为准,尤其是版本差异、弃用指令和模块默认值:
- Nginx 下载页
- Nginx Linux packages
- Beginner's Guide
- Controlling nginx
- Command-line parameters
- ngx_http_core_module
- ngx_http_proxy_module
- ngx_http_upstream_module
- ngx_http_ssl_module
- ngx_http_gzip_module
- ngx_http_limit_req_module
- ngx_http_headers_module
- Let's Encrypt 验证方式
- Let's Encrypt 集成指南
- Let's Encrypt 开放 80 端口建议
- 阿里云 ECS 安全组应用案例
- 阿里云 SSL 证书安装到 Nginx
- 腾讯云 CVM 安全组
- 腾讯云 SSL 证书安装到 Nginx
- 阿里云 ICP 备案基础服务
