SDKs

Caddy proxy

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.

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

  1. 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.

  2. 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.

    Caddyfile
    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.

  3. Step 3: Give Caddy the proxy key

    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.

    Service override
    [Service]
    Environment=FINGERLY_PROXY_KEY=fly_px_production_…
    
  4. Step 4: Restart Caddy

    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).

    Terminal
    sudo systemctl restart caddy
    
  5. Step 5: Point the browser SDK at the path

    fingerly.ts
    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:

Caddyfile
{
    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.