【计算基础|网络16】【工程实战】Nginx 部署与反向代理
前面从网络层一路讲到 Socket 手写,真实生产里你几乎不会自己写 HTTP 服务的网络层------都是把后端服务(FastAPI/Node/Go)跑在内网端口上,前面架一层 Nginx 统一对外。这篇就是把这件事从安装到压测真机跑一遍:反向代理、负载均衡、location 匹配、静态资源、HTTPS、跨域、日志,全部有实测输出。
环境:macOS(Apple Silicon),Nginx 1.31.6(
brew install nginx),FastAPI + uvicorn 当后端,ab 压测。配置文件路径/opt/homebrew/etc/nginx/;Ubuntu 对应/etc/nginx/,命令基本一致。
一、Nginx 是什么:事件驱动的高性能服务器
一句话:Nginx 是高性能 HTTP 服务器、反向代理和负载均衡器 ,核心是事件驱动、异步非阻塞,单机抗并发能力极强。
它和老牌 Apache 的区别,一张表说清:
| 维度 | Nginx | Apache |
|---|---|---|
| 并发模型 | 事件驱动、异步非阻塞 | 进程/线程每连接一个 |
| 高并发表现 | 几万连接轻松 hold | 连接多了吃内存、性能掉 |
| 资源占用 | 低 | 相对高 |
| 静态文件 | 直接内核 sendfile,极快 | 一般 |
| 配置/扩展 | 配置简洁,模块动态 | .htaccess 灵活但慢 |
🔴 结论:现在生产上"前端静态资源 + 反向代理 + 负载均衡"这一层,Nginx 是事实标准。
二、安装与启停
2.1 三种安装方式
bash
# macOS(Apple Silicon 配置在 /opt/homebrew/etc/nginx/;Intel 在 /usr/local/etc/nginx/)
brew install nginx
# Ubuntu
sudo apt install nginx
# Docker
docker run -d -p 80:80 --name nginx nginx
我本机 brew install nginx 后,nginx -v 输出 nginx/1.31.6。Homebrew 版本为了免 sudo ,把默认端口设成了 8080 ,并且会自动加载 /opt/homebrew/etc/nginx/servers/ 下的所有配置。
2.2 启停与重载
| 命令 | 作用 |
|---|---|
nginx |
启动 |
nginx -s stop |
快速停止 |
nginx -s reload |
重载配置(不中断服务,最常用) |
nginx -t |
测试配置语法(改完配置先跑这个!) |
nginx -v |
看版本 |
⚠️ 改完配置第一件事永远是 nginx -t,语法错了 reload 会失败或服务起不来。生产 reload 是无感的。
三、配置文件结构:一层套一层
Nginx 配置从上到下是嵌套的:全局块 → events 块 → http 块 → server 块 → location 块。

要点:
- http 包含多个 server (一个 server 就是一个站点/虚拟主机),server 包含多个 location(按 URL 路径分流)。
- 每条指令以分号结尾,块用
{}。 - Homebrew 主配置
nginx.conf末尾有include servers/*;,意思是把servers/目录下所有文件都当配置读进来------我们新增站点就往这个目录丢文件,不用动主配置。
常用块指令速查:
| 块 | 常用指令 |
|---|---|
| 全局 | worker_processes auto(CPU 核数)、error_log、pid |
| events | worker_connections 1024、use epoll(Linux)/kqueue(macOS) |
| http | include mime.types、sendfile on、keepalive_timeout、gzip on |
四、反向代理:把请求转发给后端
4.1 正向代理 vs 反向代理
| 正向代理 | 反向代理 | |
|---|---|---|
| 代理对象 | 客户端 | 服务端 |
| 典型 | VPN、翻墙代理,帮客户端访问外面 | Nginx,帮服务端接收请求 |
| 客户端感知 | 知道目标真实地址 | 不知道后端有几台、是谁 |
| 用途 | 突破限制、隐藏客户端 | 负载均衡、缓存、SSL 终结、隐藏后端 |
一句话:正向代理站在客户端那边,反向代理站在服务端这边。
4.2 基本配置
核心就一句:proxy_pass http://后端地址;。客户端访问 Nginx,Nginx 再把请求转发给 FastAPI。
nginx
location /api/ {
proxy_pass http://127.0.0.1:8000;
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 5s;
proxy_read_timeout 30s;
}
proxy_set_header 这几行很关键------后端拿到的"客户端真实 IP"其实是 Nginx 的 IP ,所以 Nginx 要把真实客户端 IP 写进 X-Real-IP / X-Forwarded-For 头带给后端,后端从这两个头里取真实 IP。
4.3 真机跑通
我起了两个 FastAPI 后端(8001、8002),它们的 /api/headers 会把收到的请求头原样打出来。请求打到 Nginx(8081)再转发:
bash
curl -s http://127.0.0.1:8081/api/headers
实测输出------后端确实收到了 Nginx 透传的真实客户端 IP:
json
{
"backend_port": "8001",
"host": "127.0.0.1",
"x_real_ip": "127.0.0.1",
"x_forwarded_for": "127.0.0.1",
"user_agent": "curl/8.7.1"
}
✅ 反向代理成立:客户端只访问 127.0.0.1:8081,根本不知道后端其实是 8001 这个 FastAPI 进程。
五、location 匹配规则:请求落到哪块
5.1 修饰符与优先级
nginx
location [=|~|~*|^~] /uri/ { ... }
优先级从高到低:
| 顺序 | 修饰符 | 含义 |
|---|---|---|
| 1 | = |
精确匹配 ,如 location = / |
| 2 | ^~ |
前缀匹配,命中后不再查正则 |
| 3 | ~ / ~* |
正则 匹配(~ 区分大小写,~* 不区分) |
| 4 | 普通前缀 | 不带修饰符的前缀匹配 |
| 5 | / |
通用兜底 |
口诀:精确 > 前缀^~ > 正则 > 普通前缀 > 兜底。
六、负载均衡:多台后端怎么分流量
6.1 upstream 与四种策略
用 upstream 定义一组后端,proxy_pass 指向它就行。策略:
| 策略 | 写法 | 说明 |
|---|---|---|
| round-robin(默认) | 不写 | 轮询,一个一个轮流 |
| weight | server x weight=3; |
加权轮询,性能好的机器多分 |
| ip_hash | ip_hash; |
按客户端 IP 哈希,会话保持 |
| least_conn | least_conn; |
分给当前连接数最少的 |
| hash | hash $request_uri; |
自定义键哈希 |
服务器还能带状态参数:backup(备用,全挂了才上)、down(停用)、max_fails、fail_timeout。
6.2 实测:weight 3:1 分发
我的配置:
nginx
upstream blog_backend {
server 127.0.0.1:8001 weight=3;
server 127.0.0.1:8002 weight=1;
}
server {
listen 8081;
location /api/ {
proxy_pass http://blog_backend;
}
}
连续发 8 次请求,看后端端口怎么分:
bash
for i in $(seq 1 8); do curl -s http://127.0.0.1:8081/api/hello; echo; done
实测输出------8001 接到 6 次、8002 接到 2 次,正好 3:1:
text
{"msg":"hello","backend_port":"8001"}
{"msg":"hello","backend_port":"8001"}
{"msg":"hello","backend_port":"8002"}
{"msg":"hello","backend_port":"8001"}
{"msg":"hello","backend_port":"8001"}
{"msg":"hello","backend_port":"8001"}
{"msg":"hello","backend_port":"8002"}
{"msg":"hello","backend_port":"8001"}
✅ 负载均衡生效。流量被 Nginx 按权重分散到了两个后端实例上------这就是"扩容多台后端,前面一个入口"的原理。
七、静态资源、缓存与上传限制
7.1 root vs alias
这俩最容易混:
| 指令 | 行为 | 例子(请求 /static/index.html) |
|---|---|---|
root |
拼接路径 | root /var/www → /var/www/static/index.html |
alias |
替换路径 | alias /data/ → /data/index.html |
我用 alias 把 /static/ 映射到本地目录:
nginx
location /static/ {
alias /path/to/static/;
index index.html;
expires 1h; # 浏览器缓存 1 小时
}
实测访问:
bash
curl -s http://127.0.0.1:8081/static/
# <h1>Hello from Nginx static</h1>
7.2 上传大小限制
nginx
client_max_body_size 10m;
默认才 1m,上传大文件报 413 Request Entity Too Large 就是它太小。这是新手最常踩的坑之一。
八、用反向代理解决跨域
8.1 原理
浏览器跨域是同源策略 搞的鬼。解法很简单:让前端和 API 在同一个域名下 ------前端静态资源由 Nginx 服务,/api/ 路径也由 Nginx 转发到后端,浏览器看全是同源,根本不触发跨域。
nginx
server {
listen 80;
location / {
root /path/to/frontend; # 前端静态文件
}
location /api/ {
proxy_pass http://127.0.0.1:8000; # 后端
}
}
另一种是直接让 Nginx 加 CORS 头:
nginx
add_header Access-Control-Allow-Origin *;
九、配置 HTTPS
9.1 证书从哪来
- Let's Encrypt :免费、自动续期,生产用
certbot申请。 - 自签名证书:测试用,浏览器会报不安全。
9.2 生成自签名证书
bash
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes \
-subj "/CN=localhost"
9.3 Nginx HTTPS 配置
nginx
server {
listen 8443 ssl;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location /api/ {
proxy_pass http://127.0.0.1:8000;
}
}
实测(自签名证书,-k 跳过校验):
bash
curl -sk https://127.0.0.1:8443/api/hello
# {"msg":"hello","backend_port":"8001"}
✅ HTTPS 通了。生产再配 HTTP 强制跳 HTTPS:return 301 https://$host$request_uri;。
十、日志:出问题先看它
| 日志 | 记录什么 |
|---|---|
access_log |
每个请求:谁、什么时候、访问什么、状态码、耗时 |
error_log |
错误,级别 debug/info/notice/warn/error/crit |
我本机默认访问日志 /opt/homebrew/var/log/nginx/access.log,实测内容:
text
127.0.0.1 - - [22/Sep/2026:11:56:41 +0800] "GET /api/hello HTTP/1.1" 200 37 "-" "curl/8.7.1"
常用日志变量:$remote_addr(客户端 IP)、$request(请求行)、$status(状态码)、$body_bytes_sent、$http_user_agent、$request_time、$upstream_response_time(后端耗时)。
实时盯日志:tail -f /opt/homebrew/var/log/nginx/access.log。
十一、压测:直连 vs 走 Nginx
最后用 ab(ApacheBench)真机对比。后端是单进程 uvicorn:
bash
# 直连后端:1000 请求、10 并发
ab -n 1000 -c 10 http://127.0.0.1:8001/api/hello
# 走 Nginx 代理:同样参数
ab -n 1000 -c 10 http://127.0.0.1:8081/api/hello
实测结果:
| 指标 | 直连后端(8001) | 走 Nginx 代理(8081) |
|---|---|---|
| Failed requests | 0 | 0 |
| Requests per second | 10707.90 #/sec | 17869.59 #/sec |
| Time per request | 0.934 ms | 0.560 ms |
🔴 有意思的结论:走 Nginx 代理的 QPS 反而更高。原因是 Nginx 事件驱动、异步非阻塞,而单进程 uvicorn 在高并发下处理连接效率不如 Nginx 这个"专业守门人"。这也印证了 Nginx 的价值------它不只是转发,本身就是个高性能并发层。
十二、Nginx 常见问题速查表
| 现象 | 原因 | 解决 |
|---|---|---|
413 Request Entity Too Large |
client_max_body_size 太小 |
调大,如 10m |
404 |
location 匹配不到 / root 路径不对 | 检查 location 和 root/alias |
502 Bad Gateway |
后端没启动 / proxy_pass 地址错 | 确认后端在跑、地址端口对 |
504 Gateway Timeout |
后端响应超时 | 调大 proxy_read_timeout |
静态资源 403 |
权限问题 / 首页文件不存在 | 检查目录权限、index 文件 |
| 改配置不生效 | 忘 reload / 语法错 | 先 nginx -t 再 nginx -s reload |
总结:你真正需要记住的 9 件事
- Nginx = 高性能 HTTP 服务器 + 反向代理 + 负载均衡,事件驱动、异步非阻塞。
- 配置结构:全局 → events → http → server → location ;新增站点丢进
servers/即可。 - 改完配置先
nginx -t,再nginx -s reload(无感重载)。 - 反向代理核心就一句
proxy_pass,并用X-Real-IP/X-Forwarded-For把真实客户端 IP 带给后端。 - 正向代理代理客户端,反向代理代理服务端。
- 负载均衡用
upstream,策略有轮询/weight/ip_hash/least_conn;weight 按比例分流量。 - location 优先级:精确
=>^~> 正则~/~*> 普通前缀 >/;root拼接、alias替换。 client_max_body_size默认 1m,传大文件报 413 就调它。- HTTPS 用
listen 443 ssl+ 证书;跨域靠"前后端同源 + Nginx 代理"最省事。
验证清单
- 能用
brew install nginx或apt install nginx装好,并找到配置文件路径 - 会用
nginx -t测配置、nginx -s reload重载 - 能写出一个反向代理配置,把
/api/转发到本地后端 - 能解释为什么后端要从
X-Real-IP取真实客户端 IP - 能配
upstream+ weight 做负载均衡,并实测流量按比例分发 - 能说清 location 五种修饰符的优先级
- 能区分
root和alias - 遇到 413/502/504 能分别说出原因和改法
- 能用 openssl 生成自签名证书并配出 HTTPS
参考资源
- Nginx 官方文档 nginx.org/en/docs/
- Nginx 反向代理示例 docs.nginx.com/nginx/admin...
- Nginx 负载均衡 docs.nginx.com/nginx/admin...
- Let's Encrypt(免费证书)letsencrypt.org/
- Apache Bench 压测说明 httpd.apache.org/docs/2.4/pr...
整个网络模块回顾
从网络 01 到 16,我们把计算机网络这条线走完了:
- 模型与全景(网络 01):五层模型、一次请求的完整旅程。
- HTTP 实战(网络 03、04):请求报文组成、方法语义、参数位置、JSON/表单/文件、鉴权与跨域排错。
- 网络层(网络 14):IP 寻址与路由、子网划分、ICMP、ping/traceroute 的底层原理。
- Socket 上手(网络 15):亲手写 TCP/UDP 程序、复现粘包、长度头解决、selectors 并发雏形。
- 工程实战(网络 16,本篇):用 Nginx 把这些协议落地成生产架构------反向代理藏后端、负载均衡抗扩容、HTTPS 保安全、日志排错。
一句话串起来:应用层你写 HTTP 接口 → 传输层 TCP 负责可靠送达 → 网络层 IP 负责寻址路由 → 最前面 Nginx 把这一切统一收口。网络模块到此收官,下一个模块我们转入操作系统,去看 epoll、进程、线程、内存这些"应用程序跑起来"的另一半故事。