一、引言:http/1.1 的“并发瓶颈”
在 http/1.1 时代,浏览器对同一域名的并发请求数量有严格限制(通常为 6-8 个)。这意味着,如果你的页面需要加载 20 个 css 和 js 文件,浏览器必须分批次串行下载,导致:
- 首屏渲染延迟:关键资源被非关键资源阻塞。
- tcp/tls 开销放大:每个请求都需经历 tcp 握手、tls 协商等固定开销。
- 服务器压力增加:处理大量小文件请求比处理少量大文件请求更消耗 cpu。
虽然 http/2 通过多路复用解决了此问题,但仍有大量用户停留在 http/1.1 网络环境(如老旧企业内网、部分移动网络)。
解决方案:在服务端将多个小文件动态合并成一个文件返回。这正是 ngx_http_concat_module 模块的核心价值。
💡 核心价值:
通过一次请求获取多个静态资源,将“多次小请求”变为“一次大请求”,完美规避 http/1.1 并发限制,显著提升页面加载速度!
二、核心模块:ngx_http_concat_module
1. 模块简介
ngx_http_concat_module 是由 淘宝(现阿里)开发的 nginx 第三方模块。它允许客户端在一个 url 中指定多个文件路径,nginx 在服务端将这些文件读取、拼接后,作为一个响应体返回。
⚠️ 重要提示:该模块不是 nginx 官方内置模块,需要手动编译安装。
2. 工作原理
- 客户端请求:http://example.com/??style1.css,style2.css,script1.js
- nginx 处理:
- 解析 url 中 ?? 后的文件列表。
- 依次读取磁盘上的 style1.css, style2.css, script1.js。
- 将文件内容按顺序拼接成一个大的字符串。
- 根据第一个文件的 mime 类型(或自定义规则)设置 content-type。
- 返回合并后的响应。
- 浏览器接收:像处理普通 css/js 文件一样解析和执行。
三、安装与配置实战
step 1: 下载模块源码
cd /usr/local/src git clone https://github.com/alibaba/nginx-http-concat.git
step 2: 重新编译 nginx
前提:你必须拥有当前 nginx 的源码,并使用完全相同的编译参数。
# 进入 nginx 源码目录
cd /usr/local/src/nginx-1.24.0
# 使用 nginx -v 查看的原始参数,追加 --add-module
./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--with-http_ssl_module \
--with-http_v2_module \
--add-module=/usr/local/src/nginx-http-concat # ← 添加 concat 模块
make && sudo make installstep 3: 核心配置指令
在 server 或 location 块中添加以下配置:
server {
listen 80;
server_name example.com;
root /var/www/html;
location /static/ {
# 启用 concat 功能
concat on;
# 最大可合并的文件数量 (默认 10)
concat_max_files 20;
# 是否允许跨目录合并 (默认 off, 更安全)
concat_unique on;
# 合并后响应的 mime 类型 (默认以第一个文件为准)
# concat_types text/css application/javascript;
}
}指令详解
| 指令 | 默认值 | 说明 |
|---|---|---|
| concat on/off | off | 开启/关闭合并功能 |
| concat_max_files | 10 | 单次请求最多合并的文件数,防滥用 |
| concat_unique | on | 是否只允许合并同一目录下的文件(安全限制) |
| concat_types | - | 显式指定允许合并的 mime 类型 |
四、使用方法与最佳实践
1. 客户端调用方式
url 格式为:http://<domain>/<path>/??<file1>,<file2>,...,<filen>[?<version>]
- 双问号 ??:是模块的固定语法,用于区分普通路径。
- 逗号分隔:文件路径列表,相对于 root 或 alias 目录。
- 版本号:可在末尾添加查询参数用于缓存刷新。
<!-- 合并前:3个独立请求 --> <link rel="stylesheet" href="/static/css/reset.css" rel="external nofollow" > <link rel="stylesheet" href="/static/css/layout.css" rel="external nofollow" > <link rel="stylesheet" href="/static/css/theme.css" rel="external nofollow" > <!-- 合并后:1个请求 --> <link rel="stylesheet" href="/static/??css/reset.css,css/layout.css,css/theme.css?v=1.2.3" rel="external nofollow" >
2. 生产环境最佳实践
(1) 与构建工具结合
虽然 concat 提供了运行时灵活性,但在生产环境中,更推荐在构建阶段(webpack/vite)完成资源合并。原因如下:
- 零运行时开销:避免每次请求都进行文件读取和拼接。
- 更强的缓存控制:合并后的文件名可包含 hash,实现永久缓存。
- tree shaking:构建工具能进行代码分析,移除无用代码。
concat 模块更适合用于开发环境或无法修改前端代码的遗留系统。
(2) 安全加固
- 限制 concat_unique on:防止攻击者通过 ../../../etc/passwd 这样的路径遍历读取敏感文件。
- 精确匹配 location:只对特定的静态资源目录(如 /static/)开启 concat。
- 设置 concat_max_files:防止恶意请求合并过多文件导致 oom。
# 安全的配置示例
location ~ ^/static/(?<subdir>[^/]+)/(?<filename>.+)$ {
# 只允许访问 static 下的子目录
alias /var/www/assets/$subdir/;
# 仅对 js/css 目录开启 concat
if ($subdir = "js") {
concat on;
concat_types application/javascript;
}
if ($subdir = "css") {
concat on;
concat_types text/css;
}
}(3) 缓存策略
合并后的响应应设置长期缓存,并通过 url 版本号或 hash 来更新。
location /static/ {
concat on;
expires 1y;
add_header cache-control "public, immutable";
}五、常见问题与避坑指南
1.q: 合并 js 和 css 会出错吗?
a: 会!模块默认以第一个文件的 mime 类型作为整个响应的 content-type。如果第一个文件是 css,后续的 js 代码会被浏览器当作 css 解析,导致静默失败。务必确保一次请求只合并同类型的文件。
2.q: 为什么我的 url 里只有一个问号?不行?
a: 这是模块设计的特殊语法。单个 ? 会被 nginx 视为普通的查询字符串分隔符,而 ?? 是模块识别合并请求的唯一标识。
3.q: 能否替代 webpack 的代码分割(code splitting)
a: 不能。concat 是粗粒度的文件拼接,无法实现按需加载(lazy loading)。现代应用应优先使用 webpack/vite 的动态 import() 实现精细化的代码分割。
4.q: http/2 环境下还需要它吗?
a: 不需要。http/2 的多路复用特性已经解决了队头阻塞问题。在此环境下使用 concat 反而会破坏资源的独立缓存,得不偿失。
六、结语
到此这篇关于nginx合并客户端请求的实现的文章就介绍到这了,更多相关nginx合并客户端请求内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论