提供静态站点
提供文件是 Pingclair 的另一半工作。本页从 root 和 file_server 开始,一路加上
压缩、缓存头、范围请求和单页应用所需回退,并展示每一步服务器实际返回什么。
🧾 开始之前
Section titled “🧾 开始之前”- 已经安装并运行 Pingclair(安装),实验期间先停掉服务:
sudo pc service stop。 - 一个要提供服务的目录。示例用
/srv/site。
📁 提供目录
Section titled “📁 提供目录”http://:8080 { root * /srv/site file_server}sudo cp Pingclairfile /etc/Pingclair/Pingclairfilesudo pingclair validate /etc/Pingclair/Pingclairfilesudo systemctl restart pingclaircurl -i http://localhost:8080/HTTP/1.1 200 OKContent-Type: text/html; charset=utf-8Last-Modified: Tue, 22 Sep 2026 04:37:54 GMTETag: "5e-6ab20622"Accept-Ranges: bytesroot * 为每个请求设置站点根目录,file_server 从那里提供服务。不存在的路径
返回 404。
http://:8080 { root * /srv/site encode zstd gzip file_server}参数按偏好顺序排列。同一个 36 KB 文本文件,用三种 Accept-Encoding 请求,在这套
配置上实测:
zstd 200 65 bytes content-encoding: zstdgzip 200 301 bytes content-encoding: gzipidentity 200 36000 bytes (no content-encoding)Brotli 在代理响应上没有实现,要求它会得到编译错误而不是静默降级:
Error: ❌ Configuration Error: Compile error: Unsupported feature: `encode br`: Brotli is not implemented for proxied responses; use `encode zstd gzip`消息给出了替代方案,这正是重点:要求服务器无法兑现的东西,配置根本不会运行。
file_server 已经会响应条件请求 —— 上面的 ETag 和 Last-Modified 就是客户端在
If-None-Match 或 If-Modified-Since 里回送的值。保存多久由你决定,而且应该写在
它成立的那些路径上:
http://:8080 { root * /srv/site encode zstd gzip header Cache-Control "public, max-age=60"
@assets path /assets/* header @assets Cache-Control "public, max-age=31536000, immutable"
file_server}实测:页面上是 Cache-Control: public, max-age=60,/assets/* 上是
public, max-age=31536000, immutable。只有文件名随内容变化时,immutable 才诚实,
这也正是构建工具在文件名里加哈希的原因。
范围请求不需要任何配置;请求前十个字节的客户端就能拿到它们:
HTTP/1.1 206 Partial ContentContent-Length: 10Content-Range: bytes 0-9/36000🧭 单页应用
Section titled “🧭 单页应用”在浏览器里做路由的应用,需要所有未知路径都返回入口文档,同时真实文件继续正常 提供:
http://:8080 { root * /srv/site try_files {path} /index.html file_server}实测:/assets/big.txt 仍然以自己的内容返回 200,/some/spa/route 用
index.html 返回 200。没有那行 try_files,第二个请求就是 404。
🗂️ 目录列表
Section titled “🗂️ 目录列表”file_server browse 会为没有 index 文件的目录渲染列表:
http://:8080 { root * /srv/site file_server browse}列表会列出条目,所以 /assets/ 会在 Index of 标题下显示 big.txt。除非这个
目录本来就该这样被读,否则不要加 browse。
🔒 隐藏文件
Section titled “🔒 隐藏文件”⚠️ 点开头的文件和其他文件一样会被提供:在上面的配置里 .hidden 返回了 200,
.git、.env 和编辑器备份就是这样跑到公网上的。要把它们挡在外面,就让响应发生
在文件服务器之前:
http://:8080 { root * /srv/site
@hidden path /.* respond @hidden "Not found" 404
file_server}实测:/.hidden 返回 404,而 / 和 /assets/big.txt 仍然是 200。用 404
而不是 403 是刻意的 —— 403 等于确认该文件存在。
⚠️ 出问题时
Section titled “⚠️ 出问题时”Unsupported feature: 'encode br'。 Brotli 被按名字拒绝;请用encode zstd gzip。Unknown directive 'file_server: …'。 该选项不存在,validate会指明它 拒绝的写法,而不是忽略它。- 出来的是目录列表而不是页面。 该目录没有
index.html,要么是你要的效果, 要么是文件漏放了。 - 应用负责的路由返回
404。 缺单页回退:try_files {path} /index.html。 - 重载后新页面不出现。 重载应用的是策略,不是新的监听器;文件本身按请求读取, 所以加文件是即时的,移动监听器不是,见 以服务方式运行。
- 反向代理一个应用:服务器的另一半。
file_server:指令参考。try_files:回退是如何编译的。
