Skip to content

Instantly share code, notes, and snippets.

@timfish
Last active April 14, 2025 12:26
Show Gist options
  • Save timfish/a69dd7457b8d6d97c0a8018675be6c23 to your computer and use it in GitHub Desktop.
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 26, 2024

My full code only supports POST because I'm just tunneling the request from Sentry.

export async function onRequestPost({request}) {
    const [header, body] = request.body.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) {
            const line = chunk.slice(0, index);
            const dsn = new URL(JSON.parse(line).dsn);
            const headers = request.headers;
            headers.set('X-Forwarded-For', request.headers.get('CF-Connecting-IP'));
            return fetch(`https://${dsn.host}/api${dsn.pathname}/envelope/`, {
                method: 'POST', body, headers,
            });
        }
    }
    return new Response(null, {status: 404});
}

@Kjue
Copy link

Kjue commented Dec 9, 2024

Humm, I gotta ask if anyone has made this with Nginx directly? 🤔

@alan0xd7
Copy link

Hey, just wondering if the Cloudflare Worker proxy still works?

When I tried it I get the error Can't modify immutable headers on the X-Forwarded-For line.

Any thoughts?

@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);

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