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

# First-party tracking

> Send browser events through your own domain to improve client-side event coverage.

First-party tracking sends browser events through your own domain instead of directly to Hightouch. This improves event coverage when browsers or privacy tools restrict third-party requests, such as Safari's Intelligent Tracking Prevention (ITP). You set it up by serving the SDK and collecting events from a domain you control.

First-party tracking has three parts. The first two are proxies you put in front of Hightouch; the third is a small service you host to issue cookies that survive browser privacy limits:

* **Proxy the JavaScript SDK** so the library loads from your domain instead of `cdn.hightouch-events.com`. See [Proxying the JavaScript SDK](#proxying-the-javascript-sdk).
* **Proxy events** so the SDK sends collected events to your domain instead of `hightouch-events.com`. See [Proxying events](#proxying-events).
* **Issue first-party, HTTP-only cookies** from a service on your domain so anonymous and user identifiers persist past Safari's cookie limits. See [Intelligent Tracking Prevention](#intelligent-tracking-prevention).

The first two parts improve delivery against ad blockers and third-party request blocking. The third keeps identity stable for returning visitors on browsers that enforce ITP. You can set them up independently, but most teams that need ITP mitigation do all three.

## Proxying

To use first-party tracking, set up a proxy so clients send their event data to your domain rather than directly to Hightouch.

### Before you begin

You'll need a certificate for whatever domain you plan to use for the proxy.

This guide uses [Amazon Cloudfront](https://aws.amazon.com/cloudfront), but any CDN or reverse proxy will work.

### Proxying the JavaScript SDK

Out of the box, the JavaScript SDK is served from cdn.hightouch-events.com. But you can serve the JavaScript SDK from your own domain instead.

First, create a [Cloudfront distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-creating-console.html) with the following settings:

| Setting | Value |
| - | - |
| Origin domain | `cdn.hightouch-events.com` |
| Origin path | Leave this blank |
| Name | Give your distribution a unique name |
| Enable Origin Shield | `No` |
| Compress objects automatically | `Yes` |
| Viewer protocol policy | `HTTPS only` |
| Allowed HTTP methods | `GET, HEAD` |
| Restrict viewer access | `No` |
| Cache key and origin requests | `Cache policy and origin request policy` |
| Cache policy | `CachingOptimized` |
| Origin request policy | `AllViewerExceptHostHeader` |
| Response headers policy | None |
| Function associations | No associations |
| Alternate domain name (CNAME) | The domain you want to expose to clients |
| Custom SSL certificate | Select a certificate that corresponds to the CNAME you entered |
| Supported HTTP versions | Enable `HTTP/2` and `HTTP/3` |
| Default root object | Leave this blank |
| Standard logging | `Off` |
| IPv6 | `On` |

Then, point your clients to the CNAME you configured. If using the JavaScript snippet, you can do this by replacing `cdn.hightouch-events.com` in the body of the snippet with your CNAME.

### Proxying events

By default, events are sent to `hightouch-events.com`. But you can configure Hightouch to send events to your own domain instead.

First, create a [Cloudfront distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-creating-console.html) with the following settings:

| Setting | Value |
| - | - |
| Origin domain | `us-east-1.hightouch-events.com` |
| Origin path | Leave this blank |
| Name | Give your distribution a unique name |
| Enable Origin Shield | `No` |
| Compress objects automatically | `Yes` |
| Viewer protocol policy | `HTTPS only` |
| Allowed HTTP methods | `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` |
| Restrict viewer access | `No` |
| Cache key and origin requests | `Cache policy and origin request policy` |
| Cache policy | `CachingDisabled` |
| Origin request policy | `AllViewer` |
| Response headers policy | None |
| Function associations | No associations |
| Alternate domain name (CNAME) | The domain you want to expose to clients |
| Custom SSL certificate | Select a certificate that corresponds to the CNAME you entered |
| Supported HTTP versions | Enable `HTTP/2` and `HTTP/3` |
| Default root object | Leave this blank |
| Standard logging | `Off` |
| IPv6 | `On` |

Then, set your SDK's `apiHost` setting to the CNAME you configured. For example, if you are using the Browser SDK:

```js theme={null}
HtEventsBrowser.load(
  { writeKey: "WRITE_KEY" },
  { apiHost: "events.example.com" }
);
```

## Intelligent Tracking Prevention

*Intelligent Tracking Prevention* (ITP) is an automatic feature of the Safari web browser, designed to limit user tracking. More specifically, the program is designed to prevent analytics libraries from using client-side Javascript to correlate user sessions. ITP configures the Safari browser in the following ways:

1. Third-party cookies are blocked by default.
2. First-party cookies (and localStorage) have a default expiry of 7 days.
3. A user revisiting a website will restart the 7 day expiry.

Unfortunately, most analytics libraries persist session information by:

1. Storing an identifier in a Cookie.
2. Storing an identifier in localStorage.

A legacy analytics installation, one not working to mitigate ITP, will collect less accurate session data for Safari users. For example, a single user visiting a website on both 01/01/2023 and 01/15/2023 will look like two different users. After Safari deletes the first cookie on 01/08/2023, the web SDK will be forced to make a new cookie and a new identifier for the session occurring on 01/15/2023. Downstream, your warehouse will likely see these sessions as two different users. This simple inaccuracy may impact otherwise useful metrics generated from analytics tracking.

### Mitigating ITP

When properly configured, Hightouch Events can store session identifiers outside of ITP's 7 day expiry. It does this by using first party, HTTPOnly cookies. These are not subject to Safari's 7 day expiry. These types of cookies are generally used for logging into a website and are less likely to be limited, even when dealing with anonymous users.

These "server cookies" must follow several rules put in place by ITP:

1. The server providing the HTTPOnly cookie must be on the same domain as the website.
2. If the server is on a subdomain of the website, its IP address must match the IP address that served the main HTML document.

In practice, this means that ITP mitigation requires customers to host some sort of web service responsible for returning HTTPOnly cookies to the browser. The service does not need to be complex; Hightouch has numerous example servers. The most important part of this **HTTPCookieService** is that it must be a first party service: routing a subdomain via DNS will not suffice.

In order for the HTTPCookieService to serve first party, HTTPOnly cookies, you'll need ***one of*** the following:

A **webserver** that handles both your HTML documents and your API. This would mean building the HTTPCookieService functionality into your regular webserver. As an example, you might already have a Java Spring server for handling regular website requests.

A **reverse proxy** that can forward path-dependent requests to different servers. This requires proxying your domain to both an HTTPCookieService and your regular webserver. As an example, you might already have something like NGINX that proxies HTML requests to one place and API requests to another.

A **CDN** that can run programmatic logic when matching certain path-dependent requests. If you already have a CDN, you may be able to add custom handlers without standing up new servers. This can be achieved with a variety of technologies like Amazon's Cloudfront, Lambda\@Edge, or API Gateway. Cloudflare Workers can also work.

### Client SDK setup

Once your HTTPCookieService is reachable on your domain, you may configure your Hightouch Events SDK to make use of it when generating Cookies:

```Javascript theme={null}
import { HtEventsBrowser } from '@ht-sdks/events-sdk-js-browser'

const htevents = HtEventsBrowser.load(
  { writeKey: '<YOUR_WRITE_KEY>'},
  {
    apiHost: "us-east-1.hightouch-events.com", // HtEvents API remains the same
    httpCookieServiceOptions: {
      clearUrl: '/ht/clear', // route hosted on *your* domain and infra
      renewUrl: '/ht/renew', // route hosted on *your* domain and infra
    }
  },
)

htevents.identify('hello world')

document.body?.addEventListener('click', () => {
  htevents.track('documentBodyClick')
})
```

### Server setup

The Events SDK expects to interact with a customer's HTTPCookieService that implements a specific spec for two routes. You can name the endpoints whatever you want, as long as you configure the client SDK to call those endpoints.

### An API for creating server and browser cookies

This route should look for the following **browser** cookies (from Events SDK):

* `request.headers.get("Cookie")["htjs_anonymous_id"]`
* `request.headers.get("Cookie")["htjs_user_id"]`

This route should return these values as **server** cookies:

* `response.cookie("htjs_anonymous_id_srvr", anonVal, {httpOnly:true, ...})`
* `response.cookie("htjs_user_id_srvr", userIdVal, {httpOnly:true, ...})`

If there are no browser cookies found, return any server cookies as **browser** cookies:

* `anonVal = request.headers.get("Cookie")["htjs_anonymous_id_srvr"]`
* `userIdVal = request.headers.get("Cookie")["htjs_user_id_srvr"]`
* `response.cookie("htjs_anonymous_id", anonVal, ...)`
* `response.cookie("htjs_user_id", userIdVal, ...)`

### An API for clearing server cookies

This route should look for **server** cookies and clean them:

* `res.cookie("htjs_anonymous_id_srvr", "", {maxAge: 0, httpOnly:true, ...});`
* `res.cookie("htjs_user_id_srvr", "", {maxAge: 0, httpOnly:true, ...});`

### API spec

The spec of the actual `request` and `response` payloads are kept intentionally vague. The spec should fit a variety of server environments.

The Events SDK only requires that the server: A) handles cookies and B) returns a `200` status code.

### Server examples

* [Express.js and NGINX](https://github.com/ht-sdks/events-sdk-js-mono/blob/master/packages/browser/src/core/http-cookies/server-examples/node-express-js.md)
* [Next.js and Vercel](https://github.com/ht-sdks/events-sdk-js-mono/blob/master/packages/browser/src/core/http-cookies/server-examples/node-next-js.md)
* [AWS Lambda and API Gateway](https://github.com/ht-sdks/events-sdk-js-mono/blob/master/packages/browser/src/core/http-cookies/server-examples/node-aws-lambda.md)

### More information

* Safari: [https://webkit.org/blog/9521/intelligent-tracking-prevention-2-3/](https://webkit.org/blog/9521/intelligent-tracking-prevention-2-3/)
