# Serve JavaScript SDK on Custom Domains

RudderStack serves the JavaScript SDK through Amazon S3, delivered via the Amazon CloudFront CDN.

This guide covers the steps to use your own domain instead of the RudderStack domains for serving the JavaScript SDK.

## Setup overview

Create a new distribution by following these steps:

1. Log in to your [AWS console](https://aws.amazon.com/console/).
2. Click **Services** and go to **Network & Content Delivery** > **CloudFront**.
3. Click **Create a CloudFront distribution**.

The following table gives a high-level overview of the required cache policy, origin request policy, and response headers policy for serving the JavaScript SDK:

| Cache policy | Origin request policy | Response headers policy (optional) |
| :------| :--------| :----------- | 
| [Cache policy settings](#cache-policy-settings) | [Origin request policy settings](#sdk-origin-request-policy-settings) |  `None` | 

## Step 1: Configure distribution

The following sections highlight the **required** distribution settings for serving the SDK.

#### Origin

{{< image src="images/user-guides/custom-domains/custom-domains-1.webp" alt="Custom domains distribution settings" >}}

The following table summarizes the settings:

| Field                  | Setting              |
| :--------------------- | :------------------- |
| Origin domain     | `cdn.rudderlabs.com` |
| Protocol | `HTTPS Only`           |
| HTTPS port | `443` |
| Minimum origin SSL protocol | `TLSv1.2` |
| Name | `cdn.rudderlabs.com` |
| Enable Origin Shield | `No` |

#### Default cache behavior settings

{{< image src="images/user-guides/custom-domains/custom-domains-2-new.webp" alt="Custom domains distribution settings" >}}

The following table summarizes the settings:

| Field     | Setting |
| :---------- | :---------------------- |
| Compress objects automatically | `Yes` |
| Viewer protocol policy | `Redirect HTTP to HTTPS` |
| Allowed HTTP methods | `GET`, `HEAD`, `OPTIONS`, `PUT`, `POST`, `PATCH`, `DELETE` |
| Restrict viewer access | `No` |

#### Cache key and origin requests

Select **Cache policy and origin request policy (recommended)** and configure the following settings:

{{< image src="images/user-guides/custom-domains/js-sdk-cache-key-origin-requests.webp" alt="Custom domains distribution settings" >}}

The following table summarizes the settings:

| Field | Setting |
| :------| :------|
| Cache policy | See [Cache policy settings](#cache-policy-settings) |
| Origin request policy | See [Origin request policy settings](#sdk-origin-request-policy-settings). | 
| Response headers policy | `None` | 

#### Cache policy settings

{{< image src="images/user-guides/custom-domains/js-sdk-cache-policy-settings.webp" alt="Cache policy settings for serving JS SDK" >}}

The following table summarizes the settings:

##### **Details**

| Field     | Setting |
| :---------- | :-------- |
| Name | Name of your cache policy, for example,`jssdk-caching-policy`. | 
| Description | Add your policy description here. |

##### **TTL settings**

| Field     | Setting | Notes |
| :---------- | :-------- | :----| 
| Minimum TTL | `0` (in seconds) | Ensures the no-store header is respected. |
| Maximum TTL |  `31536000` (in seconds) | Allows CloudFront to use the **Cache-Control** value from origin if it is greater than `0`. |
| Default TTL |  `300` (in seconds) | Used when origin does not include **Cache-Control** headers. |

For the RudderStack CDN, CloudFront is configured with a long default TTL (one year) to maximize cache efficiency, and rely on invalidations during releases to refresh content.

RudderStack recommends configuring the prescribed [TTLs settings](#ttl-settings) when setting up a custom domain. For the reasoning behind these recommendations, see the [AWS documentation](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html#ExpirationDownloadDist).

This ensures that the custom domain’s CloudFront distribution always respects the origin **Cache-Control** and **Age** headers as the TTLs defined by RudderStack. Since these headers are managed by RudderStack, they may be updated in the future to improve reliability and correctness.

In cases where you need to control browser-side caching policies instead of relying solely on RudderStack’s origin settings, you can configure a CloudFront response headers policy with the **Override origin** option enabled for **Cache-Control** headers.

{{< warning >}}
If you are using a different cloud provider for your custom domain, make sure to set the maximum TTL value to `300`, that is, 5 minutes.
{{< /warning >}}

##### **Cache key settings**

| Field     | Setting |
| :---------- | :-------- |
| Headers | `None` |
| Query strings | `All` |
| Cookies | `None` |

##### **Compression support**

Select both Gzip and Brotli.

Then, click **Create** to generate the cache policy.

#### Origin request policy settings {#sdk-origin-request-policy-settings}

Create a new origin request policy with the following settings:

{{< image src="images/user-guides/custom-domains/js-sdk-origin-request-policy.webp" alt="Origin request policy settings" >}}

| Field       | Setting |
| :------- | :------------------- |
| Name | Enter the origin request policy name, for example, `jssdk-origin-request-policy` | 
| Headers | `Include the following headers` | 
| Add header | <ul><li>`Access-Control-Request-Headers`</li><li>`Access-Control-Request-Method`</li><li>`Content-Encoding`</li><li>`Origin`</li></ul> |
| Query strings | `All` |
| Cookies | `None` |

#### Additional distribution settings

{{< customreadfile "/includes/custom-domains/additional-distribution-settings.md" >}}

## Step 2: Deploy distribution

{{< customreadfile "/includes/custom-domains/deploy-distribution.md" >}}

## Step 3: Serve SDK

See the following sections to serve the SDK depending on your installation method:

- [SDK installation using CDN](#cdn)
- [SDK installation using NPM](#npm)

#### CDN

{{< tabs tabTotal="2" >}}
{{% tab tabName="Latest" %}}
Paste the following [snippet]({{< ref "sources/event-streams/sdks/rudderstack-javascript-sdk/installation.md#using-cdn" >}}) in your website's `<head>` section and update the URL from:

```javascript
var sdkBaseUrl = "https://cdn.rudderlabs.com";
```

to:

```javascript
var sdkBaseUrl="https://<YOUR_CUSTOM_DOMAIN>"
```

Replace `<YOUR_CUSTOM_DOMAIN>` in the above snippet with your domain URL.

{{< warning >}}
Make sure that the [distribution settings](#step-1-configure-distribution) are configured correctly and **no** paths are blocked. Otherwise, the events may not flow through correctly.

For example, paths like `<YOUR_CUSTOM_DOMAIN>/v3/modern/plugins/rsa-plugins.js` and `<YOUR_CUSTOM_DOMAIN>/3.12.0/modern/plugins/rsa-plugins.js` should both be accessible. Otherwise, the SDK may not be able to load properly.
{{< /warning >}}

{{% /tab %}}
{{% tab tabName="Legacy (SDK v1.1 or below)" %}}
- If you're loading the JavaScript SDK **asynchronously**, paste the [snippet]({{< ref "archive/javascript-sdk/1.1/quick-start-guide.md#using-a-cdn" >}}) in your website's `<head>` section and update the URL from:

```javascript
"https://cdn.rudderlabs.com/v1.1/rudder-analytics.min.js"
```

to:

```javascript
"https://<YOUR_CUSTOM_DOMAIN>/v1.1/rudder-analytics.min.js"
```

- If you're loading the JavaScript SDK **synchronously**, paste the [snippet]({{< ref "archive/javascript-sdk/1.1/quick-start-guide.md#using-a-cdn" >}}) in your website's `<head>` section and update the URL from:

```javascript
<script src="https://cdn.rudderlabs.com/v1.1/rudder-analytics.min.js"></script>
```

to:

```javascript
<script src="https://<YOUR_CUSTOM_DOMAIN>/v1.1/rudder-analytics.min.js"></script>
```

Replace `<YOUR_CUSTOM_DOMAIN>` in the above snippet with your domain URL.
{{% /tab %}}
{{< /tabs >}}

#### NPM

Since you have used the NPM module for integrating the SDK directly into your application, there is no configuration required for serving the core SDK.

#### Additional configuration

{{< info >}}
Note that this section is only applicable for:

- npm installations
- Customized CDN installations not following the [default directory structure]({{< ref "user-guides/how-to-guides/self-hosting-js-sdk.md#prerequisites" >}}) that the SDK uses to automatically fetch the plugins and device mode destinations.
{{< /info >}}

This section covers some additional configuration required for loading the device mode destinations and plugins:

{{< tabs tabTotal="2" >}}
{{% tab tabName="Latest" %}}
Configure the [`load` API]({{< ref "sources/event-streams/sdks/rudderstack-javascript-sdk/load-js-sdk.md" >}}) option for plugins as follows **if** you are not using the [`@rudderstack/analytics-js/bundled`]({{< ref "sources/event-streams/sdks/rudderstack-javascript-sdk/installation.md#sdk-imports-for-bundling-tools-that-process-dynamic-imports" >}}) package that bundles all the plugins into the SDK package.

```javascript
rudderanalytics.load(WRITE_KEY, DATA_PLANE_URL, {
  pluginsSDKBaseURL: "https://<YOUR_CUSTOM_DOMAIN>/v3/modern/plugins"
});
```

To load [device mode destinations]({{< ref "sources/event-streams/sdks/rudderstack-javascript-sdk/migration-guide.md#additional-configuration-for-loading-device-mode-destinations" >}}), use the following [`load` API]({{< ref "sources/event-streams/sdks/rudderstack-javascript-sdk/load-js-sdk.md" >}}) option: 

```javascript
rudderanalytics.load(WRITE_KEY, DATA_PLANE_URL, {
  destSDKBaseURL: "https://<YOUR_CUSTOM_DOMAIN>/v3/modern/js-integrations"
});
```

{{< info >}}
The JavaScript SDK automatically replaces `/v3` in the configured URL with the current SDK version to load the most compatible integrations and plugins. So, make sure to proxy the whole RudderStack CDN domain instead of specific paths.

For example, if you specify `https://<YOUR_CUSTOM_DOMAIN>/v3/modern/js-integrations` for loading device mode destinations, then you might see the network request going to `https://<YOUR_CUSTOM_DOMAIN>/3.23.0/modern/js-integrations/<DEST_NAME>.min.js` to load the destination integration SDKs.
{{< /info >}}

{{% /tab %}}
{{% tab tabName="Legacy (SDK v1.1 or below)" %}}
To [load device mode destinations]({{< ref "archive/javascript-sdk/1.1/version-migration-guide.md##loading-device-mode-destinations" >}}), use the following additional [`load` API option]({{< ref "archive/javascript-sdk/1.1/load-js-sdk.md#loading-options" >}}):

```javascript
rudderanalytics.load(WRITE_KEY, DATA_PLANE_URL, {
    destSDKBaseURL: "https://<YOUR_CUSTOM_DOMAIN>/v1.1/js-integrations"
});
```

Replace `<YOUR_CUSTOM_DOMAIN>` in the above snippet with your domain URL.
{{% /tab %}}
{{< /tabs >}}

## Use custom SSL certificates

{{< customreadfile "/includes/custom-domains/custom-ssl-certificates.md" >}}

## Custom request header for GCP external load balancer

{{< customreadfile "/includes/custom-domains/gcp-load-balancer.md" >}}

## FAQ

#### Why do I see `Store clientDataInCookie` or `RudderEncrypt` parse errors on `track` calls?

Upgrade to the [latest JavaScript SDK version]({{< ref "sources/event-streams/sdks/rudderstack-javascript-sdk/installation.md" >}}) to resolve this issue. 

Note that proxying the SDK CDN does not fix this error on its own. If issues persist on Safari or iOS after you upgrade, proxy all three domains listed in [Use Custom Domains]({{< ref "user-guides/how-to-guides/custom-domains/_index.md#domains-to-proxy" >}}) and see [JavaScript SDK FAQ]({{< ref "sources/event-streams/sdks/rudderstack-javascript-sdk/faq.md" >}}).

#### Can I overcome ad blockers by serving the JavaScript SDK on my domain?

Many popular ad blockers block specific SDK-related downloads and API calls based on the domain name and URL. You can serve the SDK on your CDN to circumvent this issue.

<br />
