[{"data":1,"prerenderedAt":129},["ShallowReactive",2],{"doc:\u002Fdocs\u002Fproxies\u002Fnginx":3},{"page":4,"toc":111,"updated":128},{"path":5,"title":6,"seoTitle":7,"description":8,"blocks":9},"\u002Fdocs\u002Fproxies\u002Fnginx","nginx proxy","nginx First-Party Proxy for the Browser SDK","A location block for the nginx server that already serves your site: it answers the one path the dashboard issued and forwards the browser SDK's calls to Fingerly.",[10,13,18,85,88,90,92,96,98,102,105,107,109],{"type":11,"text":12},"p","A `location` block for the nginx server that already serves your site. 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,63,74],{"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 **nginx** 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 proxy location",[31,33,40],{"type":11,"text":32},"Save this as `\u002Fetc\u002Fnginx\u002Fsnippets\u002Ffingerly.conf`, with your path in place of the example.",{"type":34,"samples":35},"code",[36],{"label":37,"lang":38,"code":39},"\u002Fetc\u002Fnginx\u002Fsnippets\u002Ffingerly.conf","nginx","location = \u002Fk7x2qm4a {\n    # One line, readable only by root: set $fingerly_proxy_key \"fly_px_...\";\n    include \u002Fetc\u002Fnginx\u002Ffingerly-proxy-key.conf;\n\n    if ($request_method != POST) {\n        add_header Allow \"POST\" always;\n        add_header Cache-Control \"no-store\" always;\n        return 405;\n    }\n    if ($fingerly_proxy_key !~ \"^fly_px_(production|staging|development)_[A-Za-z0-9_-]+$\") {\n        add_header Cache-Control \"no-store\" always;\n        return 503;\n    }\n\n    set $fingerly_origin $http_origin;\n    if ($fingerly_origin = \"\") {\n        set $fingerly_origin \"$scheme:\u002F\u002F$http_host\";\n    }\n\n    client_max_body_size 1m;\n\n    proxy_pass https:\u002F\u002Fapi.fingerly.io\u002Fapi\u002Fv1\u002Fingest;\n    proxy_http_version 1.1;\n    proxy_ssl_server_name on;\n    proxy_ssl_verify on;\n    proxy_ssl_verify_depth 3;\n    proxy_ssl_trusted_certificate \u002Fetc\u002Fssl\u002Fcerts\u002Fca-certificates.crt;\n    proxy_redirect off;\n    proxy_connect_timeout 5s;\n    proxy_send_timeout 5s;\n    proxy_read_timeout 5s;\n\n    proxy_pass_request_headers off;\n    proxy_set_header Host api.fingerly.io;\n    proxy_set_header Content-Type $content_type;\n    proxy_set_header X-Api-Key $http_x_api_key;\n    proxy_set_header Idempotency-Key $http_idempotency_key;\n    proxy_set_header X-Fingerly-Proxy-Key $fingerly_proxy_key;\n    proxy_set_header X-Fingerly-Client-Ip $remote_addr;\n    proxy_set_header X-Fingerly-Origin $fingerly_origin;\n    proxy_set_header X-Fingerly-User-Agent $http_user_agent;\n    proxy_set_header X-Fingerly-Proxy-Version \"nginx\u002F1.0.0\";\n\n    proxy_hide_header Set-Cookie;\n    proxy_hide_header Cache-Control;\n    add_header Cache-Control \"no-store\" always;\n}",{"type":11,"text":41},"`proxy_ssl_trusted_certificate` points at the usual system CA bundle. Where your system keeps it elsewhere, such as `\u002Fetc\u002Fpki\u002Ftls\u002Fcerts\u002Fca-bundle.crt`, change the path.",{"title":43,"blocks":44},"Store the proxy key",[45,47],{"type":11,"text":46},"In its own file, readable only by root. If `\u002Fetc\u002Fnginx` is in a repository, add the file to its `.gitignore`.",{"type":34,"samples":48},[49],{"label":50,"lang":51,"code":52},"Terminal","bash","sudo install -m 600 -o root -g root \u002Fdev\u002Fnull \u002Fetc\u002Fnginx\u002Ffingerly-proxy-key.conf\necho 'set $fingerly_proxy_key \"fly_px_production_…\";' | sudo tee \u002Fetc\u002Fnginx\u002Ffingerly-proxy-key.conf >\u002Fdev\u002Fnull",{"title":54,"blocks":55},"Include it in your site",[56,58],{"type":11,"text":57},"Inside the `server` block that serves your pages.",{"type":34,"samples":59},[60],{"label":61,"lang":38,"code":62},"nginx.conf","server {\n    server_name www.example.com;\n    include \u002Fetc\u002Fnginx\u002Fsnippets\u002Ffingerly.conf;\n    # ...\n}",{"title":64,"blocks":65},"Reload nginx",[66,68,72],{"type":11,"text":67},"Behind a load balancer, set up the realip module first (below), so the visitor's address is right.",{"type":34,"samples":69},[70],{"label":50,"lang":51,"code":71},"sudo nginx -t && sudo systemctl reload nginx",{"type":11,"text":73},"nginx resolves `api.fingerly.io` when it loads the configuration, so a reload also picks up address changes.",{"title":75,"blocks":76},"Point the browser SDK at the path",[77,83],{"type":34,"samples":78},[79],{"label":80,"lang":81,"code":82},"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":84},"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 `nginx\u002F1.0.0`, with its requests over the last 30 days.",{"type":14,"level":15,"text":86,"id":87},"How the visitor's address is trusted","how-the-visitor-s-address-is-trusted",{"type":11,"text":89},"The proxy sends `$remote_addr`, the address of the connection nginx accepted, as `x-fingerly-client-ip`. Nothing the visitor sends can change it. When visitors reach nginx directly, that is the visitor.",{"type":11,"text":91},"Behind a load balancer or CDN, `$remote_addr` is the load balancer. Tell the realip module which addresses to trust and which header they write, in the `http` or `server` block:",{"type":34,"samples":93},[94],{"label":61,"lang":38,"code":95},"set_real_ip_from 10.0.0.0\u002F8;          # your load balancer's addresses\nreal_ip_header X-Forwarded-For;\nreal_ip_recursive on;",{"type":11,"text":97},"With `real_ip_recursive on`, nginx walks `X-Forwarded-For` from the right and takes the first address outside the trusted ranges, so entries a visitor adds on the left are ignored.",{"type":99,"tone":100,"text":101},"callout","warning","Never trust `0.0.0.0\u002F0`. Every visitor could then choose the address Fingerly sees.",{"type":14,"level":15,"text":103,"id":104},"What it sends and relays","what-it-sends-and-relays",{"type":11,"text":106},"Upstream, to `https:\u002F\u002Fapi.fingerly.io\u002Fapi\u002Fv1\u002Fingest` only: `content-type`, `x-api-key`, `Idempotency-Key` when present, and 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: nginx\u002F1.0.0`). `proxy_pass_request_headers off` makes this an allowlist: cookies, `Authorization` and any `x-fingerly-*` header a visitor forges never leave your server.",{"type":11,"text":108},"Bodies over 1 MiB are refused with `413`, redirects are not followed, and a key that is not shaped like a proxy key answers `503`. Other methods answer `405` with `Allow: POST`. If you prefer `limit_except POST { deny all; }` to the `if` block, they answer `403` instead; the browser SDK only posts, so either works.",{"type":11,"text":110},"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.",[112,113,116,118,120,122,124,126,127],{"id":17,"text":16,"level":15},{"id":114,"text":22,"level":115},"step-create-the-proxy",3,{"id":117,"text":29,"level":115},"step-add-the-proxy-location",{"id":119,"text":43,"level":115},"step-store-the-proxy-key",{"id":121,"text":54,"level":115},"step-include-it-in-your-site",{"id":123,"text":64,"level":115},"step-reload-nginx",{"id":125,"text":75,"level":115},"step-point-the-browser-sdk-at-the-path",{"id":87,"text":86,"level":15},{"id":104,"text":103,"level":15},"2026-09-25T18:31:31.000Z",1793630453196]