# Proxy integrations

> Route the browser SDK through a path on your own domain so content blockers do not stop identification, without losing the visitor's real network signals.

Last updated: 2026-09-17

Content blockers and privacy extensions often refuse requests to third-party APIs. When they refuse Fingerly's, the visitor is simply not identified. A proxy integration sends the SDK's requests to a path on your own site instead, where they look like the rest of your traffic.

## Why a Fingerly proxy is different

A naive reverse proxy would make every visitor arrive from your server's address, and every network signal would describe your server rather than the visitor. A Fingerly proxy authenticates with a **proxy key**, and only then may it tell Fingerly the visitor's real address, origin and user agent. A public key alone can never do that, so nobody can spoof those details with your public key.

## Options

| Option | Runs in | Best for |
| --- | --- | --- |
| [Cloudflare Worker](https://docs.fingerly.io/docs/sdks/cloudflare-worker) | Your Cloudflare account | Sites already on Cloudflare. No application changes. |
| [Node.js `createProxy`](https://docs.fingerly.io/docs/sdks/node#serve-the-browser-sdk-from-your-domain) | Your application | Next.js, Remix, Hono, SvelteKit, Nuxt and any runtime with web `Request` and `Response`. |
| Your own proxy | Anywhere | Other stacks. Follow the contract below. |

## Proxy keys

Proxy keys look like `fly_px_us_production_…`. Owners and admins issue them for one environment in **Integration > Proxy keys**, and each key is shown once, when it is issued. Only owners and admins can see and revoke them. A proxy key only forwards client requests: it cannot read events, and it must match the public key's organization, environment and region.

> **Warning:** Store the proxy key as a server secret. Never ship it to browsers.

## Point the SDK at your path

```ts fingerly.ts
import { load } from '@fingerly/web-js'

const fingerly = await load({
  apiKey: 'fly_pk_us_production_…',
  endpoints: '/metrics',                // your proxy's path
  fallbackToDefaultEndpoint: true,      // optional: try Fingerly directly if the proxy fails
})
```

The SDK appends `/api/v1/identify` to the path, and sends deferred reports to `/api/v1/events/{request_id}/supplement` under it. Omit `fallbackToDefaultEndpoint` to keep every request first-party.

> **Tip:** Choose a neutral path. Blockers look for words such as `fingerprint`, `tracking` and `fingerly`.

## Build your own proxy

Forward only the two client routes, as `POST`, to your region's API, and replace the visitor headers with values you resolved yourself.

| Incoming path | Forward to |
| --- | --- |
| `{prefix}/api/v1/identify` | `https://us.api.fingerly.io/api/v1/identify` |
| `{prefix}/api/v1/events/{request_id}/supplement` | `https://us.api.fingerly.io/api/v1/events/{request_id}/supplement` |

| Header to send | Value |
| --- | --- |
| `x-api-key` | The incoming `x-api-key` (the public key), unchanged. |
| `x-fingerly-proxy-key` | Your proxy key. |
| `x-fingerly-client-ip` | The visitor's IP address, from your load balancer or CDN. |
| `x-fingerly-origin` | The incoming `Origin`. |
| `x-fingerly-user-agent` | The incoming `User-Agent`. |
| `Idempotency-Key` | The incoming value, when present. |
| `Content-Type` | `application/json`. |

- Do not forward cookies or `Authorization` headers, and strip `Set-Cookie` from responses.
- Refuse other paths and methods, and cap request bodies at 1 MiB.
- Do not follow redirects from the upstream.
- Answer CORS preflights if the page and the proxy are on different origins.

> **Warning:** Resolve the visitor's IP from infrastructure you control, such as `CF-Connecting-IP` behind Cloudflare. Never trust the left-most `X-Forwarded-For` value, which the visitor can write.

## Data residency

A proxy never changes where data goes. The region is part of both keys, and a request forwarded to another region's API is refused.
