Static hosts
public/ is a plain folder of files. Any host that serves a directory serves it. On a host that builds for you, two settings are the whole configuration:
| Setting | Value |
|---|---|
| Build command | curl -fsSL https://baudelaire.cstef.dev/install.sh | sh && ~/.local/bin/baudelaire build |
| Publish directory | public |
None of these platforms ships a Rust toolchain, so the build command installs the prebuilt binary first. It lands in ~/.local/bin, which is not on the build image’s PATH, hence the full path.
Netlify
netlify.toml at the repository root, so the settings live with the site:
[build]
command = "curl -fsSL https://baudelaire.cstef.dev/install.sh | sh && ~/.local/bin/baudelaire build"
publish = "public"
Cloudflare Pages
Same two values in the project’s build settings. To publish from your own CI instead, build and hand the folder to Wrangler:
baudelaire build
wrangler pages deploy public --project-name=my-site --branch=main
Vercel
Set Build Command to the line above and Output Directory to public, either in the project settings or as buildCommand and outputDirectory in vercel.json. Vercel reads neither rule file below; its redirects and headers go in vercel.json.
Your own web server
Copy public/ where the server roots. Or skip the copy and let baudelaire reconcile the directory over SSH: see SSH & SFTP.
TIP
With clean URLs a page lives atposts/hello/index.html. Most servers serve index.html for a directory already. If yours does not, turn on its pretty URL or index rewrite option.
Rule files
Netlify and Cloudflare Pages both read two files from the publish directory, and baudelaire writes either on request:
headers {
cache { }
}
redirects {
file #true
}
| Key | Type | Does |
|---|---|---|
headers |
block | Write _headers from the cache and CSP policies, plus any rule under rules. |
redirects { file } |
flag | Write _redirects from each page’s declared aliases. |
_headers
The file states the cache policy once, so a site that also uploads to a bucket cannot end up with two different answers. Fingerprinted assets get the immutable value, everything else the revalidating one:
headers {
cache {
immutable "public, max-age=604800, immutable"
default "public, max-age=300"
}
}
Both keys default to a sensible policy, so a bare cache { } is a complete declaration. The asset rule is only written when assets { fingerprint } is on: without it an asset keeps its authored name across builds and is exactly as mutable as a page.
A configured Content-Security-Policy is written into the same file.
Anything else the site wants to send is a rule of its own: a path pattern, and the headers it adds.
headers {
rules {
"/private/*" {
X-Robots-Tag "noindex"
}
"/*" {
X-Frame-Options "DENY"
}
}
}
These come first in the file, before the two derived rules, which end in a catch-all. Patterns are written relative to the site, so a site under a base path gets that path prefixed for it. A rules block replaces the whole list, like every other list in the config, and the flag on the block’s own line still stands in front of the file: headers #false { cache { } } states the policy for a bucket upload and writes no file at all.
WARN
headers { } on its own writes nothing. It needs a cache { } block, a CSP, or a rule of its own to have something to say, and an empty rule file means what the host already assumed.
_redirects
Each redirect entry in a page’s frontmatter becomes a real 301 line instead of an HTML stub:
/old-path /posts/hello/ 301
It replaces the stubs rather than joining them. Both hosts serve a static file in preference to a redirect rule, so a stub left at the old path would win and the rule would never fire.
NOTE
Leave both off for a host that reads neither, GitHub Pages included. The stubs work anywhere, which is why they are the default, and cache headers are then your host’s to set.