> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runconverge.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up a proxy

> Set up first-party tracking by passing Converge requests through your own domain

## Overview

By proxying requests to Converge through your own domain, Converge is no longer treated as a third-party script.
This has the following advantages:

* **First-party cookies**: When Converge runs on your main domain, we automatically set a first-party cookie.
  First-party cookies are not restricted by browser privacy features like Apple ITP. They are longer-lasting, allowing you to track longer user journeys.
* **Bypassing ad blockers**: Converge's domain can be blocked by ad blockers based on our domain name. Using your own domain lets you bypass these ad blockers.

## Installation instructions

There are two parts to proxying requests to Converge:

* **Hosting the script on your own domain** By proxying requests to `https://static.runconverge.com/pixels/*.js` the Converge script is loaded through your domain.
* **Proxying the tracking requests** By proxying requests to `https://app.runconverge.com/api/tr/*` the tracking requests are sent through your domain.

<Note>
  Ensure that the proxy sets the `X-Forwarded-For` header to the original IP address of the request.
</Note>

<Note>
  Ensure that the proxy sets the `X-Forwarded-Host` header to the domain name of your proxy.
</Note>

To let Converge know your proxied domain, update your Converge pixel snippet as follows:

<Steps>
  <Step>
    Update the `src` of the Converge script to point to your domain. Replace `xxxxxx` with your Converge pixel public token.

    ```html theme={null}
    <script src="https://yourdomain.com/cvg/static/pixels/xxxxxx.js" async></script>
    ```
  </Step>

  <Step>
    Include the `proxy` call to configure your proxied endpoints for static and tracking requests.

    ```javascript theme={null}
    cvg({method: "proxy", tracking: "https://yourdomain.com/cvg", static: "https://yourdomain.com/cvg/static"});
    ```

    This should happen before any tracking calls.
  </Step>
</Steps>

This would yield the following updated snippet:

```html theme={null}
<script src="https://yourdomain.com/cvg/static/pixels/xxxxxx.js" async></script>
<script>
window.cvg||(cvg=function(){cvg.process?cvg.process.apply(cvg,arguments):cvg.queue.push(arguments)},cvg.queue=[]);
cvg({method: "proxy", tracking: "https://yourdomain.com/cvg", static: "https://yourdomain.com/cvg/static"});
cvg({method:"track",eventName:"$page_load"});
</script>
```

### Setting up a proxy through Nginx

If you're using Nginx as your web server or reverse proxy, you can configure it to proxy requests to Converge. Below is a complete working configuration example.

```nginx theme={null}
server {
    listen 80;
    server_name cvg.yourdomain.com;

    # DNS resolver needed for proxy_pass to HTTPS upstreams
    resolver 8.8.8.8 ipv6=off valid=300s;

    # Forward original client information
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host  $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP         $remote_addr;

    # Use HTTP/1.1 to keep upstream connections open
    proxy_http_version 1.1;
    proxy_set_header Connection "";

    # =========================
    # 1) STATIC PIXELS
    #    Proxy /pixels/ to static.runconverge.com
    # =========================
    location ^~ /pixels/ {
        proxy_pass https://static.runconverge.com/pixels/;
        proxy_set_header Host static.runconverge.com;
        proxy_ssl_server_name on;
        proxy_ssl_name static.runconverge.com;

        # CORS headers for pixel JavaScript
        add_header Access-Control-Allow-Origin * always;
        add_header Access-Control-Allow-Methods "GET, OPTIONS" always;

        # Handle preflight requests
        if ($request_method = OPTIONS) {
            return 204;
        }
    }

    # =========================
    # 2) TRACKING REQUESTS
    #    Proxy /api/tr/ to app.runconverge.com
    # =========================
    location ^~ /api/tr/ {
        # Handle CORS preflight requests
        if ($request_method = OPTIONS) {
            add_header Access-Control-Allow-Origin  $http_origin always;
            add_header Access-Control-Allow-Credentials true always;
            add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
            add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
            add_header Access-Control-Max-Age 86400 always;
            add_header Vary "Origin, Access-Control-Request-Headers, Access-Control-Request-Method" always;
            return 204;
        }

        # Hide CORS headers from upstream to avoid conflicts
        proxy_hide_header Access-Control-Allow-Origin;
        proxy_hide_header Access-Control-Allow-Credentials;
        proxy_hide_header Access-Control-Allow-Methods;
        proxy_hide_header Access-Control-Allow-Headers;

        proxy_pass https://app.runconverge.com/api/tr/;
        proxy_set_header Host app.runconverge.com;
        proxy_ssl_server_name on;
        proxy_ssl_name app.runconverge.com;

        # CORS headers for actual responses
        add_header Access-Control-Allow-Origin $http_origin always;
        add_header Access-Control-Allow-Credentials true always;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
        add_header Vary "Origin, Access-Control-Request-Headers, Access-Control-Request-Method" always;
    }

    # Everything else returns 404
    location / {
        return 404;
    }
}
```

After setting up your Nginx configuration:

1. Test the configuration: `nginx -t`
2. Reload Nginx: `systemctl reload nginx` or `nginx -s reload`
3. Update your Converge pixel snippet to use your proxy domain:

```html theme={null}
<script src="https://cvg.yourdomain.com/pixels/xxxxxx.js" async></script>
<script>
window.cvg||(cvg=function(){cvg.process?cvg.process.apply(cvg,arguments):cvg.queue=[]);
cvg({method: "proxy", tracking: "https://cvg.yourdomain.com", static: "https://cvg.yourdomain.com"});
cvg({method:"track",eventName:"$page_load"});
</script>
```

#### FAQ

<Accordion title="I am getting 404/410 errors when making requests?">
  If you're getting 404 or 410 errors and don't see traffic reaching Converge's servers, this is typically caused by a missing DNS resolver directive.
  Add `resolver 8.8.8.8 ipv6=off valid=300s;` to your server block.
</Accordion>

### Setting up a proxy through Vercel rewrites

If your site is hosted on Vercel, you can use the vercel.json configuration file to easily set up a proxy to Converge.

```json theme={null}
{
  "rewrites": [
    {
      "source": "/cvg/static/:path*",
      "destination": "https://static.runconverge.com/:path*"
    },
    {
      "source": "/cvg/:path*",
      "destination": "https://app.runconverge.com/api/tr/:path*"
    }
  ]
}
```

<Accordion title="Advanced implementation">
  If you want to make sure that your proxy is only used for the pixels in your Converge workspace you can add some extra conditions to the configuration file.

  Replace `xxxxxx` with your Converge pixel public token and YY-YYYYYYY with the GTAG ID from Google Analytics 4 or Google Ads.

  ```json theme={null}
  {
    "rewrites": [
      {
        "source": "/cvg/static/pixels/xxxxxx.js",
        "destination": "https://static.runconverge.com/pixels/xxxxxx.js"
      },
      {
        "source": "/cvg/static/js",
        "has": [
          { "type": "query", "key": "tid", "value": "YY-YYYYYYY" }
        ],
        "destination": "https://static.runconverge.com/js"
      },
      {
        "source": "/cvg/sgtm",
        "has": [
          { "type": "query", "key": "ep.cvg_public_token", "value": "xxxxxx" }
        ],
        "destination": "https://app.runconverge.com/api/tr/sgtm"
      },
      {
        "source": "/cvg/:method",
        "has": [
          { "type": "query", "key": "public_token", "value": "xxxxxx" }
        ],
        "destination": "https://app.runconverge.com/api/tr/:method"
      }
    ]
  }
  ```
</Accordion>

Update your Converge pixel snippet to the following:

```html theme={null}
<script src="/cvg/static/pixels/xxxxxx.js" async></script>
<script>
window.cvg||(cvg=function(){cvg.process?cvg.process.apply(cvg,arguments):cvg.queue.push(arguments)},cvg.queue=[]);
cvg({method: "proxy", tracking: "https://yourdomain.com/cvg", static: "https://yourdomain.com/cvg/static"});
cvg({method:"track",eventName:"$page_load"});
</script>
```

After deploying the changes, verify that

* static scripts like the Converge pixel and GTAG's load from your own domain
* tracking requests to `/tr/*` are proxied through your domain
* the response headers from the `/tr/track` call include the `Set-Cookie` header which sets a `__cvg_1p_uid` cookie
