Static Web Server 2.44.1 配置参数详解

Static Web Server v2.44.1 配置参数全解:CLI/TOML/环境变量三种配置源与优先级实测、[general] 与 [advanced] 全参数表、压缩/认证/CORS/TLS/虚拟主机/内存缓存逐项实测与两套生产配置模板。

Static Web Server网络配置Ubuntu安全2026/10/21

Static Web Server 2.44.1 配置参数详解

目标:掌握 Static Web Server(SWS)v2.44.1 的全部配置参数——三种配置来源的优先级、每个参数的真实默认值、关键参数的实测效果验证,并能按场景套用生产配置模板。全部参数取自官方二进制 --help 实测回显与源码配置结构(src/settings/file.rs),实测环境为 Ubuntu 22.04(x86_64)。

一、环境信息

  • 操作系统:Ubuntu 22.04 LTS(x86_64)
  • 软件版本:Static Web Server v2.44.1(官方 musl 二进制,2026-10-07 构建;版本查询日期 2026-10-20)
  • 配置文件:/etc/static-web-server/static-web-server.toml(TOML 格式)
  • 服务托管:systemd(static-web-server.service),监听 0.0.0.0:8780,站点根目录 /var/www/html
  • 安装方式:官方 tar.gz 二进制 + systemd(安装过程见本专栏《Ubuntu 22.04 安装 Static Web Server 指南》)

二、前置条件

  1. 已按安装指南完成 SWS 安装与 systemd 托管,systemctl is-active static-web-server 返回 active(实测回显:active);
  2. 站点根目录存在演示文件(安装指南 4.4 节的 /var/www/html/index.html);
  3. 修改配置前建议备份:cp /etc/static-web-server/static-web-server.toml{,.bak};
  4. 生成 Basic 认证口令散列需要 htpasswd 工具:apt-get install -y apache2-utils(安装实测回显略,版本无要求)。

三、参数查看与修改方式

3.1 三种配置来源

SWS 的同一个参数有三个等价的设置途径,键名一一对应(以监听端口为例):

来源写法优先级
TOML 配置文件[general] 节内 port = 8780最高(显式写了就生效)
CLI 参数-p 8781 或 --port 8781中(配置文件未写该键时生效)
环境变量SERVER_PORT=8781中(与 CLI 同层,CLI 显式值覆盖环境变量)
内置默认—最低(以上均未设置时取默认值)

⚠️ 与多数软件「命令行最高」的习惯相反,SWS 是配置文件优先。实测验证:配置文件写 port = 8780,同时命令行传 -p 8781 启动,启动日志显示 server bound to tcp socket 0.0.0.0:8780——配置文件的 8780 生效,CLI 的 8781 被忽略。源码合并逻辑(src/settings/mod.rs)亦证实:配置文件键存在时无条件覆盖 CLI/环境变量值。

环境变量名即参数名的大写下划线形式并加 SERVER_ 前缀,--help 回显中每个参数下方都标注了对应变量(节选):

  -p, --port <PORT>
          Host port

          [env: SERVER_PORT=]
          [default: 80]

3.2 查看当前生效配置

SWS 没有「打印当前配置」的命令,查看生效配置靠两个手段:

  1. --help 查看全部参数与真实默认值(含环境变量名),完整回显见本专栏《Static Web Server 2.44.1 常用 CLI 命令详解》;
  2. 以 -g info 启动看日志:每个特性一行状态输出,如实测回显(节选):
INFO static_web_server::server: config file used: /etc/static-web-server/static-web-server.toml
INFO static_web_server::server: index files: index.html, index.htm
INFO static_web_server::directory_listing: directory listing: enabled=false
INFO static_web_server::compression: auto compression: enabled=true, formats=deflate,gzip,brotli,zstd, compression level=Default
INFO static_web_server::mem_cache::cache: in-memory cache (experimental): enabled=false

3.3 修改方式与生效条件

  1. 编辑 TOML 文件:vim /etc/static-web-server/static-web-server.toml;
  2. 重启服务生效(SWS 不支持配置热加载,SIGHUP 也不重读):systemctl restart static-web-server;
  3. 确认:journalctl -u static-web-server --no-pager -n 5 观察新配置对应的状态行,无 error during toml configuration file deserialization 即加载成功。

四、参数总览

4.1 [general] 通用参数表(v2.44.1 全量)

下表默认值均来自官方二进制 --help 实测回显:

参数类型默认值说明
host字符串::监听地址(如 127.0.0.1、0.0.0.0、::1)
port整数80监听端口
root路径./public静态文件根目录
log-level枚举error日志级别:error/warn/info/debug/trace
log-with-ansi布尔false日志 ANSI 彩色输出
cache-control-headers布尔true按文件类型自动附加 Cache-Control
compression布尔true按需压缩(gzip/deflate/brotli/zstd)
compression-level枚举default压缩级别:fastest/best/default
compression-static布尔false优先回源预压缩文件(.gz/.br/.zst)
page404路径./404.html404 页面(相对路径相对 root)
page50x路径./50x.html50x 页面
http2布尔false启用 HTTP/2(需 TLS)
http2-tls-cert路径—TLS 证书文件
http2-tls-key路径—TLS 私钥文件
https-redirect布尔falseHTTP→HTTPS 重定向(依赖 http2 开启)
https-redirect-host字符串localhostHTTPS 跳转目标主机
https-redirect-from-port整数80HTTP 重定向监听端口
https-redirect-from-hosts字符串localhost允许重定向的来源主机列表
security-headers布尔falseHSTS + X-Frame-Options + CSP 安全头
cors-allow-origins字符串""允许的 CORS 来源(逗号分隔,* 全放行)
cors-allow-headers字符串origin, content-type, authorization允许的请求头
cors-expose-headers字符串origin, content-type暴露给浏览器的响应头
index-files字符串index.html目录索引文件(逗号分隔,字符串非数组)
directory-listing布尔false目录列表
directory-listing-order整数6列表排序码:0-5(名称/时间/大小 升降序),6 无序
directory-listing-format枚举html列表格式:html/json
directory-listing-download列表空(禁用)目录打包下载格式,仅 targz
basic-auth字符串""Basic 认证 user-id:bcrypt-hash
fd整数—从指定文件描述符继承监听(systemd socket activation)
threads-multiplier整数1worker 线程数倍率(线程数=CPU 核数×n)
max-blocking-threads整数512阻塞线程池上限
grace-period整数0优雅停机宽限秒数(上限 255)
page-fallback路径""SPA 回退页(GET 未命中时 200 返回)
log-remote-address布尔false访问日志记录客户端地址
log-x-real-ip布尔false日志记录 X-Real-IP 头
log-forwarded-for布尔false日志记录 X-Forwarded-For 头
trusted-proxies列表空(信任所有)可信代理 IP 列表(X-Forwarded-For 取值来源)
redirect-trailing-slash布尔true目录路径缺尾斜杠时 308 重定向
ignore-hidden-files布尔true拒绝服务点开头的隐藏文件
disable-symlinks布尔true禁止符号链接逃逸
use-relative-root布尔false请求时解析 root(支持运行时替换软链)
health布尔false开启 /health 探活端点
accept-markdown布尔falseAccept: text/markdown 内容协商
text-charset布尔true文本响应自动附 charset=utf-8
metrics布尔false开启 /metrics Prometheus 指标
maintenance-mode布尔false维护模式(全站 503)
maintenance-mode-status整数503维护模式状态码
maintenance-mode-file路径""自定义维护页 HTML
memory-cache布尔(实验)false内存文件缓存总开关

4.2 [advanced] 高级参数表(仅配置文件可用)

参数结构说明
[[advanced.headers]]数组表自定义响应头:source(通配符匹配请求路径)+ headers(键值对)
[[advanced.redirects]]数组表URL 重定向:host(可选)/source/destination/kind(301 或 302)
[[advanced.rewrites]]数组表URL 重写:source/destination/redirect(可选 301/302,缺省为内部重写)
[[advanced.virtual-hosts]]数组表域名虚拟主机:host/root
[advanced.memory-cache]表内存缓存细则:capacity(条目数)/ttl(秒)/tti(秒)/max-file-size(字节)

关键差异解读:[general] 的键在 CLI/环境变量/TOML 三处均可设置;[advanced] 五组高级规则只能写在 TOML 配置文件里,CLI 与环境变量没有对应键,这也是复杂静态站点必须用配置文件模式的原因。

五、核心参数分类详解

5.1 网络与监听(host / port / threads-multiplier / grace-period)

监听地址与端口决定服务入口,线程与停机参数决定运行时行为。实测配置(环境变量文件含 SERVER_GRACE_PERIOD=2)后重启服务并观察启动日志:

systemctl restart static-web-server
journalctl -u static-web-server --no-pager -n 50 -o cat | grep -E "bound|listening|worker|blocking|grace"

实测回显(一轮 systemctl restart = 旧进程优雅退出 + 新进程启动,日志完整呈现两个阶段):

2026-10-20T16:38:30.652415834Z  INFO static_web_server::signals: grace period of 2s after the SIGTERM started
2026-10-20T16:38:32.653652113Z  INFO static_web_server::signals: grace period has elapsed
2026-10-20T16:38:32.653689717Z  INFO static_web_server::signals: delegating server's graceful shutdown
2026-10-20T16:38:32.672354877Z  INFO static_web_server::server: server bound to tcp socket 0.0.0.0:8780
2026-10-20T16:38:32.672356963Z  INFO static_web_server::server: runtime worker threads: 12
2026-10-20T16:38:32.672359192Z  INFO static_web_server::server: runtime max blocking threads: 512
2026-10-20T16:38:32.672390001Z  INFO static_web_server::server: grace period before graceful shutdown: 2s
2026-10-20T16:38:32.672530448Z  INFO static_web_server::server: http1 server is listening on http://0.0.0.0:8780

要点:worker threads: 12 来自默认 threads-multiplier = 1(本机 12 核);grace period: 2s 来自环境变量 SERVER_GRACE_PERIOD=2。实测优雅停机:systemctl stop static-web-server 在 2 秒宽限期下耗时 real 0m2.005s(日志即上例前三行的 grace period of 2s after the SIGTERM started → grace period has elapsed → delegating server's graceful shutdown)。有长连接下载流量时建议设 2–10 秒,避免在途请求被掐断。

5.2 静态资源与索引(root / index-files / redirect-trailing-slash / ignore-hidden-files / disable-symlinks)

实测效果(默认配置下):

curl -sI http://127.0.0.1:8780/assets | head -3   # 目录缺尾斜杠
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8780/.hidden.txt

实测回显:

HTTP/1.1 308 Permanent Redirect
location: /assets/
vary: accept-encoding
404
  • 尾斜杠 308 重定向由 redirect-trailing-slash = true(默认)驱动,保证目录 URL 规范化;
  • .hidden.txt 返回 404:ignore-hidden-files = true(默认)拒绝服务 dotfiles,防配置/密钥误发布;
  • disable-symlinks = true(默认)阻止通过软链跳出 root,除非站点确需软链共享文件,保持默认;
  • index-files 是字符串:"index.html, index.htm" 按顺序探测,实测日志 index files: index.html, index.htm。

5.3 压缩(compression / compression-level / compression-static)

compression = true(默认)按请求 Accept-Encoding 协商压缩,只作用于文本类 MIME。实测(19KB 文本文件):

curl -s -o /dev/null -D - -H "Accept-Encoding: gzip" http://127.0.0.1:8780/big.txt | grep -iE "content-encoding|content-type"
curl -s -o /dev/null -D - -H "Accept-Encoding: br" http://127.0.0.1:8780/big.txt | grep -i "content-encoding"
curl -s -o /dev/null -D - -H "Accept-Encoding: zstd" http://127.0.0.1:8780/big.txt | grep -i "content-encoding"
curl -s -o /dev/null -D - http://127.0.0.1:8780/big.txt | grep -iE "content-encoding|content-length"

实测回显:

content-type: text/plain; charset=utf-8
content-encoding: gzip
content-encoding: br
content-encoding: zstd
content-length: 19292

💡 技术拓展:压缩格式怎么选——

格式压缩率兼容性建议
gzip中全部浏览器兜底默认
deflate中全部浏览器很少单独用
brotli高现代浏览器静态资源首选
zstd高、解压最快新式客户端大文件分发优选

SWS 按客户端 Accept-Encoding 自动选择,四种全部内置,无需二选一;compression-level 建议 default,best 只用于带宽极度昂贵且 CPU 空闲的场景。compression-static = true 时优先找 .gz/.br/.zst 预压缩文件直接发送,适合构建期预压缩的静态站点。

5.4 缓存控制头(cache-control-headers)

cache-control-headers = true(默认)按文件类型自动附加缓存头。实测:HTML 返回 cache-control: max-age=86400(缓存 1 天),CSS 返回 cache-control: max-age=31536000(1 年,带哈希指纹的静态资源可长缓存),404 响应返回 cache-control: no-cache。该行为内置于源码 control_headers.rs 的类型清单,不可自定义明细,需要覆盖个别路径时配合 [advanced.headers] 使用(见 5.9)。

5.5 CORS(cors-allow-origins / cors-allow-headers / cors-expose-headers)

实测配置 cors-allow-origins = "*" 后的预检请求:

curl -s -o /dev/null -D - -X OPTIONS -H "Origin: https://example.com" -H "Access-Control-Request-Method: GET" http://127.0.0.1:8780/index.html | grep -iE "HTTP|access-control"

实测回显:

HTTP/1.1 204 No Content
access-control-allow-headers: content-type, authorization, origin
access-control-expose-headers: content-type, origin
access-control-allow-methods: HEAD, GET, OPTIONS
access-control-allow-origin: https://example.com

要点:cors-allow-origins 为空(默认)时不输出任何 CORS 头;* 全放行仅适用于公开只读资源;精确域名列表(逗号分隔,如 https://a.example.com, https://b.example.com)更安全。静态 API 化的文件服务才需要它,纯站点可保持关闭。

5.6 Basic 认证(basic-auth)

basic-auth 的值格式为 user-id:bcrypt-hash,散列用 htpasswd 生成(口令以 admin/q1w2e3r4 演示):

htpasswd -bnBC 10 "" 'q1w2e3r4' | tr -d ':\n'

实测回显:

$2y$10$2hjAJKs0Il2cJV9QbZW.feNdFHblEok5/UXMW3AMHns5Vq1lYFCb6

配置 basic-auth = "admin:$2y$10$2hjAJKs0Il2cJV9QbZW.feNdFHblEok5/UXMW3AMHns5Vq1lYFCb6" 后实测:

curl -sI http://127.0.0.1:8780/ | head -6
curl -s -o /dev/null -w "%{http_code}\n" -u admin:q1w2e3r4 http://127.0.0.1:8780/
curl -s -o /dev/null -w "%{http_code}\n" -u admin:wrongpass http://127.0.0.1:8780/

实测回显:

HTTP/1.1 401 Unauthorized
content-type: text/html; charset=utf-8
content-length: 318
accept-ranges: bytes
www-authenticate: Basic realm="Static Web Server", charset="UTF-8"
date: Tue, 20 Oct 2026 16:16:52 GMT
200
401

⚠️ 注意三点:散列中含 $ 字符,写进 systemd 环境变量文件时无需转义但不能放在 shell 双引号内展开;启动日志会提示 bcrypt is limited to 72 bytes per password(口令散列前 72 字节有效);多账号用逗号分隔(user1:hash1, user2:hash2)。

5.7 TLS 与 HTTP/2(http2 / http2-tls-cert / http2-tls-key / https-redirect)

生成自签证书并启用 HTTP/2(生产请用正规 CA 签发的证书):

openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -keyout /etc/static-web-server/local.key -out /etc/static-web-server/local.crt -days 365 -nodes -subj "/CN=localhost"
chown root:www-data /etc/static-web-server/local.key /etc/static-web-server/local.crt
chmod 640 /etc/static-web-server/local.key /etc/static-web-server/local.crt

配置 http2 = true、http2-tls-cert = "/etc/static-web-server/local.crt"、http2-tls-key = "/etc/static-web-server/local.key" 并重启后实测:

journalctl -u static-web-server --no-pager -n 2 -o cat | grep listening
curl -skI --http2 https://127.0.0.1:8780/ | head -3

实测回显:

INFO static_web_server::server: http2 server is listening on https://0.0.0.0:8780
HTTP/2 200
content-length: 170
content-type: text/html; charset=utf-8

要点:启用 http2 后同一端口只跑 HTTPS(HTTP/1.1 明文监听即停止);https-redirect = true 会在 80 端口(https-redirect-from-port 可改)再起一个重定向监听,把 HTTP 请求 301 到 HTTPS;私钥文件必须保证 www-data 可读,权限排障实录见安装指南 FAQ(Permission denied (os error 13))。

5.8 目录列表(directory-listing 系列)

directory-listing = true 后访问无索引文件的目录返回内置 HTML 列表页,实测 curl http://127.0.0.1:8780/docs/ 返回页脚为 Powered by Static Web Server (SWS) / static-web-server.net 的索引页,含名称/修改时间/大小三列(?sort=0/2/4 可点排序,对应 directory-listing-order 的 0-5 码,默认 6 不排序)。directory-listing-format = "json" 可输出机器可读 JSON;directory-listing-download = ["targz"] 让每个目录页出现整目录打包下载入口。文件分发站推荐开启,正式站点关闭(避免暴露目录结构)。

5.9 高级规则([advanced.headers] / redirects / rewrites / virtual-hosts / memory-cache)

五组规则仅 TOML 可用。以下配置全部实测生效:

[general]
host = "0.0.0.0"
port = 8780
root = "/var/www/html"
log-level = "info"
health = true
security-headers = true
memory-cache = true
http2 = true
http2-tls-cert = "/etc/static-web-server/local.crt"
http2-tls-key = "/etc/static-web-server/local.key"

[advanced.memory-cache]
capacity = 512
ttl = 60
tti = 30
max-file-size = 1048576

[[advanced.virtual-hosts]]
host = "app2.example.com"
root = "/var/www/app2"

[[advanced.headers]]
source = "**/*.html"
headers = { Cache-Control = "public, max-age=3600", X-Frame-Options = "DENY", X-Custom-Header = "sws-demo" }

[[advanced.redirects]]
source = "/old-page"
destination = "/index.html"
kind = 301

[[advanced.rewrites]]
source = "/v1/pretty"
destination = "/assets/style.css"

逐项实测回显(演示站使用 5.7 节生成的自签证书,故命令加 -sk 忽略自签告警;-H "Host: app2.example.com" 模拟第二个域名请求):

curl -skI --http2 https://127.0.0.1:8780/ | grep -iE "HTTP|x-frame|cache-control|x-custom|strict|content-security"
curl -skI https://127.0.0.1:8780/old-page | grep -iE "HTTP|location"
curl -sk -o /dev/null -w "%{http_code} %{content_type}\n" https://127.0.0.1:8780/v1/pretty
curl -sk --http2 -H "Host: app2.example.com" https://127.0.0.1:8780/
journalctl -u static-web-server --no-pager -n 40 -o cat | grep -E "in-memory|security headers"
HTTP/2 200
cache-control: public, max-age=3600
strict-transport-security: max-age=63072000; includeSubDomains; preload
x-frame-options: DENY
content-security-policy: frame-ancestors 'self'
x-custom-header: sws-demo
HTTP/2 301
location: /index.html
200 text/css
virtual host app2
2026-10-20T16:36:19.938873619Z  INFO static_web_server::security_headers: security headers: enabled=true
2026-10-20T16:36:19.938875481Z  INFO static_web_server::mem_cache::cache: in-memory cache (experimental): enabled=true, capacity=512, ttl=60, tti=30, max_file_size=1048576

逐项说明:

  • headers:source 用 glob 匹配请求路径(**/*.html 匹配全部 HTML),headers 键值对直接覆盖内置 Cache-Control 并附加自定义响应头(实测 x-custom-header: sws-demo 随每个 HTML 响应返回);
  • redirects:/old-page 返回 301 与 location: /index.html,kind 只接受 301/302;host 可选,用于仅对特定域名跳转;
  • rewrites:/v1/pretty 内容返回 CSS(200 text/css)而浏览器地址不变——重写是服务端内部映射;redirect 键填 301/302 时变为显式跳转;
  • virtual-hosts:Host: app2.example.com 命中后 root 切到 /var/www/app2,单实例托管多站点;未匹配任何条目时回落到 [general].root;
  • memory-cache:实验特性,capacity/ttl/tti/max-file-size 四值在启动日志确认(enabled=true, capacity=512, ttl=60, tti=30, max_file_size=1048576),把热点小文件缓存进内存减少磁盘 IO;版本升级可能变更行为,生产启用前先压测。

5.10 探活与指标(health / metrics)

在开启 health = true、metrics = true 且已配置 basic-auth(5.6 节口令,/metrics 与全站同受认证保护)的实例上实测:

curl -s -o /dev/null -w "%{http_code} %{size_download}B\n" http://127.0.0.1:8780/health
curl -s -u admin:q1w2e3r4 http://127.0.0.1:8780/metrics | head -4

实测回显:

200 2B
# HELP sws_http_connections_active Number of currently active HTTP connections.
# TYPE sws_http_connections_active gauge
sws_http_connections_active 1

/health 固定返回 200 与 2 字节正文且不产生访问日志(适合负载均衡探活不打爆日志);/metrics 输出 Prometheus 文本格式(连接数 gauge、请求延迟直方图 sws_http_request_duration_seconds_bucket 等),Prometheus 抓取配置 metrics_path: "/metrics" 即可。注意:未开 Basic 认证时 /metrics 无鉴权,公网环境应通过反向代理或网络策略限制访问(SWS 启动日志会主动警告)。

5.11 错误页与回退页(page404 / page50x / page-fallback)

page404/page50x 指定自定义错误页(相对路径相对 root 解析),实测 404 响应:

HTTP/1.1 404 Not Found
content-type: text/html; charset=utf-8
content-length: 312
vary: accept-encoding
cache-control: no-cache

page-fallback 是 SPA 神器:指定一个 HTML 后,所有 GET 未命中路径都返回该页(200 状态码),前端路由(React/Vue history 模式)不再 404。不配置则该特性不启用。

5.12 维护模式(maintenance-mode 系列)

maintenance-mode = true 后全站统一返回维护页,实测:

curl -sk --http2 -o /dev/null -w "%{http_code}\n" https://127.0.0.1:8780/

实测回显:

503

默认状态码 503(maintenance-mode-status 可改),对搜索引擎友好(503 表示临时不可用,爬虫会稍后再来)。配合 maintenance-mode-file 换成带公告的维护页。发布静态站新版本时:开维护模式 → 替换文件 → 关维护模式,全程对用户透明。

5.13 日志与调试(log-level / log-remote-address / log-with-ansi)

参数取值效果(实测)
log-levelerror/warn/info/debug/traceinfo 起输出请求日志;error 时几乎静默
log-remote-addresstrue请求日志附客户端地址:incoming request: method=GET uri=/docs/(实测行)
log-x-real-ip / log-forwarded-fortrue反代场景记录真实 IP 头,配合 trusted-proxies 防伪造
log-with-ansitrue交互终端彩色输出,journald 场景保持 false

生产建议 log-level = "warn"(只看异常),排障临时调 info/debug。

六、生产配置模板

6.1 模板一:小型文档站(1 核 / 1GB 内存 / 普通云盘)

[general]
host = "0.0.0.0"
port = 80
root = "/var/www/docs"
log-level = "warn"
compression = true
compression-level = "default"
cache-control-headers = true
redirect-trailing-slash = true
ignore-hidden-files = true
disable-symlinks = true
health = true

[advanced]

适用:个人/团队文档站、博客,1 核 1GB 足够支撑日均十万级 PV(SWS 常驻内存约 18MB 实测);health = true 供监控探活。

6.2 模板二:下载分发站(4 核 / 8GB 内存 / NVMe SSD,HTTPS + 认证 + 目录列表)

[general]
host = "0.0.0.0"
port = 443
root = "/data/downloads"
log-level = "warn"
http2 = true
http2-tls-cert = "/etc/static-web-server/site.crt"
http2-tls-key = "/etc/static-web-server/site.key"
basic-auth = "admin:$2y$10$2hjAJKs0Il2cJV9QbZW.feNdFHblEok5/UXMW3AMHns5Vq1lYFCb6"
compression = false
directory-listing = true
directory-listing-order = 4
directory-listing-format = "html"
directory-listing-download = ["targz"]
grace-period = 10
threads-multiplier = 1
metrics = true
health = true

[advanced]

适用:内部软件包/大文件分发。要点:大文件场景关闭按需压缩(compression = false,压缩已打包文件无意义且浪费 CPU);目录列表按大小升序(order = 4)+ 整目录 targz 打包下载;grace-period = 10 保证升级重启时在途下载完成;443 特权端口绑定需 systemd 托管(root 启动后自动降权到 User=)或用官方 socket activation 方案(fd 参数)。

6.3 模板三:SPA 前端站(2 核 / 4GB,反代后置)

[general]
host = "127.0.0.1"
port = 8780
root = "/var/www/spa"
log-level = "warn"
page-fallback = "/var/www/spa/index.html"
page404 = "/var/www/spa/404.html"
page50x = "/var/www/spa/50x.html"
compression = true
compression-static = true
security-headers = true
log-remote-address = true
log-forwarded-for = true
trusted-proxies = ["10.0.0.0/8"]
metrics = true

[[advanced.headers]]
source = "/assets/**"
headers = { Cache-Control = "public, max-age=31536000, immutable" }

[[advanced.headers]]
source = "/index.html"
headers = { Cache-Control = "no-cache" }

适用:前端构建产物托管,仅监听 127.0.0.1 由 Nginx/Caddy/Traefik 反代对外。要点:page-fallback 承接前端 history 路由;compression-static 配合构建期预压缩(vite plugin 生成 .gz/.br);安全头全开;带指纹的 assets 长缓存 + index.html 不缓存;trusted-proxies 限定反代网段防 X-Forwarded-For 伪造。

七、常见问题(FAQ)

Q1:启动即失败,报 invalid type: sequence, expected a string in general.index-files?

index-files 类型是字符串(逗号分隔),写成数组会反序列化失败。实测报错回显:

Caused by:
    0: error during toml configuration file deserialization
    1: invalid type: sequence, expected a string
       in `general.index-files`

改为 index-files = "index.html, index.htm"。通用排查法:该错误必带具体键名,按报错修正该键的类型即可。

Q2:改了配置文件为什么不生效?

SWS 不支持热加载,必须 systemctl restart static-web-server。另外确认改的是配置文件优先链路里真正生效的那一层:若环境变量文件里同名 SERVER_* 变量与 TOML 冲突,TOML 胜出(见 3.1 优先级实测)。

Q3:CLI 参数和配置文件同时设置,谁生效?

配置文件。实测:配置文件 port = 8780 + CLI -p 8781,启动日志 server bound to tcp socket 0.0.0.0:8780,CLI 值被忽略。

Q4:/metrics 401 或裸奔无鉴权?

开了 basic-auth 则 /metrics 同样要带凭据(curl -u admin:<password>);没开认证时 /metrics 任何人可读(启动日志会警告),公网务必收紧。

Q5:维护模式打开后管理员自己也被挡住怎么办?

维护模式作用于全部路径,没有白名单机制。替代方案:临时把 root 切到维护页目录,或用 [[advanced.headers]] + 反代层做灰度。发布窗口短的话直接改 maintenance-mode-status 为 503 保持 SEO 友好即可。

Q6:如何确认某个安全开关真的生效?

两步:启动日志核对状态行(如 disable symlinks: enabled=true、ignore hidden files: enabled=true);行为实测(如 curl -I http://127.0.0.1:8780/.hidden.txt 应 404)。本文所有开关均按此法双重验证。

八、参考链接