🚀 前端必修课: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 + 健康检查 |
掌握这些配置,前端独立完成生产环境部署不再是问题。建议把本文的配置模板保存下来,作为项目部署的起点。
如果觉得有帮助,点赞收藏,部署时翻出来直接用 👍