I’m trying to set up a reverse proxy in front of my IC canister to intercept a single path and route it to an external server, while all other paths continue to my canister. The specific use case is routing WebSocket traffic (which IC doesn’t natively support) to a separate server, while keeping all other traffic on IC. However, I’m running into SSL issues and redirect loops when trying to put a proxy layer in front of the IC HTTP gateway.
Setup
Custom domain: pds.edjcase.com configured with IC custom domain setup
Custom domain registration completed and status is “Available”
Domain works perfectly when accessing IC directly
Goal
Use a reverse proxy (Cloudflare Worker) to intercept ONE specific path and route it to an external server, while all other paths continue to the IC canister:
/xrpc/com.atproto.sync.subscribeRepos → External WebSocket server
All other paths → IC canister
Problem
When I enable the proxy layer (required to intercept traffic), the custom domain stops working:
With DNS record set to “DNS only”:
$ curl -I https://pds.edjcase.com/.well-known/atproto-did
HTTP/2 200
# Works perfectly
Is it possible to use IC custom domains with a reverse proxy in front of the IC HTTP gateway?
And if so what does the http gateway/reverse proxy require to make this work
Any guidance or alternative approaches would be appreciated!
For more context I am working on an atproto PDS canister that requires a websocket connection for the com.atproto.sync.subscribeRepos request, which is needed to propagate data to a relay. So in this case I do not have control over the client and the client does not allow for redirects. So essentially (since the IC doesnt have websocket support) I have to fake the one endpoint to point at a non-ic server, that makes a websocket connection on one end and polls my canister on the other, as glue to fix it. But that doesn’t work if i cant get this problem solved
I would recommend against using your setup. If you want both Cloudflare and a custom domain, both will need to obtain a valid certificate. This can lead to interference between the two (and the potential failure of successfully obtaining it).
I would recommend you set up some web worker in Cloudflare that simply takes the Websocket traffic and sends it to your external service, while proxying all other requests to the canister ID domain (and not to the custom domain). This proxying can be done transparently such that the user never sees the canister ID domain.
Here is a snippet for a worker that does the proxying to the canister ID domain:
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const url = new URL(request.url);
// Target domain (the one you're showing content from)
const targetDomain = "https://<CANISTER_ID>.icp0.io";
// Reconstruct the path to pass through
const targetUrl = targetDomain + url.pathname + url.search;
const modifiedRequest = new Request(targetUrl, {
method: request.method,
headers: request.headers,
body: request.body,
redirect: 'follow'
});
// Fetch and return the response
let response = await fetch(modifiedRequest);
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: response.headers
});
}
Oh thank you, this saved me
Never even though of just bypassing and going straight to the canister id url
I have also had issues with custom domains not being able to do ‘raw’ requests, besides potential security, do you see issues with sending it to the <canister_id>.raw.icp0.io url in this scenario? That would be a game changer for me so i could do query requests (much easier) for the API