[{"data":1,"prerenderedAt":115},["ShallowReactive",2],{"doc:\u002Fdocs\u002Fproxies\u002Fcaddy":3},{"page":4,"toc":99,"updated":114},{"path":5,"title":6,"seoTitle":7,"description":8,"blocks":9},"\u002Fdocs\u002Fproxies\u002Fcaddy","Caddy proxy","Caddy First-Party Proxy for the Browser SDK","A handle block for the Caddy site that already serves your pages: it answers the one path the dashboard issued and forwards the browser SDK's calls to Fingerly.",[10,13,18,75,78,80,82,86,90,93,95,97],{"type":11,"text":12},"p","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.",{"type":14,"level":15,"text":16,"id":17},"heading",2,"Set up","set-up",{"type":19,"steps":20},"steps",[21,28,42,53,64],{"title":22,"blocks":23},"Create the proxy",[24,26],{"type":11,"text":25},"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](\u002Fdocs\u002Fproxy-integrations#proxy-keys) it shows once, and note the path it issued, such as `\u002Fk7x2qm4a`.",{"type":11,"text":27},"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.",{"title":29,"blocks":30},"Add the handle block",[31,33,40],{"type":11,"text":32},"First among the handlers in the site block that serves your pages, with your path in place of the example.",{"type":34,"samples":35},"code",[36],{"label":37,"lang":38,"code":39},"Caddyfile","caddy","www.example.com {\n    handle \u002Fk7x2qm4a {\n        header >Cache-Control no-store\n\n        @notpost not method POST\n        header @notpost Allow POST\n        respond @notpost 405\n\n        @nokey not vars_regexp {env.FINGERLY_PROXY_KEY} ^fly_px_(production|staging|development)_[A-Za-z0-9_-]+$\n        respond @nokey 503\n\n        request_body {\n            max_size 1MiB\n        }\n\n        request_header -X-Fingerly-*\n\n        @noorigin not header Origin *\n        request_header @noorigin Origin {scheme}:\u002F\u002F{hostport}\n\n        rewrite * \u002Fapi\u002Fv1\u002Fingest\n        reverse_proxy https:\u002F\u002Fapi.fingerly.io {\n            header_up Host {upstream_hostport}\n            header_up -Cookie\n            header_up -Authorization\n            header_up X-Fingerly-Proxy-Key {env.FINGERLY_PROXY_KEY}\n            header_up X-Fingerly-Client-Ip {client_ip}\n            header_up X-Fingerly-Origin {http.request.header.Origin}\n            header_up X-Fingerly-User-Agent {http.request.header.User-Agent}\n            header_up X-Fingerly-Proxy-Version caddy\u002F1.0.0\n\n            header_down -Set-Cookie\n\n            transport http {\n                dial_timeout 5s\n                response_header_timeout 5s\n            }\n        }\n    }\n\n    # ... the rest of your site ...\n}",{"type":11,"text":41},"`header_up Host {upstream_hostport}` is automatic for HTTPS upstreams since Caddy 2.11. It stays in the block for older versions.",{"title":43,"blocks":44},"Give Caddy the proxy key",[45,47],{"type":11,"text":46},"As the `FINGERLY_PROXY_KEY` environment variable of the Caddy process, never in the Caddyfile. With systemd, run `sudo systemctl edit caddy` and add these lines, or point `EnvironmentFile=` at a file readable only by root. In a container, pass it from your secret store.",{"type":34,"samples":48},[49],{"label":50,"lang":51,"code":52},"Service override","ini","[Service]\nEnvironment=FINGERLY_PROXY_KEY=fly_px_production_…",{"title":54,"blocks":55},"Restart Caddy",[56,58],{"type":11,"text":57},"A restart rather than a reload, so Caddy sees the new environment. Behind a load balancer, set `trusted_proxies` and `trusted_proxies_strict` first (below).",{"type":34,"samples":59},[60],{"label":61,"lang":62,"code":63},"Terminal","bash","sudo systemctl restart caddy",{"title":65,"blocks":66},"Point the browser SDK at the path",[67,73],{"type":34,"samples":68},[69],{"label":70,"lang":71,"code":72},"fingerly.ts","ts","import { load } from '@fingerly\u002Fweb-js'\n\nconst fingerly = await load({\n  apiKey: 'fly_pk_production_…',\n  endpoints: '\u002Fk7x2qm4a',\n})",{"type":11,"text":74},"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\u002F1.0.0`, with its requests over the last 30 days.",{"type":14,"level":15,"text":76,"id":77},"How the visitor's address is trusted","how-the-visitor-s-address-is-trusted",{"type":11,"text":79},"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.",{"type":11,"text":81},"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:",{"type":34,"samples":83},[84],{"label":37,"lang":38,"code":85},"{\n    servers {\n        trusted_proxies static 10.0.0.0\u002F8\n        trusted_proxies_strict\n    }\n}",{"type":87,"tone":88,"text":89},"callout","warning","Without `trusted_proxies_strict`, Caddy reads the header from the left, where a visitor can write anything.",{"type":14,"level":15,"text":91,"id":92},"What it sends and relays","what-it-sends-and-relays",{"type":11,"text":94},"Upstream, to `https:\u002F\u002Fapi.fingerly.io\u002Fapi\u002Fv1\u002Fingest` 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\u002F1.0.0`).",{"type":11,"text":96},"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`.",{"type":11,"text":98},"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.",[100,101,104,106,108,110,112,113],{"id":17,"text":16,"level":15},{"id":102,"text":22,"level":103},"step-create-the-proxy",3,{"id":105,"text":29,"level":103},"step-add-the-handle-block",{"id":107,"text":43,"level":103},"step-give-caddy-the-proxy-key",{"id":109,"text":54,"level":103},"step-restart-caddy",{"id":111,"text":65,"level":103},"step-point-the-browser-sdk-at-the-path",{"id":77,"text":76,"level":15},{"id":92,"text":91,"level":15},"2026-09-25T18:31:31.000Z",1793630453198]