# AWS CloudFront proxy

> A CloudFront Function and one cache behavior on the distribution that serves your site, sending the browser SDK's calls on the issued path to Fingerly.

Last updated: 2026-09-25

A CloudFront Function and a cache behavior for the distribution that already serves your site. The behavior answers one path the dashboard issued, on your own domain, and sends only the browser SDK's submissions to Fingerly as its origin. It is not an open proxy: the origin is `api.fingerly.io`, the function rewrites every request to one URL on it, and every other 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 **AWS CloudFront** under **How it runs**. Copy the [proxy key](https://docs.fingerly.io/docs/proxy-integrations#proxy-keys) 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: Create the function

**CloudFront > Functions > Create function**, runtime `cloudfront-js-2.0`. Paste the function the dashboard prints, `function.js`, whose `PATH` holds your path, then save and publish. The same function runs on both the viewer request and the viewer response.

### Step 3: Add the origin

In the distribution that serves your site. The custom header carries the proxy key, and CloudFront always overwrites a viewer-sent header of the same name, so a visitor cannot supply their own.

```text Origin
Origin domain:  api.fingerly.io
Protocol:       HTTPS only
Custom header:  x-fingerly-proxy-key = fly_px_production_…
```

### Step 4: Add the cache behavior

Your path exactly, with no wildcard, and the function on both events.

```text Cache behavior
Path pattern:            /k7x2qm4a
Origin:                  api.fingerly.io
Viewer protocol policy:  HTTPS only
Allowed HTTP methods:    GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
Cache policy:            CachingDisabled
Origin request policy:   AllViewerExceptHostHeader
Viewer request:          CloudFront Functions, the function above
Viewer response:         CloudFront Functions, the function above
```

### Step 5: Point the browser SDK at the path

```ts 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 `aws-cloudfront/1.0.0`, with its requests over the last 30 days.

## With CloudFormation

The dashboard also prints `cloudformation.yaml`: the same function, plus the parameter, origin and cache behavior to merge into your distribution's template. Pass the key as the `FingerlyProxyKey` parameter. It is `NoEcho`, so the key stays out of the template and out of version control.

> **Note:** Anyone allowed to read the distribution's configuration can read the origin header, so limit `cloudfront:GetDistributionConfig` accordingly.

## How the visitor's address is trusted

The function sends `event.viewer.ip`, the address CloudFront accepted the connection from, as `x-fingerly-client-ip`. It is not read from any header, so the visitor cannot change it.

> **Warning:** Put nothing in front of CloudFront for this path. Behind another proxy, the viewer address would be that proxy's.

The proxy key lives in the origin configuration, not the function, so unlike the other templates there is no key check in the code. Without the origin header, the API refuses every call with `401` and `proxy_key_missing`, because the function still sends the visitor's details. Add the header before routing traffic.

## What it sends and relays

Upstream, to `https://api.fingerly.io/api/v1/ingest` only: the viewer's request with every cookie, `Authorization` and any visitor-sent `x-fingerly-*` header removed, plus the `x-fingerly-*` headers the function sets (the visitor's address, the page origin, the user agent and `x-fingerly-proxy-version: aws-cloudfront/1.0.0`) and the proxy key from the origin's custom header. Other methods answer `405` with `Allow: POST`.

CloudFront Functions cannot read request bodies, so the body passes through unchanged and CloudFront's own request limits apply rather than a 1 MiB cap. The API enforces its own limit.

Back to the page: the viewer response keeps only `content-type`, `x-request-id`, `ratelimit-limit`, `ratelimit-remaining` and `retry-after` (and the framing headers CloudFront manages), drops every cookie and sets `cache-control: no-store`. CloudFront adds its own `Via` and `X-Cache` style headers after the function runs; none of them names Fingerly.
