mirror of
https://github.com/BillyOutlast/posthog.com.git
synced 2026-02-07 12:51:21 +01:00
152 lines
4.3 KiB
Plaintext
152 lines
4.3 KiB
Plaintext
---
|
|
title: Using node:http as a reverse proxy
|
|
sidebar: Docs
|
|
showTitle: true
|
|
---
|
|
|
|
import RegionWarning from "../_snippets/region-warning.mdx"
|
|
import ProxyWarning from "../_snippets/proxy-usage-warning.mdx"
|
|
import ProxyPathNamesWarning from "../_snippets/proxy-path-names-warning.mdx"
|
|
import { CalloutBox } from 'components/Docs/CalloutBox'
|
|
|
|
<CalloutBox icon="IconWarning" title="Warning" type="fyi">
|
|
|
|
1. <ProxyWarning />
|
|
2. <RegionWarning />
|
|
3. <ProxyPathNamesWarning />
|
|
|
|
</CalloutBox>
|
|
|
|
You can use Node's built-in `http` module to proxy requests to PostHog. This is a simple and lightweight way to proxy requests.
|
|
|
|
```ts
|
|
import http from "node:http";
|
|
|
|
const API_HOST = "eu.i.posthog.com"; // Change to "us.i.posthog.com" for the US region
|
|
const ASSET_HOST = "eu-assets.i.posthog.com"; // Change to "us-assets.i.posthog.com" for the US region
|
|
|
|
|
|
// Convert Node.js headers to Fetch API headers
|
|
const toHeaders = (headers: Record<string, string[] | undefined>): Headers =>
|
|
Object.entries(headers).reduce((acc, [name, values]) => {
|
|
values?.forEach((value) => acc.append(name, value));
|
|
return acc;
|
|
}, new Headers());
|
|
|
|
// Convert Fetch API headers to Node.js headers
|
|
const fromHeaders = (headers: Headers): Record<string, string[] | undefined> =>
|
|
[...headers].reduce<Record<string, string[] | undefined>>(
|
|
(acc, [name, value]) => {
|
|
if (acc[name]) {
|
|
acc[name] = [...acc[name], value];
|
|
} else {
|
|
acc[name] = [value];
|
|
}
|
|
return acc;
|
|
},
|
|
{},
|
|
);
|
|
|
|
/**
|
|
* Proxies an incoming HTTP request to the appropriate PostHog endpoint.
|
|
*/
|
|
export default function proxy({ prefix }: { prefix: string }) {
|
|
return (
|
|
request: http.IncomingMessage,
|
|
response: http.ServerResponse,
|
|
next: (err?: Error) => void,
|
|
): void => {
|
|
if (!request.url?.startsWith(prefix)) {
|
|
next();
|
|
return;
|
|
}
|
|
|
|
const pathname = (request.url ?? "").slice(prefix.length);
|
|
const posthogHost = pathname.startsWith("/static/") ? ASSET_HOST : API_HOST;
|
|
|
|
// Rewrite headers
|
|
const headers = toHeaders(request.headersDistinct);
|
|
headers.set("host", posthogHost);
|
|
if (request.headers.host) {
|
|
headers.set("X-Forwarded-Host", request.headers.host);
|
|
}
|
|
if (request.socket.remoteAddress) {
|
|
headers.set("X-Real-IP", request.socket.remoteAddress);
|
|
headers.set("X-Forwarded-For", request.socket.remoteAddress);
|
|
}
|
|
|
|
// Remove sensitive or hop-by-hop headers
|
|
headers.delete("cookie");
|
|
headers.delete("connection"); // https://datatracker.ietf.org/doc/html/rfc7230#section-6.1
|
|
|
|
fetch(new URL(pathname, `https://${posthogHost}`), {
|
|
method: request.method ?? "",
|
|
headers,
|
|
|
|
...(!["HEAD", "GET"].includes(request.method ?? "")
|
|
? {
|
|
body: request,
|
|
duplex: "half",
|
|
}
|
|
: {}),
|
|
})
|
|
.then(async (res: Response) => {
|
|
const headers = new Headers(res.headers);
|
|
const body = await res.text();
|
|
|
|
// See https://github.com/nodejs/undici/issues/2514
|
|
if (headers.has("content-encoding")) {
|
|
headers.delete("content-encoding");
|
|
headers.delete("content-length");
|
|
}
|
|
|
|
response.writeHead(res.status, fromHeaders(headers));
|
|
response.end(body);
|
|
})
|
|
.catch((e: unknown) => {
|
|
next(new Error("Bad gateway", { cause: e }));
|
|
});
|
|
};
|
|
}
|
|
```
|
|
|
|
To use this proxy, you can create a server like this:
|
|
|
|
```js
|
|
import http from "node:http";
|
|
import cors from "cors";
|
|
import proxy from "./proxy.js";
|
|
|
|
const corsMiddleware = cors({ origin: 'https://<your_app_domain>' });
|
|
const posthogMiddleware = proxy({ prefix: "/<ph_proxy_path>" });
|
|
|
|
const server = http.createServer((req, res) => {
|
|
corsMiddleware(req, res, (err) => {
|
|
if (err) {
|
|
// Some error handling for errors returned by the CORS middleware.
|
|
} else {
|
|
posthogMiddleware(req, res, (err) => {
|
|
if (err) {
|
|
// Some error handling for errors returned by the posthog middleware.
|
|
} else {
|
|
// Your next handler ...
|
|
// handler(req, res);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
```
|
|
|
|
Once done, you can initialize the PostHog client with the proxy path:
|
|
|
|
```js
|
|
import { PostHog } from 'posthog-node'
|
|
|
|
const client = new PostHog(
|
|
'<ph_project_api_key>',
|
|
{ host: '/<ph_proxy_path>' }
|
|
)
|
|
```
|
|
|
|
> Thank you to [SimonSimCity](https://github.com/SimonSimCity) for this method. |