Skip to content

Instantly share code, notes, and snippets.

@timfish
Last active February 18, 2026 21:15
Show Gist options
  • Select an option

  • Save timfish/a69dd7457b8d6d97c0a8018675be6c23 to your computer and use it in GitHub Desktop.

Select an option

Save timfish/a69dd7457b8d6d97c0a8018675be6c23 to your computer and use it in GitHub Desktop.
Sentry Cloudflare Workers Proxy - Makes JavaScript and event submission first-party!
  • Add the worker.js code to a new Cloudflare Worker
  • Set up a worker for your domain than responds to /tunnel/* and point it to your new worker
  • Add the Sentry script to your html but replace https://browser.sentry-cdn.com/ with ./tunnel/
    • Eg. <script src="./tunnel/6.9.0/bundle.min.js"></script>
  • init Sentry with the tunnel option set to /tunnel/
    • Eg. Sentry.init({ dsn: "__DSN__", tunnel: "/tunnel/" })
  • Rejoice at how everything now works with ad blockers
const SLUG = '/tunnel/';
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url)
// Handle requests for Sentry CDN JavaScript
if (request.method === 'GET' && url.pathname.startsWith(SLUG) && (url.pathname.endsWith('.js') || url.pathname.endsWith('.js.map'))) {
const path = url.pathname.slice(SLUG.length);
// Fetch and pass the same response, including headers
return fetch(`https://browser.sentry-cdn.com/${path}`);
}
if (request.method === 'POST' && url.pathname === SLUG) {
let { readable, writable } = new TransformStream()
request.body.pipeTo(writable);
// We tee the stream so we can pull the header out of one stream
// and pass the other straight as the fetch POST body
const [header, body] = readable.tee();
let decoder = new TextDecoder()
let chunk = '';
const headerReader = header.getReader();
while (true) {
const { done, value } = await headerReader.read();
if (done) {
break;
}
chunk += decoder.decode(value);
const index = chunk.indexOf('\n');
if (index >= 0) {
// Get the first line
const firstLine = chunk.slice(0, index);
const event = JSON.parse(firstLine);
const dsn = new URL(event.dsn);
// Pass through the user IP address
const headers = request.headers
headers.set('X-Forwarded-For', request.headers.get('CF-Connecting-IP')) // enhance the original headers
// Post to the Sentry endpoint!
return fetch(`https://${dsn.host}/api${dsn.pathname}/envelope/`, {
method: 'POST',
body,
headers,
})
}
}
}
// If the above routes don't match, return 404
return new Response(null, { status: 404 });
}
@gander
Copy link

gander commented Jan 22, 2025

@alan0xd7, try:

const newRequest = new Request(request);
newRequest.headers.set('X-Custom-Header', '...');

or:

const newHeaders = new Headers(request.headers);
newHeaders.set('X-Custom-Header', '...');

@alan0xd7
Copy link

Thanks! This works:

const newHeaders = new Headers(request.headers);
const clientIp = request.headers.get('CF-Connecting-IP');
newHeaders.set('X-Forwarded-For', clientIp);

@sansh72
Copy link

sansh72 commented Feb 18, 2026

has the envelope.rs https://github.com/getsentry/relay/blob/2e924639d7bcfa24db69ba2ed78a82e2c07478e1/relay-server/src/envelope.rs#L1144 been solved? apparently, it was reading forward for from request body instead of headers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment