跳转到内容

提供静态站点

提供文件是 Pingclair 的另一半工作。本页从 rootfile_server 开始,一路加上 压缩、缓存头、范围请求和单页应用所需回退,并展示每一步服务器实际返回什么。

  • 已经安装并运行 Pingclair(安装),实验期间先停掉服务: sudo pc service stop
  • 一个要提供服务的目录。示例用 /srv/site
http://:8080 {
root * /srv/site
file_server
}
终端窗口
sudo cp Pingclairfile /etc/Pingclair/Pingclairfile
sudo pingclair validate /etc/Pingclair/Pingclairfile
sudo systemctl restart pingclair
curl -i http://localhost:8080/
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Last-Modified: Tue, 22 Sep 2026 04:37:54 GMT
ETag: "5e-6ab20622"
Accept-Ranges: bytes

root * 为每个请求设置站点根目录,file_server 从那里提供服务。不存在的路径 返回 404

http://:8080 {
root * /srv/site
encode zstd gzip
file_server
}

参数按偏好顺序排列。同一个 36 KB 文本文件,用三种 Accept-Encoding 请求,在这套 配置上实测:

zstd 200 65 bytes content-encoding: zstd
gzip 200 301 bytes content-encoding: gzip
identity 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 已经会响应条件请求 —— 上面的 ETagLast-Modified 就是客户端在 If-None-MatchIf-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 Content
Content-Length: 10
Content-Range: bytes 0-9/36000

在浏览器里做路由的应用,需要所有未知路径都返回入口文档,同时真实文件继续正常 提供:

http://:8080 {
root * /srv/site
try_files {path} /index.html
file_server
}

实测:/assets/big.txt 仍然以自己的内容返回 200/some/spa/routeindex.html 返回 200。没有那行 try_files,第二个请求就是 404

file_server browse 会为没有 index 文件的目录渲染列表:

http://:8080 {
root * /srv/site
file_server browse
}

列表会列出条目,所以 /assets/ 会在 Index of 标题下显示 big.txt。除非这个 目录本来就该这样被读,否则不要加 browse

⚠️ 点开头的文件和其他文件一样会被提供:在上面的配置里 .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 等于确认该文件存在。

  • Unsupported feature: 'encode br' Brotli 被按名字拒绝;请用 encode zstd gzip
  • Unknown directive 'file_server: …' 该选项不存在,validate 会指明它 拒绝的写法,而不是忽略它。
  • 出来的是目录列表而不是页面。 该目录没有 index.html,要么是你要的效果, 要么是文件漏放了。
  • 应用负责的路由返回 404 缺单页回退:try_files {path} /index.html
  • 重载后新页面不出现。 重载应用的是策略,不是新的监听器;文件本身按请求读取, 所以加文件是即时的,移动监听器不是,见 以服务方式运行