- 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>
- Eg.
init
Sentry with thetunnel
option set to/tunnel/
- Eg.
Sentry.init({ dsn: "__DSN__", tunnel: "/tunnel/" })
- Eg.
- Rejoice at how everything now works with ad blockers
Last active
April 14, 2025 12:26
-
-
Save timfish/a69dd7457b8d6d97c0a8018675be6c23 to your computer and use it in GitHub Desktop.
Sentry Cloudflare Workers Proxy - Makes JavaScript and event submission first-party!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }); | |
} |
Humm, I gotta ask if anyone has made this with Nginx directly? 🤔
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?
@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', '...');
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
My full code only supports
POST
because I'm just tunneling the request from Sentry.