A handle block for the Caddy site that already serves your pages. It answers one path the dashboard issued, on your own host, and forwards only the browser SDK's submissions to Fingerly. It is not an open proxy: the upstream is fixed in the configuration, and every other path and method is refused. Version 1.0.0.
Set up
Step 1: Create the proxy
In the dashboard, open Integration > Proxy keys and choose New proxy. Pick the environment of the public key it will forward, and Caddy under How it runs. Copy the proxy key it shows once, and note the path it issued, such as
/k7x2qm4a.The dashboard then prints the steps below, filled in with your proxy's own path and key. They stay under Show setup in the proxy's menu, without the key, which is never shown again.
Step 2: Add the handle block
First among the handlers in the site block that serves your pages, with your path in place of the example.
www.example.com { handle /k7x2qm4a { header >Cache-Control no-store @notpost not method POST header @notpost Allow POST respond @notpost 405 @nokey not vars_regexp {env.FINGERLY_PROXY_KEY} ^fly_px_(production|staging|development)_[A-Za-z0-9_-]+$ respond @nokey 503 request_body { max_size 1MiB } request_header -X-Fingerly-* @noorigin not header Origin * request_header @noorigin Origin {scheme}://{hostport} rewrite * /api/v1/ingest reverse_proxy https://api.fingerly.io { header_up Host {upstream_hostport} header_up -Cookie header_up -Authorization header_up X-Fingerly-Proxy-Key {env.FINGERLY_PROXY_KEY} header_up X-Fingerly-Client-Ip {client_ip} header_up X-Fingerly-Origin {http.request.header.Origin} header_up X-Fingerly-User-Agent {http.request.header.User-Agent} header_up X-Fingerly-Proxy-Version caddy/1.0.0 header_down -Set-Cookie transport http { dial_timeout 5s response_header_timeout 5s } } } # ... the rest of your site ... }header_up Host {upstream_hostport}is automatic for HTTPS upstreams since Caddy 2.11. It stays in the block for older versions.Step 3: Give Caddy the proxy key
As the
FINGERLY_PROXY_KEYenvironment variable of the Caddy process, never in the Caddyfile. With systemd, runsudo systemctl edit caddyand add these lines, or pointEnvironmentFile=at a file readable only by root. In a container, pass it from your secret store.[Service] Environment=FINGERLY_PROXY_KEY=fly_px_production_…Step 4: Restart Caddy
A restart rather than a reload, so Caddy sees the new environment. Behind a load balancer, set
trusted_proxiesandtrusted_proxies_strictfirst (below).sudo systemctl restart caddyStep 5: Point the browser SDK at the path
import { load } from '@fingerly/web-js' const fingerly = await load({ apiKey: 'fly_pk_production_…', endpoints: '/k7x2qm4a', })The SDK posts both of its calls to the path exactly as given. Once pages use it, the proxy's row in Proxy keys shows it last seen, running
caddy/1.0.0, with its requests over the last 30 days.
How the visitor's address is trusted
The proxy sends {client_ip} as x-fingerly-client-ip. With no trusted_proxies configured, that is the address of the connection Caddy accepted, and nothing the visitor sends can change it.
Behind a load balancer or CDN, trust only its addresses, and have Caddy read X-Forwarded-For from the right so entries a visitor adds on the left are ignored:
{
servers {
trusted_proxies static 10.0.0.0/8
trusted_proxies_strict
}
}
What it sends and relays
Upstream, to https://api.fingerly.io/api/v1/ingest only: the request with Cookie, Authorization and every visitor-sent X-Fingerly-* header removed, plus the x-fingerly-* headers the proxy sets itself (the proxy key, the visitor's address, the page origin, the user agent and x-fingerly-proxy-version: caddy/1.0.0).
Bodies over 1 MiB are refused with 413, redirects are not followed, and a missing or malformed key answers 503. Other methods answer 405 with Allow: POST.
Back to the page: the API's status and body, with Set-Cookie dropped and Cache-Control: no-store. The API sends nothing naming Fingerly to proxied callers, so dropping headers here is defense in depth.