在传统的 Web 架构中,如果将图片、CSS、JS 等静态资源和 API 接口请求全部交给后端的 Tomcat、SpringBoot 或 PHP 处理,会造成极大的资源浪费。因为后端服务器更擅长处理复杂的业务逻辑,而不是通过 IO 读取静态文件。

什么是动静分离?

  • :动态请求(如 JSP, PHP, API 接口),交给后端应用服务器(Tomcat/uWSGI 等)处理。
  • :静态资源(如 HTML, CSS, JS, Images),由 Nginx 直接从磁盘读取返回,不再经过后端。

核心优势

  1. 减轻后端压力:后端只专注于业务逻辑,不再处理繁琐的静态文件 IO。
  2. 提升访问速度:Nginx 处理静态文件的并发能力是 Tomcat 的数倍,且配合 Gzip 和缓存,加载速度飞快。

架构示意图

1
2
3
4
5
6
7
8
9
10
🌐 用户浏览器


🚀 Nginx (端口 80/443)

├── ⚙️ 动态分流 (匹配 /api, .jsp)
│ └── ➡️ 转发至 Tomcat:8080

└── 🖼️ 静态分流 (匹配 .jpg, .css, .js)
└── 📂 读取本地 /data/static

环境准备

假设我们有一个 Java Web 项目,后端运行在 127.0.0.1:8080

1. 规划目录结构 (在 Nginx 服务器上)
我们需要将项目中的静态文件提取出来,上传到 Nginx 所在的服务器(如果是 Docker,需挂载目录)。

1
2
3
4
5
6
7
# 创建静态资源存放目录
mkdir -p /data/www/static/images
mkdir -p /data/www/static/css
mkdir -p /data/www/static/js

# 放入一张测试图片
echo "static image" > /data/www/static/images/logo.png

配置实战

编辑 Nginx 配置文件 nginx.confconf.d/default.conf

基础配置:基于后缀名分离

这是最常用的方式,通过正则表达式匹配文件后缀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 80;
server_name localhost;

# 1. 动态请求:转发给后端 Tomcat
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}

# 2. 静态请求:由 Nginx 直接处理
# 匹配以 gif, jpg, png, css, js 结尾的请求
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
root /data/www/static; # 指定静态文件所在的根目录
# 解释:访问 http://ip/images/logo.png
# Nginx 会去 /data/www/static/images/logo.png 找
}
}

进阶配置:基于目录分离

如果你的 URL 结构很规范,比如静态文件都在 /static/ 目录下,这种方式效率更高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80;

# 通用后端转发
location / {
proxy_pass http://127.0.0.1:8080;
}

# 只要路径包含 /static/ 就走本地磁盘
location /static/ {
root /data/www;
# 注意:这里有个大坑!关于 root 和 alias 的区别,见下文避坑指南
}
}

性能优化(Gzip + 缓存)

既然静态文件交给 Nginx 了,我们当然要开启“加速外挂”。

开启 Gzip 压缩

修改 /etc/nginx/nginx.confhttp 块:

1
2
3
4
5
6
7
8
9
10
http {
# 开启 gzip
gzip on;
# 只有超过 1k 的文件才压缩
gzip_min_length 1k;
# 压缩级别 1-9,建议 4-6,太高消耗 CPU
gzip_comp_level 4;
# 压缩的文件类型
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript image/jpeg image/gif image/png;
}

设置浏览器缓存 (Expires)

让静态文件在用户浏览器里缓存 7 天,减少重复请求。

1
2
3
4
5
6
7
8
9
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
root /data/www/static;
expires 7d; # 缓存7天
}

location ~ .*\.(js|css)?$ {
root /data/www/static;
expires 12h; # 缓存12小时
}

Root 与 Alias 的区别

配置动静分离时,新手最容易在路径映射上翻车。

场景:用户访问 http://ip/static/a.jpg,文件实际存在 /data/www/static/a.jpg

写法 A (使用 root):

1
2
3
location /static/ {
root /data/www;
}
  • 解析逻辑root路径 + 完整URL路径
  • 实际寻找/data/www + /static/a.jpg = /data/www/static/a.jpg (正确)

写法 B (使用 alias):

1
2
3
location /static/ {
alias /data/www/static/;
}
  • 解析逻辑alias路径 + 去掉/static/后的文件名
  • 实际寻找/data/www/static/ + a.jpg = /data/www/static/a.jpg (正确)

总结:

  • root 是把 location 后面配置的路径追加到 root 指定的目录后面。
  • alias 是把 location 后面配置的路径替换为 alias 指定的目录(相当于重命名)。

验证效果

  1. 检查配置语法nginx -t
  2. 重载配置nginx -s reload
  3. 浏览器测试
    • 打开 F12 开发者工具 -> Network 面板。
    • 访问一张图片。
    • 查看 Response Headers
    • 如果看到 Server: nginx 且包含 Content-Encoding: gzipCache-Control: max-age=...,说明动静分离及优化生效了!
    • 如果访问 404,请检查 Nginx 错误日志 (/var/log/nginx/error.log),通常是目录权限或路径拼写错误。