Nginx 基础学习
Nginx 基础学习笔记(以本博客项目为例)
本文以本仓库
aurora-deploy/nginx/nginx.conf的真实配置为素材,讲清楚三件事:
- Nginx 的基础结构(层级、指令、语法)
- 在本项目里怎么用(部署、重启、验证、排错)
- Nginx(生产)和 dev 代理(开发)到底有什么区别
读者对象:能跑起来前端
npm run serve,但对「生产环境 Nginx 到底在干嘛」还没完全吃透的同学。
目录
- Nginx 在你项目里扮演什么角色
- Nginx 是什么
- 配置文件的基础结构
- 逐段读懂你的 nginx.conf
- 关键指令详解
- Nginx 如何日常使用
- Nginx(生产)vs dev 代理(开发)的本质区别
- 常见问题与踩坑
- 总结与速查表
1. Nginx 在你项目里扮演什么角色
先看整体架构(本博客是前后端分离 + Docker Compose 部署):
浏览器
│
│ https://arcueidblog.top (前台)
│ https://admin.arcueidblog.top (后台)
│ https://img.arcueidblog.top (图片)
▼
Nginx(一个容器,监听 80/443)
│ ├─ / → 返回静态文件 dist/index.html(前端)
│ ├─ /api/... → 转发给 backend:8080(后端 SpringBoot)
│ └─ img 子域名 → 转发给 minio:9000(图片存储)
▼
后端 backend:8080 ──▶ MySQL / Redis / RabbitMQ
图片 minio:9000
Nginx 在这里身兼两职:
| 角色 | 干的活 |
|---|---|
| 静态文件服务器 | 把打包好的前端 dist/(HTML/JS/CSS/图片)直接发给浏览器 |
| 反向代理 | 把 /api 请求转给后端,把图片请求转给 MinIO |
一句话:浏览器只跟 Nginx 打交道,Nginx 再根据「URL 路径」决定是发静态文件、还是转发给后端。 后端、MinIO 都不直接暴露给公网(除了 8080 测试用)。
2. Nginx 是什么
Nginx(engine x)是一个高性能的 HTTP 服务器和反向代理。它擅长:
- 静态文件:直接返回磁盘上的 HTML/CSS/JS/图片,非常快;
- 反向代理:把请求「转发」给后面的应用(如 SpringBoot),再把响应传回;
- 负载均衡 / 动静分离 / HTTPS 终结等(本项目用到「静态 + 反代 + HTTPS」三样)。
正向代理 vs 反向代理(一句话区分):
- 正向代理:代理的是客户端(你挂的梯子);
- 反向代理:代理的是服务端(浏览器以为在访问 Nginx,实际是 Nginx 替它去问后端)。
3. 配置文件的基础结构
Nginx 配置是层级嵌套的,像俄罗斯套娃。从外到内是:
# ① 全局块(main context)—— 整个 Nginx 的全局设置
user nginx;
worker_processes auto;
# ② events 块 —— 网络连接相关
events {
worker_connections 1024;
}
# ③ http 块 —— 所有 HTTP 服务的总容器
http {
include mime.types; # 文件类型映射(.js→application/javascript 等)
gzip on; # 开启压缩
# ④ server 块 —— 一个「虚拟主机」= 一个域名/端口组合
server {
listen 80;
server_name example.com;
# ⑤ location 块 —— 一条 URL 路径匹配规则
location / {
root /var/www/html;
}
}
}
层级关系记忆:
events —— 连接怎么处理
http —— 所有网站的总开关
server —— 每个「站点」(域名 + 端口)
location —— 站点里的每条「路径规则」
一个 server 块对应一个站点;一个站点可以有很多 location 规则。你的项目里,因为用了 3 个子域名 ×(80 跳转 + 443 加密),所以一共 6 个 server 块。
4. 逐段读懂你的 nginx.conf
你的完整配置在 aurora-deploy/nginx/nginx.conf(约 100 行),核心是 6 个 server 块。下面拆开讲。
4.1 顶部:events + http 全局
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types; # 让 .js/.css 有正确的 Content-Type
default_type application/octet-stream;
sendfile on; # 高效发送静态文件的开关
keepalive_timeout 65;
client_max_body_size 50m; # 上传大小上限(后端图片上传会经过这里)
gzip on; # 压缩,省流量
gzip_min_length 1k;
gzip_comp_level 4;
gzip_types text/plain text/css application/javascript ...;
}
client_max_body_size 50m很重要:后端文章/图片上传如果超过 50M 会被 Nginx 拦下报 413。gzip让静态文件(尤其 JS/CSS)传输体积大幅缩小,加快首屏。
4.2 前台博客:arcueidblog.top
每个子域名都是「一对」server 块:一个 listen 80 负责跳转,一个 listen 443 ssl 负责真正服务。
# ① 80 端口:HTTP 请求,一律 301 跳到 HTTPS
server {
listen 80;
server_name arcueidblog.top www.arcueidblog.top;
location /.well-known/acme-challenge/ { root /var/www/certbot; } # 留给 certbot 续期
location / { return 301 https://$host$request_uri; } # 其余全跳 HTTPS
}
# ② 443 端口:真正的 HTTPS 服务
server {
listen 443 ssl;
server_name arcueidblog.top www.arcueidblog.top;
ssl_certificate /etc/letsencrypt/live/arcueidblog.top/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/arcueidblog.top/privkey.pem;
root /var/www/aurora/blog; # 前台静态文件根目录
index index.html;
location / {
try_files $uri $uri/ /index.html; # SPA 的关键(见 5.4)
}
location ^~ /api/ {
proxy_pass http://backend:8080/; # 转发给后端,见 5.5
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;
}
}
4.3 后台管理:admin.arcueidblog.top
结构与前台几乎一模一样,只是:
server_name换成admin.arcueidblog.toproot换成/var/www/aurora/admin(后台的 dist)
4.4 图片:img.arcueidblog.top → MinIO
server {
listen 443 ssl;
server_name img.arcueidblog.top;
ssl_certificate ...;
ssl_certificate_key ...;
location / {
proxy_pass http://minio:9000; # 整站反代给 MinIO 容器
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;
}
}
- 这里没有
root和try_files,因为图片不存本地磁盘,而是存在 MinIO 里。 - 为什么单独搞个
img子域名?因为主站上 HTTPS 后,浏览器会拦截 HTTPS 页面里嵌的 HTTP 图片(混合内容),所以让图片也走 HTTPS。
5. 关键指令详解
5.1 listen —— 监听哪个端口
listen 80; # 普通 HTTP
listen 443 ssl; # HTTPS(带 ssl 标记,需配证书)
5.2 server_name —— 匹配哪个域名
Nginx 用请求里的 Host 头(浏览器自动带上域名)来匹配 server_name,命中哪个 server 块就用哪个:
- 访问
arcueidblog.top→ 命中「前台」server - 访问
admin.arcueidblog.top→ 命中「后台」server - 访问
img.arcueidblog.top→ 命中「图片」server
旧配置里写
server_name _;,_是通配符,表示「不管什么域名都进这个块」——所以以前用 IP 也能访问。改成具体域名后,更安全清晰。
5.3 root + index —— 静态文件在哪、默认首页是谁
root /var/www/aurora/blog; # 访问 /index.html 时,去这个目录找
index index.html; # 访问 / 时,默认返回 index.html
5.4 try_files $uri $uri/ /index.html —— SPA 路由的关键 ⭐
这是前端单页应用(SPA)必须有的。意思是按顺序尝试:
- 先找
$uri(例如/about这个文件)—— 找不到; - 再找
$uri/(例如/about/目录)—— 也找不到; - 最后回退到
/index.html,交给前端路由处理。
为什么需要它:SPA 只有一个 index.html,/about、/articles/123 这些「路径」其实由前端 JS 在浏览器里处理,服务器上并没有对应的文件。没有这一行,用户直接刷新 /about 会 404。
5.5 proxy_pass —— 反向代理转发 ⭐
location ^~ /api/ {
proxy_pass http://backend:8080/;
}
-
location ^~ /api/:所有/api/开头的请求都进这里(^~表示「前缀匹配且优先」)。 -
proxy_pass http://backend:8080/;末尾的/很关键:- 带尾斜杠:
/api/user→backend:8080/user(剥掉/api前缀) - 不带尾斜杠:
/api/user→backend:8080/api/user(前缀保留)
- 带尾斜杠:
-
backend是 Docker Compose 里的服务名(不是 IP),容器网络内自动解析成后端容器的内网 IP。
5.6 proxy_set_header —— 让后端知道「真实请求」是谁
| 头 | 含义 |
|---|---|
Host |
原始域名 |
X-Real-IP |
客户端真实 IP |
X-Forwarded-For |
完整代理链(客户端 → 代理) |
X-Forwarded-Proto |
原始协议(http/https),后端据此判断是否走 HTTPS |
不设这些,后端日志里看到的 IP 全是 Nginx 容器的内网 IP,排查问题无从下手。
5.7 return 301 —— HTTP 跳 HTTPS
location / { return 301 https://$host$request_uri; }
用户输入 http://arcueidblog.top 时,Nginx 返回 301,浏览器自动跳到 https://...。
5.8 ssl_certificate —— HTTPS 证书
ssl_certificate /etc/letsencrypt/live/arcueidblog.top/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/arcueidblog.top/privkey.pem;
fullchain.pem:证书 + 中间证书链(用这个,别用cert.pem,否则可能「证书不受信任」);privkey.pem:私钥,绝不能泄露。
5.9 location /.well-known/acme-challenge/ —— 证书续期
location /.well-known/acme-challenge/ { root /var/www/certbot; }
这是 Let's Encrypt(certbot)验证域名所有权 + 自动续期的专用路径,指向 certbot 容器的挂载目录,不要删。
6. Nginx 如何日常使用
你的 Nginx 跑在 Docker 容器里,配置文件通过挂载生效:
# docker-compose.yml 里 nginx 服务
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # 配置文件挂载
- ./frontend/blog:/var/www/aurora/blog:ro # 前台 dist
- ./frontend/admin:/var/www/aurora/admin:ro # 后台 dist
- ./certbot/www:/var/www/certbot:ro # 证书验证目录
- ./certbot/conf:/etc/letsencrypt:ro # 证书目录
常用命令(服务器上 cd ~/aurora 后执行)
# 修改配置后,先检查语法是否正确(强烈建议每次改完都做)
docker compose exec nginx nginx -t
# 语法通过后,重载/重启 nginx
docker compose restart nginx
# 看 nginx 访问日志 / 错误日志
docker compose logs nginx
docker compose logs nginx --tail 50
# 看当前所有容器状态
docker compose ps
修改配置的标准流程
- 本地改
aurora-deploy/nginx/nginx.conf; scp上传:scp aurora-deploy/nginx/nginx.conf blog@你的IP:~/aurora/nginx/;- 服务器上
docker compose exec nginx nginx -t检查语法; docker compose restart nginx生效。
只改前端静态文件时
前端 dist/ 是目录挂载(ro 只读),你 scp 覆盖 frontend/blog/ 里的文件后,Nginx 会立刻读到新文件,但建议 docker compose restart nginx 一下确保缓存刷新。
7. Nginx(生产)vs dev 代理(开发)的本质区别 ⭐
这是最容易混淆、也最值得搞懂的一点。
7.1 它们其实是「同一份工作的两种实现」
两者干的是一模一样的事:托管前端 + 把 /api 转发给后端。
| 职责 | 生产(服务器) | 开发(你 Mac) |
|---|---|---|
| 托管前端 | Nginx 的 root /var/www/aurora/blog |
webpack-dev-server(内存编译) |
转发 /api |
Nginx 的 location /api { proxy_pass ... } |
devServer.proxy 的 target |
为什么开发时不用 Nginx?因为开发时不方便也没必要在你 Mac 上装一个 Nginx,vue-cli 的 dev server 顺手把「转发 /api」这件小事也干了。
7.2 一图看懂数据流
开发环境(npm run serve):
浏览器 ── localhost:8080 ──▶ webpack-dev-server ──/api──▶ devServer.proxy.target(你的后端)
生产环境(部署后):
浏览器 ── https://arcueidblog.top ──▶ Nginx ──/api──▶ backend:8080
7.3 详细对比表
| 维度 | dev 代理(vue-cli) | Nginx(生产) |
|---|---|---|
| 运行时机 | npm run serve 时 |
部署后一直运行 |
| 是什么 | webpack-dev-server 内置功能 | 独立的 Web 服务器/反代 |
| 配置文件 | vue.config.js 的 devServer.proxy |
nginx.conf |
| 前端从哪来 | 内存里实时编译,不落盘 | 读磁盘上的 dist/ 目录 |
| 热更新 HMR | ✅ 有,改代码秒刷新 | ❌ 无,要重新 build 部署 |
转发 /api |
target 字段 |
location /api + proxy_pass |
| HTTPS | 一般不配 | 配 443 + 证书 |
| 多站点/子域名 | ❌ 不支持 | ✅ server_name 区分 |
| 静态文件优化(gzip/缓存) | 弱 | ✅ 强 |
7.4 对应到你的代码
开发时的代理(aurora-blog/vue.config.js):
devServer: {
proxy: {
'/api': {
target: 'https://arcueidblog.top', // 本地 serve 时,/api 转发到这里
changeOrigin: true
}
}
}
生产时的反代(aurora-deploy/nginx/nginx.conf):
location ^~ /api/ {
proxy_pass http://backend:8080/;
}
关键结论:
前端代码从头到尾只认识相对路径
/api,它不知道、也不关心背后是谁。到底是「谁」把/api转发给后端,取决于「此刻是谁在服务这个前端」——
- 本地
serve时 → 是devServer.proxy;- 服务器上 → 是 Nginx。
这也是为什么当初 dev 代理指向原作者 linhaojun.top 时,本地页面会「无声无息」拿到原作者的数据——前端代码并不知情,它只是把「谁回给它的数据」渲染出来而已。
8. 常见问题与踩坑
| 现象 | 可能原因 | 解决 |
|---|---|---|
刷新 /about 等子页面 404 |
缺 try_files ... /index.html |
补上 SPA 回退规则 |
| 访问 API 502 Bad Gateway | Nginx 连不上后端 | 检查 proxy_pass 地址/后端容器是否 Up;docker compose restart nginx |
| 上传大图报 413 | 超过 client_max_body_size |
调大该值 |
| 页面图片不显示、浏览器提示混合内容 | HTTPS 页面里嵌了 HTTP 图片 | 让图片也走 HTTPS(img 子域名) |
| 改了 nginx 配置不生效 | 没重载 / 挂载路径错 | nginx -t 检查 + docker compose restart nginx |
| 证书报「不受信任」 | 用了 cert.pem 而非 fullchain.pem |
改成 fullchain.pem |
| 后端日志 IP 全是内网 IP | 没设 proxy_set_header |
补 X-Real-IP / X-Forwarded-For |
9. 总结与速查表
核心三句话
- Nginx 在你项目里 = 静态文件服务器(发 dist)+ 反向代理(
/api→后端、图片→MinIO)。 - 配置结构是 events → http → server → location 层层嵌套;
server_name分站点,location分路径。 - dev 代理和 Nginx 干的是同一件事:前端代码只写
/api,开发时由devServer.proxy转发,生产时由 Nginxproxy_pass转发。
关键指令速查
| 指令 | 作用 |
|---|---|
listen 80 / 443 ssl |
监听端口 |
server_name |
按域名匹配站点 |
root / index |
静态文件根目录 / 默认首页 |
location |
URL 路径匹配 |
try_files $uri $uri/ /index.html |
SPA 刷新回退 |
proxy_pass |
反向代理转发 |
proxy_set_header |
转发时改写请求头 |
return 301 |
重定向(HTTP→HTTPS) |
ssl_certificate |
HTTPS 证书 |
client_max_body_size |
上传大小上限 |
gzip on |
开启压缩 |
相关文件速查
| 关注点 | 路径 |
|---|---|
| 生产 Nginx 配置 | aurora-deploy/nginx/nginx.conf |
| 临时 HTTP 配置(首次签证书用) | aurora-deploy/nginx/nginx-http.conf |
| Docker 编排(挂载关系) | aurora-deploy/docker-compose.yml |
| 前台 dev 代理 | aurora/aurora-vue/aurora-blog/vue.config.js |
| 后台 dev 代理 | aurora/aurora-vue/aurora-admin/vue.config.js |
配合阅读:建站过程.md(部署全流程)、域名与HTTPS配置过程.md(域名/证书/HTTPS 改造)。