强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

03 - 静态文件服务 / Static File Serving

静态文件服务 / Static File Serving


🟢 基础 / Basics

最简配置

localhost {
    root * /var/www/html
    file_server
}

root 指定文件根目录,file_server 启用静态文件服务。

指定端口

:8080 {
    root * /home/user/website
    file_server
}

多站点

site-a.example.com {
    root * /var/www/site-a
    file_server
}

site-b.example.com {
    root * /var/www/site-b
    file_server
}

🟡 进阶 / Intermediate

启用目录浏览

localhost {
    root * /var/www/files
    file_server browse
}

browse 参数启用目录列表,适合文件下载站。

自定义浏览模板

localhost {
    root * /var/www/files
    file_server browse /path/to/custom-template.html
}

自定义模板使用 Go template 语法,变量包括:

  • .Name — 文件名
  • .Size — 文件大小
  • .ModTime — 修改时间
  • .IsDir — 是否为目录

处理 SPA(单页应用)

React / Vue / Svelte 等 SPA 框架需要所有路径回退到 index.html

localhost {
    root * /var/www/spa/dist
    try_files {path} /index.html
    file_server
}

try_files 会先查找请求路径对应的文件,找不到就回退到 /index.html

配置 Cache-Control

localhost {
    root * /var/www/static

    @static {
        path *.css *.js *.png *.jpg *.gif *.svg *.woff2
    }
    header @static Cache-Control "public, max-age=31536000, immutable"

    @html {
        path *.html
    }
    header @html Cache-Control "no-cache"

    file_server
}

路径重写(Path Rewriting)

localhost {
    root * /var/www

    # 去掉 .html 后缀
    @noext {
        not path */*
        not file {path}
    }
    rewrite @noext {path}.html

    file_server
}

这样 /about 会自动匹配 /about.html

禁止访问隐藏文件

localhost {
    root * /var/www

    @hidden {
        path */.*
    }
    respond @hidden 403

    file_server
}

🔴 高级 / Advanced

file_server 的内部机制

file_server 是一个 HTTP handler module,内部流程:

请求 /css/style.css
      │
      ▼
  Etag 计算 ──→ 如果 If-None-Match 匹配 → 304 Not Modified
      │
      ▼
  检查文件是否存在
      │
      ├─ 存在 → 读取文件 → Content-Type 检测 → 200
      │
      └─ 不存在 → try_files / 404

Precompressed 文件支持

localhost {
    root * /var/www/dist
    file_server {
        precompressed gzip br zstd
    }
    encode gzip zstd
}

precompressed 让 Caddy 直接发送预压缩好的 .gz / .br / .zst 文件,避免运行时压缩开销。

构建时生成预压缩文件:

# gzip
find /var/www/dist -type f \( -name "*.js" -o -name "*.css" -o -name "*.html" \) \
    -exec gzip -k {} \;

# brotli
find /var/www/dist -type f \( -name "*.js" -o -name "*.css" -o -name "*.html" \) \
    -exec brotli {} \;

结合模板引擎

Caddy 内置 templates 模块,可以在 HTML 中嵌入服务端逻辑:

localhost {
    root * /var/www
    templates
    file_server
}

HTML 文件中:

<h1>当前时间:{{ now }}</h1>
<p>请求路径:{{ .Request.URL.Path }}</p>
<p>随机数:{{ randInt 1 100 }}</p>

自定义 MIME 类型

Caddy 根据文件扩展名自动检测 Content-Type,如果遇到未知类型:

localhost {
    root * /var/www
    header Content-Type "application/octet-stream" {
        path *.bin
    }
    file_server
}

或修改 Caddy 内置的 MIME 数据库(需自编译)。

大文件分片 / Range Requests

Caddy 默认支持 Range 请求头,无需额外配置:

# 下载前 1024 字节
curl -H "Range: bytes=0-1023" https://example.com/large-file.zip -o part1

# 测试是否支持
curl -I https://example.com/large-file.zip | grep -i accept-ranges
# Accept-Ranges: bytes

视频播放器依赖 Range 请求实现 seek 功能,Caddy 开箱即用。

并发限流与静态文件

高流量场景下,静态文件服务可能需要配合限流:

{
    order rate_limit before file_server
}

import /etc/caddy/rate_limit.conf

localhost {
    root * /var/www
    rate_limit {
        zone static_zone {
            key    {remote_host}
            events 100
            window 1m
        }
    }
    file_server
}

小结 / Summary

层级内容
🟢 基础root + file_server,多站点
🟡 进阶browse、SPA try_files、Cache-Control、路径重写
🔴 高级precompressed、模板引擎、Range 请求、MIME 定制

下一章:反向代理 / Reverse Proxy