Run one app on two domains
You'll make one binary answer for two host names and serve something different on each. The loader reads which domain it was asked on. A forged Host header can't change what it reads.
Before you start
Install from crates.io. You need no Node and no package manager.
cargo install snapfire_compiler
cargo install snapfire_fsr_cli
fsr --version
Every command and screenshot on this page was captured with fsr 0.x.
Declare the hosts#
A Host header is whatever the client wrote, so FSR ignores it until you say which names your deployment actually answers on.
[]
= ["acme.example", "widgets.example"]The list is an allowlist of exact names. A request's Host is lowercased and compared whole, port included. localhost:3000 and localhost are two different entries. Anything not on the list reads as null instead of the string the caller sent, so your code only ever sees a name from the list.
Read it in a loader#
ctx.host is string | null. Null means either you left the key out or the request named something you don't serve.
;
;
Both brands come from the same binary and plan. The server picks one before any markup is rendered.
Check the boot report#
While the key is set, fsr serve --verbose prints the list on every start.
Set the header in the proxy#
ctx.host can only be trusted as far as the server that terminates the connection. A value on your allowlist looks the same whether your proxy set it or someone typed it into a curl command.
With nginx:
proxy_set_header Host $host;
under a server_name that matches, plus a default server for the socket so a request naming something you don't serve is answered there rather than passed through. Without the default server a client can pick which of your brands it gets, so decide whether that matters for you.
Canonical links#
document.origin is your deployment's one preferred origin. It does not change with the host a request arrived on. A canonical link exists to collapse two names serving the same pages into one. If you emit one per host, you claim both are the original, which is the duplicate the link is meant to prevent.
If you genuinely serve a different site per host, build the canonical yourself from ctx.host. A loader module can export meta beside load. It receives what load returned and sets the document's title and head. Return the host from load so meta can read it:
;
;
;
;The host leaves an absolute URL in a canonical link as you wrote it.
Prerendering#
A loader that reads
ctx.hostis never prerendered, because each configured host gets a different answer and a prerendered file can hold only one. 100 covers keeping the rest of a page cacheable.
Next up: 310. Put a docs site inside your app, where a whole second application mounts inside this one.