Nginx-SSI合并服务器端文件
一、引言:静态页面的“重复代码”之痛
在纯静态网站或前后端未完全分离的项目中,我们常常会遇到这样的困境:
- 头部(Header)、尾部(Footer)、导航栏(Navigation)等公共组件在每个 HTML 页面中都要重复编写。
- 修改一个公共组件,需要手动同步到几十甚至上百个页面,极易出错和遗漏。
传统的解决方案是使用前端框架(如 React/Vue)进行组件化。但对于简单的营销页、文档站或遗留系统,引入重型框架显得“杀鸡用牛刀”。
有没有一种轻量级方案,能让静态 HTML 也拥有“包含”(include)
答案就是:SSI (Server Side Include) —— 一种由 Web 服务器在响应前动态拼接文件的技术。
💡 核心价值:
无需任何前端框架或后端语言,仅通过 Nginx 配置,即可实现静态 HTML 文件的模块化、复用和动态组装,大幅提升开发效率和可维护性!
二、SSI 是什么?工作原理揭秘
1. 基本概念
SSI (Server Side Include),即服务器端包含,是一种由 Web 服务器(如 Apache, Nginx)提供的功能。它允许在 HTML 文件中嵌入特殊的指令,服务器在将页面发送给客户端之前,会先执行这些指令,并将结果替换到原位置。
2. 核心指令:<!--#include file="..." -->
这是最常用的 SSI 指令,用于包含另一个文件的内容。
3. 工作流程
- 客户端请求
index.html。 - Nginx 发现
index.html中包含 SSI 指令。 - Nginx 读取被包含的文件(如
header.html,footer.html)。 - Nginx 将所有内容拼接成一个完整的 HTML 文档。
- 将最终的完整文档返回给客户端。
对浏览器而言,它收到的就是一个普通的、完整的 HTML 文件,完全感知不到背后的“拼装”过程。
三、Nginx SSI 模块配置实战
Step 1: 确认模块已安装
Nginx 的 SSI 功能由 ngx_http_ssi_module 模块提供。该模块默认已编译进官方 Nginx 包,通常无需额外安装。
可通过以下命令验证:
nginx -V 2>&1 | grep -o with-http_ssi_module
# 如果有输出,则表示模块已存在
Step 2: 开启 SSI 支持
在你的 server 或 location 块中添加配置:
server {
listen 80;
server_name example.com;
root /var/www/html;
# 方式一:为特定类型文件开启 SSI
location ~* \.shtml$ {
ssi on; # 开启 SSI
ssi_silent_errors on; # 静默处理错误,不显示错误信息
ssi_types text/html; # 指定处理的 MIME 类型
}
# 方式二:为整个站点开启(谨慎使用)
# location / {
# ssi on;
# }
}
关键指令详解
| 指令 | 默认值 | 说明 |
|---|---|---|
ssi on/off |
off | 核心开关,启用/禁用 SSI 处理 |
ssi_silent_errors on/off |
off | 开启后,SSI 指令执行错误时不会在页面上显示 [an error occurred while processing the directive] |
ssi_types |
text/html | 指定哪些 MIME 类型的响应体需要被 SSI 过滤器处理 |
✅ 最佳实践:不要直接对
.html文件开启 SSI!这会导致 Nginx 对每一个 HTML 请求都进行扫描,带来不必要的性能开销。推荐使用独立的后缀名,如.shtml。
Step 3: 编写 SSI 指令
创建你的页面文件 /var/www/html/index.shtml:
<!DOCTYPE html>
<html>
<head>
<title>我的主页</title>
</head>
<body>
<!--#include virtual="/common/header.shtml" -->
<main>
<h1>欢迎来到我的网站!</h1>
<p>这里是首页的独有内容。</p>
</main>
<!--#include virtual="/common/footer.shtml" -->
</body>
</html>
创建公共头部文件 /var/www/html/common/header.shtml:
<header>
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
<a href="/contact">联系</a>
</nav>
</header>
创建公共尾部文件 /var/www/html/common/footer.shtml:
<footer>
<p>© 2026 我的公司. 保留所有权利。</p>
</footer>
四、高级用法与技巧
1. file vs virtual 参数
-
file: 指定相对于当前文件所在目录的路径。<!-- 当前文件在 /pages/,此指令会查找 /pages/includes/nav.html --> <!--#include file="includes/nav.html" --> -
virtual: 指定相对于网站根目录(root)的 URI 路径。<!-- 无论当前文件在哪,此指令都会查找 /common/nav.html --> <!--#include virtual="/common/nav.html" -->
✅ 推荐使用
virtual,因为它更灵活,不受文件物理位置影响。
2. 条件判断与变量(有限支持)
SSI 还支持一些简单的编程逻辑,虽然远不如后端语言强大,但在特定场景下非常有用。
<!-- 设置变量 -->
<!--#set var="site_name" value="My Awesome Site" -->
<!-- 使用变量 -->
<title><!--#echo var="site_name" --></title>
<!-- 条件判断 -->
<!--#if expr="$HTTP_USER_AGENT = /Mobile/" -->
<link rel="stylesheet" href="mobile.css">
<!--#else -->
<link rel="stylesheet" href="desktop.css">
<!--#endif -->
3. 与缓存策略结合
SSI 是在每次请求时动态执行的,这意味着即使原始文件未变,响应也可能不同(例如,基于 User-Agent 的条件判断)。因此,对包含 SSI 指令的页面应谨慎设置缓存。
location ~* \.shtml$ {
ssi on;
# 不设置长期缓存,或根据业务逻辑设置较短的缓存时间
expires 10m;
}
五、常见问题与避坑指南
1. Q: 为什么我的 SSI 指令没有生效?
A: 最常见的原因有三个:
- 文件后缀名不对:确保你开启了对应后缀名(如
.shtml)的 SSI。 - 指令语法错误:检查注释格式是否为
<!--#directive ... -->,注意#后面的空格。 - 被包含文件路径错误:使用
virtual时,路径是相对于root的 URI,不是服务器上的绝对路径。
2. Q: SSI 会影响性能吗?
A: 会,但影响可控。Nginx 需要读取多个文件并进行字符串拼接。对于高并发、低复杂度的静态站,影响微乎其微。但如果一个页面包含了数十个文件,或者被包含的文件本身很大,则会产生可观的 I/O 开销。建议只用于组装少量、小型的公共组件。
3. Q: SSI 和 Nginx concat 模块有什么区别?
A: 两者目标完全不同。
- SSI:面向开发者,解决的是代码复用和维护问题。它在服务端组装的是结构化的 HTML 片段。
- Concat:面向网络传输,解决的是HTTP/1.1 并发限制问题。它在服务端合并的是同类型的静态资源(如多个 JS 文件)。
4. Q: 现代前端框架下还有必要用 SSI 吗?
A: 有!在以下场景依然非常有价值:
- 营销落地页(Landing Page):需要快速上线、SEO 友好、无复杂交互。
- 文档网站:如使用 Sphinx、MkDocs 生成的静态文档。
- 混合架构:部分页面由后端渲染(SSR),部分是纯静态,可以用 SSI 统一公共布局。
六、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)