前端必修课:Nginx 从入门到实战,一篇搞定生产级配置

4 阅读5分钟

🚀 前端必修课:Nginx 从入门到实战,一篇搞定生产级配置

还在求助后端配置 Nginx?前端工程化时代,Nginx 已经是我们必须掌握的技能。本文从实际场景出发,覆盖前端 90% 的 Nginx 配置需求,直接复制即用。

前言

作为一名前端开发者,你是否遇到过这些场景:

  • 本地开发没问题,部署后接口跨域了

  • SPA 项目刷新页面 404

  • 首屏加载慢,不知道怎么开启压缩和缓存

  • 多环境部署,不知道怎么配置反向代理

这些问题,都能用 Nginx 解决。Nginx 不再是后端的专属技能,它是前端工程化链路中不可缺失的一环。


一、Nginx 核心概念速览

1.1 什么是 Nginx

Nginx 是一个高性能的 HTTP 和反向代理服务器,核心能力:

| 能力 | 说明 |

|------|------|

| 静态资源服务 | 直接托管前端构建产物 |

| 反向代理 | 将请求转发到后端服务 |

| 负载均衡 | 多实例分发请求 |

| SSL/TLS | HTTPS 证书配置 |

1.2 配置文件结构


# /etc/nginx/nginx.conf

main 全局配置

├── events {}        # 连接配置

└── http {}          # HTTP 服务配置

    ├── upstream {}  # 负载均衡组

    ├── server {}    # 虚拟主机

    │   └── location {}  # 路由匹配

    └── server {}    # 可配置多个虚拟主机

1.3 location 匹配规则

这是最容易混淆的部分,一张图说清楚:


location = /api/exact  {}   # 精确匹配,优先级最高

location ^~ /api/prefix {}  # 前缀匹配,匹配后不查正则

location ~ /api/regex\.html {}  # 正则匹配(区分大小写)

location ~* /api/regex\.html {} # 正则匹配(不区分大小写)

location /api/prefix  {}   # 普通前缀匹配

匹配优先级:精确 = > 前缀 ^~ > 正则 ~/~* > 普通前缀


二、前端必备的 8 个配置场景

场景 1:SPA 路由 History 模式支持

问题:Vue/React 项目使用 history 路由模式,刷新页面 404。

原因:刷新时浏览器向服务器请求 /user/profile,服务器找不到对应文件。


server {

    listen 80;

    server_name example.com;

    root /usr/share/nginx/html;

    index index.html;

  


    # 核心:所有路由都回退到 index.html,交给前端路由处理

    location / {

        try_files $uri $uri/ /index.html;

    }

}

try_files 按顺序查找文件:先找 $uri 对应文件,再找 $uri/ 目录,都没有则返回 index.html

场景 2:反向代理解决跨域

问题:前端 http://localhost:5173,后端 http://localhost:8080,跨域报错。


server {

    listen 80;

    server_name localhost;

  


    # 前端静态资源

    location / {

        root /usr/share/nginx/html;

        try_files $uri $uri/ /index.html;

    }

  


    # 接口代理:/api/* → 后端服务

    location /api/ {

        proxy_pass http://backend: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;

    }

}

****** **proxy_pass** 末尾斜杠的坑**:


# 请求:/api/users/list

location /api/ {

    proxy_pass http://backend:8080/;   # 转发 → /users/list(去掉 /api 前缀)

}

location /api/ {

    proxy_pass http://backend:8080;    # 转发 → /api/users/list(保留原路径)

}

location /api/ {

    proxy_pass http://backend:8080/v2/; # 转发 → /v2/users/list(替换前缀)

}

场景 3:Gzip 压缩

问题:打包后 JS/CSS 文件过大,传输慢。


# 开启 gzip,传输体积可减少 60%-80%

gzip on;

gzip_min_length 1k;           # 小于 1k 不压缩,压缩反而更大

gzip_comp_level 6;            # 压缩级别 1-9,6 是性价比最优解

gzip_types

    text/plain

    text/css

    text/javascript

    application/javascript

    application/json

    application/xml

    image/svg+xml;             # 需要压缩的 MIME 类型

gzip_vary on;                 # 添加 Vary: Accept-Encoding 头

gzip_buffers 16 8k;           # 压缩缓冲区

gzip_http_version 1.1;        # 最低 HTTP 版本

实测效果:一个 1.2MB 的 JS 文件,Gzip 后约 350KB,Brotli 后约 280KB。

如果支持 Brotli(需安装 ngx_brotli 模块):


brotli on;

brotli_comp_level 6;

brotli_types text/plain text/css application/javascript application/json image/svg+xml;

场景 4:静态资源缓存策略

问题:每次访问都重新请求静态资源,浪费带宽。


location /assets/ {

    root /usr/share/nginx/html;

  


    # 强缓存:带 hash 的文件,1 年不过期

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {

        expires 1y;

        add_header Cache-Control "public, immutable";

        # immutable 告诉浏览器:此资源永不变化,无需发条件请求

    }

}

  


# 协商缓存:index.html 不缓存,保证每次拿到最新版本

location = /index.html {

    add_header Cache-Control "no-cache";

    # no-cache ≠ 不缓存,而是每次使用前必须向服务器验证

}

为什么这么配:前端构建产物中,[name].[hash].js 的 hash 变了文件名就变了,可以放心强缓存;而 index.html 是入口,必须每次验证。

场景 5:多环境 / 多项目部署

问题:同一台服务器部署多个前端项目(如后台管理 + 用户端 + H5 活动)。


# 项目 1:后台管理系统

server {

    listen 80;

    server_name admin.example.com;

    root /usr/share/nginx/admin;

    location / {

        try_files $uri $uri/ /index.html;

    }

}

  


# 项目 2:用户端

server {

    listen 80;

    server_name www.example.com;

    root /usr/share/nginx/web;

    location / {

        try_files $uri $uri/ /index.html;

    }

    location /api/ {

        proxy_pass http://backend:8080/;

    }

}

  


# 项目 3:H5 活动页(同域子路径)

server {

    listen 80;

    server_name www.example.com;

  


    location /activity/ {

        alias /usr/share/nginx/activity/;  # alias 替换路径

        try_files $uri $uri/ /activity/index.html;

    }

}

****** **root** vs **alias** 的区别**:


# 请求:/activity/index.html

location /activity/ {

    root /usr/share/nginx/html;  # 实际查找:/usr/share/nginx/html/activity/index.html

}

location /activity/ {

    alias /usr/share/nginx/activity/; # 实际查找:/usr/share/nginx/activity/index.html

}

场景 6:HTTPS 配置


server {

    listen 443 ssl http2;

    server_name example.com;

  


    # 证书配置

    ssl_certificate     /etc/nginx/ssl/fullchain.pem;

    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

  


    # SSL 优化

    ssl_protocols TLSv1.2 TLSv1.3;

    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

    ssl_prefer_server_ciphers on;

    ssl_session_cache shared:SSL:10m;

    ssl_session_timeout 1d;

  


    # HSTS:强制 HTTPS(生产环境开启,先短时间测试)

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

  


    location / {

        root /usr/share/nginx/html;

        try_files $uri $uri/ /index.html;

    }

}

  


# HTTP 自动跳转 HTTPS

server {

    listen 80;

    server_name example.com;

    return 301 https://$host$request_uri;

}

场景 7:安全头配置


server {

    # 防止 MIME 类型嗅探

    add_header X-Content-Type-Options "nosniff" always;

  


    # 防止点击劫持

    add_header X-Frame-Options "SAMEORIGIN" always;

  


    # XSS 防护

    add_header X-XSS-Protection "1; mode=block" always;

  


    # CSP 策略(按需配置,建议逐步收紧)

    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;

  


    # Referrer 策略

    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

  


    # 权限策略

    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

}

场景 8:负载均衡


upstream backend_servers {

    # 轮询(默认)

    server 192.168.1.101:8080;

    server 192.168.1.102:8080;

    server 192.168.1.103:8080 backup;  # 备用节点

  


    # 加权轮询

    # server 192.168.1.101:8080 weight=5;

    # server 192.168.1.102:8080 weight=3;

  


    # IP Hash(会话保持,同一 IP 始终分配到同一后端)

    # ip_hash;

  


    # 最少连接数

    # least_conn;

  


    # 健康检查

    keepalive 32;

}

  


server {

    listen 80;

    location /api/ {

        proxy_pass http://backend_servers/;

        proxy_next_upstream error timeout http_503;  # 故障自动切换

        proxy_connect_timeout 5s;

        proxy_read_timeout 30s;

    }

}


三、Docker 一键部署方案

前端项目最常用的部署方式,直接集成到 CI/CD 中:

3.1 Nginx 配置文件


# nginx.conf

server {

    listen 80;

    server_name _;

    root /usr/share/nginx/html;

    index index.html;

  


    gzip on;

    gzip_min_length 1k;

    gzip_comp_level 6;

    gzip_types text/plain text/css application/javascript application/json image/svg+xml;

    gzip_vary on;

  


    location / {

        try_files $uri $uri/ /index.html;

    }

  


    location /api/ {

        proxy_pass http://backend: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;

    }

  


    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {

        expires 1y;

        add_header Cache-Control "public, immutable";

    }

  


    location = /index.html {

        add_header Cache-Control "no-cache";

    }

}

3.2 Dockerfile


# 构建阶段

FROM node:20-alpine AS builder

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

  


# 运行阶段

FROM nginx:1.25-alpine

COPY --from=builder /app/dist /usr/share/nginx/html

COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

3.3 常用命令


# 构建镜像

docker build -t my-app .

  


# 运行容器

docker run -d -p 80:80 --name my-app my-app

  


# 重新部署(更新前端产物)

docker cp dist/. my-app:/usr/share/nginx/html/

docker exec my-app nginx -s reload


四、生产环境 Checklist

上线前逐项检查:

| 项目 | 配置 | 状态 |

|------|------|------|

| SPA 路由回退 | try_files $uri $uri/ /index.html | ☐ |

| Gzip 压缩 | gzip on + 完整 gzip_types | ☐ |

| 静态资源缓存 | hash 文件 immutable,html no-cache | ☐ |

| HTTPS | 443 监听 + HTTP 301 跳转 | ☐ |

| 安全头 | X-Frame-Options, CSP, HSTS | ☐ |

| 反向代理 | proxy_pass + 必要的 header 转发 | ☐ |

| 错误页面 | 自定义 404/50x 页面 | ☐ |

| 日志 | access_log + error_log | ☐ |

| 敏感文件拦截 | 拒绝访问 .env, .git 等 | ☐ |

敏感文件拦截配置


location ~ /\. {

    deny all;    # 拒绝 .git, .env 等隐藏文件

}

location ~* \.(env|log|sql|bak)$ {

    deny all;    # 拒绝访问敏感后缀文件

}


五、常见问题排查

Q1:改了配置不生效?


# 检查配置语法

nginx -t

  


# 重新加载(不中断服务)

nginx -s reload

  


# 查看实际加载的配置文件

nginx -T

Q2:502 Bad Gateway

原因:Nginx 无法连接到后端服务。

排查:


# 检查后端服务是否存活

curl http://backend:8080/health

  


# 检查 Nginx 错误日志

tail -f /var/log/nginx/error.log

Q3:504 Gateway Timeout

原因:后端响应超时。


location /api/ {

    proxy_pass http://backend:8080/;

    proxy_connect_timeout 10s;   # 连接超时

    proxy_send_timeout 30s;      # 发送超时

    proxy_read_timeout 60s;      # 读取超时(按业务调整)

}

Q4:WebSocket 连接失败


location /ws/ {

    proxy_pass http://backend:8080;

    proxy_http_version 1.1;

    proxy_set_header Upgrade $http_upgrade;

    proxy_set_header Connection "upgrade";

    proxy_set_header Host $host;

    proxy_read_timeout 86400s;   # WebSocket 长连接需要更长超时

}


总结

| 场景 | 关键配置 |

|------|----------|

| SPA 路由 404 | try_files $uri $uri/ /index.html |

| 跨域 | proxy_pass 反向代理 |

| 体积优化 | gzip + brotli |

| 缓存策略 | hash 文件 immutable + html no-cache |

| HTTPS | ssl_certificate + HTTP 301 跳转 |

| 多项目 | server_name 虚拟主机 / alias 子路径 |

| 安全 | 安全头 + 敏感文件拦截 |

| 负载均衡 | upstream + 健康检查 |

掌握这些配置,前端独立完成生产环境部署不再是问题。建议把本文的配置模板保存下来,作为项目部署的起点。

如果觉得有帮助,点赞收藏,部署时翻出来直接用 👍