Skip to content

Instantly share code, notes, and snippets.

@superjose
Created February 20, 2025 21:08
Show Gist options
  • Save superjose/9eaa87be5fe320f25325e22ee8e455de to your computer and use it in GitHub Desktop.
Save superjose/9eaa87be5fe320f25325e22ee8e455de to your computer and use it in GitHub Desktop.
Remix V2 + Express + Vite + PostHog Proxy Rewrite
import { createRequestHandler } from '@remix-run/express';
import express, { Request } from 'express';
import { createCookieSessionStorage, type ServerBuild } from '@remix-run/node';
import { dispatchWithContext } from './app/.server/shared/config.js';
import { Dispatch } from './app/.server/services/mediator.types';
import shrinkRay from 'shrink-ray-current';
import 'dotenv/config';
import { getGrowthbookClient } from '@alertdown/core';
import { createProxyMiddleware } from 'http-proxy-middleware';
const API_HOST = 'us.i.posthog.com';
const ASSET_HOST = 'us-assets.i.posthog.com';
const viteDevServer =
process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging'
? undefined
: await import('vite').then((vite) => {
return vite.createServer({
server: { middlewareMode: true },
appType: 'custom',
});
});
const remixHandler = createRequestHandler({
build: viteDevServer
? () =>
viteDevServer.ssrLoadModule(
'virtual:remix/server-build'
) as Promise<ServerBuild>
: await import('./build/server/index.js'),
async getLoadContext(req) {
const env = process.env as Record<string, string>;
const sessionFlashSecret = env.SESSION_FLASH_SECRET;
const flashStorage = createCookieSessionStorage({
cookie: {
name: '__flash',
httpOnly: true,
maxAge: 60,
path: '/',
sameSite: 'lax',
secrets: [sessionFlashSecret],
secure: true,
},
});
return {
cloudflare: {
env,
},
dispatch: (await dispatchWithContext({
env,
request: req,
})) as Dispatch,
flashStorage,
growthbook: await getGrowthbookClient(env.GROWTHBOOK_API_KEY),
};
},
});
const app = express();
app.use(shrinkRay());
// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
app.disable('x-powered-by');
// PostHog proxy configuration
app.use(
'/ingest/static',
createProxyMiddleware({
target: `https://${ASSET_HOST}`,
pathRewrite: (path) => {
// Remove /ingest/static and ensure /static is present
const newPath = path.replace(/^\/ingest\/static/, '');
return `/static${newPath}`;
},
changeOrigin: true,
on: {
proxyReq: (proxyReq, req, res) => {
console.log('Final target URL:', proxyReq.path);
},
error: (err, req, res) => {
console.error('Proxy error:', err);
},
},
secure: true,
})
);
app.use(
'/ingest',
createProxyMiddleware({
target: `https://${API_HOST}`,
pathRewrite: {
'^/ingest': '',
},
changeOrigin: true,
secure: true,
on: {
error: (err, req, res) => {
console.error('Proxy error:', err);
},
},
})
);
// handle asset requests
if (viteDevServer) {
app.use(viteDevServer.middlewares);
} else {
// Vite fingerprints its assets so we can cache forever.
app.use(
'/assets',
express.static('build/client/assets', { immutable: true, maxAge: '1y' })
);
}
// Everything else (like favicon.ico) is cached for an hour. You may want to be
// more aggressive with this caching.
app.use(express.static('build/client', { maxAge: '1h' }));
// app.use(morgan('tiny'));
// handle SSR requests
app.all('*', remixHandler);
const port = process.env.PORT || 3000;
app.listen(port, () =>
console.log(`Express server listening at http://localhost:${port}`)
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment