Using Next.js rewrites as a reverse proxy

Last updated:

|Edit this page

On this page

Note: If you are using the EU cloud then use eu instead of us in all domains (e.g. us.i.posthog.com -> eu.i.posthog.com)

If you are using Next.js, you can take advantage of rewrites to behave like a reverse proxy. To do so, add a rewrites() function and the skipTrailingSlashRedirect option to your next.config.js file:

JavaScript
// next.config.js
const nextConfig = {
async rewrites() {
return [
{
source: "/ingest/static/:path*",
destination: "https://us-assets.i.posthog.com/static/:path*",
},
{
source: "/ingest/:path*",
destination: "https://us.i.posthog.com/:path*",
},
];
},
// This is required to support PostHog trailing slash API requests
skipTrailingSlashRedirect: true,
}
module.exports = nextConfig

Then configure the PostHog client to send requests via your rewrite.

JavaScript
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
api_host: "/ingest",
ui_host: '<ph_app_host>'
})

If this isn't working for you (returning 503 errors), it may be an issue with how your hosting service (such as Heroku) handles rewrites. You can write Next.js middleware to proxy requests instead.

Setup video

Questions?

  • Anton
    11 days ago

    Trailing Slash leads to duplicate content.

    Thank you for the docs! As you see in the title we have a conflict about the trailing slash between SEO crawlers and the posthog settings:

    PostHog requires

    skipTrailingSlashRedirect: true

    API requests like /ph_proxy_path/ with or without a trailing slash must not be redirected.

    But SEO crawler penalize duplicate content, meaning my website is accessible at both /page and /page/, creating duplicate pages from Google's perspective...

    What is the recommended solution? Just manually adjust all urls in the nextjs middleware?

  • Jay
    2 months ago

    TypeError: Response with null body status (101, 204, 205, or 304) cannot have a body

    seeing this after deploying my app to Cloudflare Workers

    name: "TypeError", trigger: "POST /ingest/i/v0/e/", message: "Response with null body status (101, 204, 205, or 304) cannot have a body.",

  • Lautaro
    3 months ago

    is ui_host required?

    video doesn't use the ui_host setting on init, but current docs mention it alongside api_host.

    should we add it? what should it's value be?

    • Roni
      3 months agoSolution

      ui_host is only needed when you are using a reverse proxy setup. It allows links (from for example the toolbar) to point to the PostHog app and not your reverse proxy.

      It should point to the domain of the PostHog app you are using, so:

  • berkay
    7 months ago

    Potential Security Problem of the nextjs rewrites as a reverse proxy

    What kind of potential security issues might be out there while not using managed reverse proxy and configured them in nextjs project?

    • berkay
      Author7 months ago

      I asked this question regarding this note:

      Note: The following self-hosted proxy isn't provided by PostHog, so we can't take responsibility for it! If unsure, we recommend using our managed reverse proxy.

    • Max
      7 months ago

      Based on the provided information sources, I can highlight one specific security concern that has been documented:

      From the Cloudfront implementation example, there have been instances of odd events appearing in the Activity page from URLs that don't belong to the domain, potentially indicating unauthorized access (1). This suggests that improperly configured reverse proxies might allow unintended access to your PostHog data.

      However, I cannot make specific claims about other security implications of self-hosted Next.js reverse proxies as they are not explicitly documented in the information sources. The warning about PostHog not taking responsibility for self-hosted proxies appears across all proxy implementation documentation (2), (3), (4), (5), suggesting there may be additional security considerations, but these are not explicitly detailed in the provided sources.

      For security-critical implementations, the documentation consistently recommends using PostHog's managed reverse proxy service (2).

  • Rav
    7 months ago

    Getting this strange error on app load

    Seeing this below after doing the setting as mentioned in the article

    Uncaught SyntaxError: Unexpected token '<' (at config.js:1:1)

    This is where config is getting created: image.png

    • Rav
      Author7 months agoSolution

      It was just happening due to a middleware in my app. Not a problem with Posthog.

  • rami
    8 months ago

    getting ECONNRESET sometimes.

    Failed to proxy https://us-assets.i.posthog.com/static/recorder.js?v=1.140.1 Error: socket hang up at connResetException (node:internal/errors:720:14) at TLSSocket.socketCloseListener (node:_http_client:474:25) at TLSSocket.emit (node:events:529:35) at node:net:350:12 at TCP.done (node:_tls_wrap:657:7) at TCP.callbackTrampoline (node:internal/async_hooks:128:17) { code: 'ECONNRESET' }

  • rami
    8 months ago

    getting ECONNRESET

    Failed to proxy https://us-assets.i.posthog.com/static/recorder.js?v=1.140.1 Error: socket hang up at connResetException (node:internal/errors:720:14) at TLSSocket.socketCloseListener (node:_http_client:474:25) at TLSSocket.emit (node:events:529:35) at node:net:350:12 at TCP.done (node:_tls_wrap:657:7) at TCP.callbackTrampoline (node:internal/async_hooks:128:17) { code: 'ECONNRESET' }

  • Mike
    a year ago

    Trailing slash config is unclear

    The config above includes this:

    // This is required to support PostHog trailing slash API requests
    skipTrailingSlashRedirect: true,

    Is this required for the proxy to work? Or only for specific cases?

    The docs on next.config.js show a trailingSlash key as well.

    • Jacob
      10 months ago

      Adding to this! Would love to know more about this since our system is not compatible with skipTrailingSlashRedirect: true.

  • Andon
    a year ago

    Not all logs been added

    I have reverse proxy like this:

    next.config.mjs

    async rewrites() {
    return [
    {
    source: "/ingest/static/:path*",
    destination: "https://us-assets.i.posthog.com/static/:path*",
    },
    {
    source: "/ingest/:path*",
    destination: "https://us.i.posthog.com/:path*",
    },
    ];
    }

    and this:

    if (typeof window !== 'undefined') {
    posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, {
    api_host: `${env.NEXT_PUBLIC_APP_BASE_URL}/ingest`,
    ui_host: env.NEXT_PUBLIC_POSTHOG_HOST,
    capture_pageview: true,
    });
    }

    and some logs are being recoreded and some not, i see in vercel people are hitting on pages but some of the logs are not being added

    • Marcus
      a year agoSolution

      Hey Andon, that is the endpoint for session recordings, which will be captured while the user doesn't have to trigger any events. If you do not have specific events that are missing, there is nothing to worry about.

  • Amitav
    a year ago

    Update needed for eu.i.posthog.com?

    Since requests are now being passed to other subdomains as well I imagine we need an update on these?

  • Ian
    a year ago

    Getting 401 & 400

    When trying to do this, I'm seeing a few different errors.

    For the request to the posthog /decide/ endpoint I get a 401 response back with this body:

    {
    "type": "authentication_error",
    "code": "no_api_key",
    "detail": "No project API key provided. You can find your project API key in PostHog project settings.",
    "attr": null
    }

    then the following request to the /e/ endpoint I get a 400 response back with this body:

    {
    "type": "validation_error",
    "code": "no_data",
    "detail": "No data found. Make sure to use a POST request when sending the payload in the body of the request.",
    "attr": null
    }

    But without rewrites both these request work without issue. I can see the cookie passed along with the request. Only thing I notice as a difference is the one with rewrites doesn't seem to have a request body.

    • Emil
      7 months ago

      Figured it out. Make sure to make it route to "eu-assets.i.posthog.com" "eu.i.posthog.com" If you're EU

Was this page useful?

Next article

Using Next.js middleware as a reverse proxy

If you are using Next.js and rewrites aren't working for you , you can write custom middleware to proxy requests to PostHog. To do this using the app router , create a file named middleware.js in your base directory (same level as the app folder). In this file, set up code to match requests to a custom route, set a new host header, change the URL to point to PostHog, and rewrite the response. Add the skipTrailingSlashRedirect option to your next.config.js file: Once done, configure…

Read next article