# AWS CloudFront reverse proxy - Docs

**Before you start**

-   If you use a self-hosted proxy, PostHog can't help troubleshoot. Use [our managed reverse proxy](/docs/advanced/proxy/managed-reverse-proxy.md) if you want support.
-   Use domains matching your PostHog region: `us.i.posthog.com` for US, `eu.i.posthog.com` for EU.
-   Don't use obvious path names like `/analytics`, `/tracking`, `/telemetry`, or `/posthog`. Blockers will catch them. Use something unique to your app instead.

This guide shows you how to configure [AWS CloudFront](https://docs.aws.amazon.com/cloudfront/) as a reverse proxy for PostHog.

## Why CloudFront needs custom configuration

CloudFront's default settings don't forward the headers, cookies, and query parameters PostHog needs to function. Without custom cache and origin request policies:

-   Events will fail to authenticate due to missing `Authorization` headers
-   CORS requests will be blocked due to missing `Origin` headers
-   Feature flags and session recording won't work correctly due to stripped query parameters

You'll need to create custom policies to ensure CloudFront passes everything through to PostHog.

## Architecture overview

Your CloudFront distribution will route two types of requests:

-   **Event and API requests:** Routes to your main origin, `us.i.posthog.com` or `eu.i.posthog.com`. This handles event capture, feature flags, session recordings, and all PostHog API calls.
-   **Static assets:** Routes to your assets origin, `us-assets.i.posthog.com` or `eu-assets.i.posthog.com`. This serves PostHog's JavaScript SDK and other static files.

CloudFront uses "behaviors" to route requests. The `/static/*` and `/array/*` behaviors route to your assets origin, while the default behavior routes everything else to your main origin.

## Set up your CloudFront proxy

1.  1

    ## Create a CloudFront distribution

    In the CloudFront console, follow AWS's guide to [create a CloudFront distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-creating-console.html).

    Configure these CloudFront settings for your origin:

    Origin settings:

    -   **Origin domain:** `us.i.posthog.com` or `eu.i.posthog.com`
    -   **Protocol:** HTTPS only

    This tells CloudFront to forward requests to PostHog's ingestion endpoint. HTTPS only ensures all traffic is encrypted.

    Default cache behavior:

    -   **Allowed HTTP methods:** `GET`, `HEAD`, `OPTIONS`, `PUT`, `POST`, `PATCH`, `DELETE`
    -   **Cache HTTP methods:** Check `OPTIONS`

    PostHog needs all HTTP methods because you'll capture events (`POST`), retrieve feature flags (`GET`), and handle CORS preflight requests (`OPTIONS`).

2.  2

    ## Create a custom cache policy

    In the CloudFront console, follow AWS's guide to [create a cache policy](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/controlling-the-cache-key.html#cache-key-create-cache-policy).

    Configure this custom policy:

    -   **Name:** `posthog-cache-policy` or any name you prefer
    -   **Headers:** Include `Origin` and `Authorization`
    -   **Query strings:** All

    This policy tells CloudFront to include these values when caching responses. Without this, CloudFront would strip authentication headers and break event capture.

3.  3

    ## Apply your custom cache policy

    In the CloudFront console, edit your distribution's default cache behavior.

    Apply these CloudFront policies:

    -   **Cache policy:** Your custom `posthog-cache-policy` from step 2
    -   **Origin request policy:** `CORS-CustomOrigin`

    The `CORS-CustomOrigin` policy is built into AWS and forwards CORS-related headers to your origin. You don't need to create it.

    Leave AWS WAF disabled for now. If you enable it later, make sure its size limit is at least 1MB for events and 64MB for session recordings. AWS WAF defaults to 8KB, which will block PostHog traffic.

4.  4

    ## Add a second origin for static assets

    In the CloudFront console, add a second origin to your distribution.

    Configure this origin:

    -   **Origin domain:** `us-assets.i.posthog.com` or `eu-assets.i.posthog.com`

    This origin serves PostHog's JavaScript SDK and other static files. Separating static assets from API requests allows different caching strategies.

5.  5

    ## Create a custom origin request policy

    In the CloudFront console, follow AWS's guide to [create an origin request policy](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/controlling-origin-requests.html#origin-request-create-origin-request-policy).

    Configure this custom policy:

    -   **Name:** `posthog-origin-request-policy` or any name you prefer
    -   **Headers:** Include `Origin`
    -   **Query strings:** All

    Origin request policies control what CloudFront sends to your origin, regardless of caching. This ensures PostHog receives all headers and query parameters it needs.

6.  6

    ## Create cache behaviors for static assets and config

    In the CloudFront console, add two new cache behaviors to your distribution.

    **Behavior 1: Static assets**

    -   **Path pattern:** `/static/*`
    -   **Origin:** Your assets origin, `us-assets.i.posthog.com` or `eu-assets.i.posthog.com`
    -   **Cache policy:** Your custom `posthog-cache-policy` from step 2
    -   **Origin request policy:** Your custom `posthog-origin-request-policy` from step 5
    -   **Response headers policy:** `CORS-with-preflight-and-SecurityHeadersPolicy`

    This behavior routes requests to `/static/*` to your assets origin instead of the main origin. The response headers policy is built into AWS and adds CORS headers to responses.

    **Behavior 2: Remote config**

    -   **Path pattern:** `/array/*`
    -   **Origin:** Your assets origin, `us-assets.i.posthog.com` or `eu-assets.i.posthog.com`
    -   **Cache policy:** Your custom `posthog-cache-policy` from step 2
    -   **Origin request policy:** Your custom `posthog-origin-request-policy` from step 5
    -   **Response headers policy:** `CORS-with-preflight-and-SecurityHeadersPolicy`

    This behavior routes requests to `/array/*` to your assets origin. The `/array` path serves PostHog's remote configuration, which controls Feature flags, session recording settings, and other SDK behavior. Routing this through the assets origin ensures proper cache-control headers are preserved, allowing the SDK to refresh its configuration correctly.

    **Route /array/\* to the assets origin**

    If `/array/*` routes through the main origin instead, config requests will still succeed but cache-control headers will be stripped. Without these headers, browsers fall back to heuristic caching and may cache stale configuration for hours or days. This can cause session recording conditions, feature flags, and other SDK settings to stop updating.

    If you encounter CORS errors after setup, you may need to create a custom response headers policy with `Access-Control-Allow-Headers` set to `*` for all headers. See [AWS's CORS troubleshooting guide](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-cors) for details.

7.  7

    ## Update your PostHog SDK

    In your application code, update your PostHog initialization to use your CloudFront distribution domain:

    PostHog AI

    ### US

    ```javascript
    posthog.init('<ph_project_token>', {
      api_host: 'https://d111111abcdef8.cloudfront.net',
      ui_host: 'https://us.posthog.com'
    })
    ```

    ### EU

    ```javascript
    posthog.init('<ph_project_token>', {
      api_host: 'https://d111111abcdef8.cloudfront.net',
      ui_host: 'https://eu.posthog.com'
    })
    ```

    Replace `d111111abcdef8.cloudfront.net` with your actual CloudFront distribution domain. Find this in the CloudFront console under **distribution domain name**.

    The `ui_host` must point to PostHog's actual domain so features like the toolbar link correctly.

8.  ## Verify your setup

    Checkpoint

    Confirm events are flowing through CloudFront:

    1.  Open your browser's developer tools and go to the Network tab
    2.  Trigger an event in your app, like a page view
    3.  Look for a request to your CloudFront domain
    4.  Verify the response is `200 OK`, not `403 Forbidden` or `400 Bad Request`
    5.  Check the [PostHog app](https://app.posthog.com) to confirm events appear

    If you see `403` errors, your cache or origin request policies aren't forwarding required headers. Double-check your policy configurations.

## Troubleshooting

### 403 Forbidden errors

If CloudFront returns `403 Forbidden` when capturing events:

1.  Verify your cache policy includes `Authorization` and `Origin` headers
2.  Check that your origin request policy forwards all query strings
3.  Confirm you're using the `CORS-CustomOrigin` origin request policy on your default behavior

CloudFront blocks requests when required headers are missing. The `Authorization` header is essential for PostHog to authenticate your events.

### CORS errors in browser console

If you see CORS errors like `No 'Access-Control-Allow-Origin' header`:

1.  Verify you applied the `CORS-with-preflight-and-SecurityHeadersPolicy` response headers policy to your `/static/*` behavior
2.  Check that your cache policy includes the `Origin` header
3.  If errors persist, create a custom response headers policy with `Access-Control-Allow-Headers` set to `*`

See [AWS's CORS troubleshooting guide](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-cors) for more details.

### Events or recordings are being truncated

If large events or session recordings aren't reaching PostHog:

1.  Check if you enabled AWS WAF on your distribution
2.  If enabled, verify the WAF body size limit is at least 64MB
3.  AWS WAF defaults to 8KB, which will block PostHog traffic

PostHog events can be up to 1MB and session recordings up to 64MB per message. Any size limit below these values will silently drop data.

### Static assets aren't loading

If the PostHog SDK fails to load or you see 404 errors for `/static/*` requests:

1.  Verify you created a second origin pointing to `us-assets.i.posthog.com` or `eu-assets.i.posthog.com`
2.  Confirm you created a cache behavior with path pattern `/static/*` pointing to this origin
3.  Check that the assets origin matches your PostHog region (US or EU)

Without the `/static/*` behavior, CloudFront tries to serve static files from your main origin, which will fail.

### Session recording conditions or Feature flags not updating

If session recording ignores your configured conditions (e.g., records everything despite having event triggers or sampling), or Feature flags seem stuck on old values:

1.  Verify you created a cache behavior with path pattern `/array/*` pointing to your assets origin (`us-assets.i.posthog.com` or `eu-assets.i.posthog.com`)
2.  Check the response headers for `/array/<your-token>/config.js` by curling your proxy URL — you should see `cache-control: max-age=300` in the response
3.  If `cache-control` is missing, the `/array/*` path is routing through your main origin instead of the assets origin

Without the `/array/*` behavior, config requests route through the main origin. This silently works (returns valid config) but strips cache-control headers. Browsers then fall back to heuristic caching based on `Last-Modified`, which can cache stale configuration for hours or days. This means SDK configuration changes (recording conditions, Feature flags, sampling rates) won't reach users until their browser cache expires.

### Community questions

Ask a question

### Was this page useful?

HelpfulCould be better