Nginx 基础学习

· coding · #博客搭建

Nginx 基础学习笔记(以本博客项目为例)

本文以本仓库 aurora-deploy/nginx/nginx.conf 的真实配置为素材,讲清楚三件事:

  1. Nginx 的基础结构(层级、指令、语法)
  2. 在本项目里怎么用(部署、重启、验证、排错)
  3. Nginx(生产)和 dev 代理(开发)到底有什么区别

读者对象:能跑起来前端 npm run serve,但对「生产环境 Nginx 到底在干嘛」还没完全吃透的同学。


目录

  1. Nginx 在你项目里扮演什么角色
  2. Nginx 是什么
  3. 配置文件的基础结构
  4. 逐段读懂你的 nginx.conf
  5. 关键指令详解
  6. Nginx 如何日常使用
  7. Nginx(生产)vs dev 代理(开发)的本质区别
  8. 常见问题与踩坑
  9. 总结与速查表

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 服务器和反向代理。它擅长:

正向代理 vs 反向代理(一句话区分):


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 ...;
}

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

结构与前台几乎一模一样,只是:

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;
    }
}

5. 关键指令详解

5.1 listen —— 监听哪个端口

listen 80;        # 普通 HTTP
listen 443 ssl;   # HTTPS(带 ssl 标记,需配证书)

5.2 server_name —— 匹配哪个域名

Nginx 用请求里的 Host 头(浏览器自动带上域名)来匹配 server_name,命中哪个 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)必须有的。意思是按顺序尝试:

  1. 先找 $uri(例如 /about 这个文件)—— 找不到;
  2. 再找 $uri/(例如 /about/ 目录)—— 也找不到;
  3. 最后回退到 /index.html,交给前端路由处理。

为什么需要它:SPA 只有一个 index.html,/about、/articles/123 这些「路径」其实由前端 JS 在浏览器里处理,服务器上并没有对应的文件。没有这一行,用户直接刷新 /about 会 404。

5.5 proxy_pass —— 反向代理转发 ⭐

location ^~ /api/ {
    proxy_pass http://backend:8080/;
}

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;

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

修改配置的标准流程

  1. 本地改 aurora-deploy/nginx/nginx.conf;
  2. scp 上传:scp aurora-deploy/nginx/nginx.conf blog@你的IP:~/aurora/nginx/;
  3. 服务器上 docker compose exec nginx nginx -t 检查语法;
  4. 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. 总结与速查表

核心三句话

  1. Nginx 在你项目里 = 静态文件服务器(发 dist)+ 反向代理(/api→后端、图片→MinIO)。
  2. 配置结构是 events → http → server → location 层层嵌套;server_name 分站点,location 分路径。
  3. dev 代理和 Nginx 干的是同一件事:前端代码只写 /api,开发时由 devServer.proxy 转发,生产时由 Nginx proxy_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 改造)。

← 全部文章