Cloudflare Worker Proxy for the Dashboard¶
The ScamShield AI dashboard can be served through a Cloudflare Worker to provide a custom domain, SSL termination, caching, and DDoS protection.
Why Use a Proxy¶
Cloud Run generates URLs like scamshield-dashboard-xxxxx-el.a.run.app. A Cloudflare Worker maps your custom domain (e.g., scam.example.com) to the Cloud Run service while adding:
- Custom domain with automatic SSL
- Cloudflare's DDoS protection and WAF
- Edge caching for static assets
- Request/response header manipulation
Worker Setup¶
1. Create the Worker¶
In the Cloudflare dashboard:
- Go to Workers & Pages > Create application > Create Worker
- Name it
your-cloudflare-worker-name - Replace the default code with:
export default {
async fetch(request) {
const url = new URL(request.url);
// Rewrite to Cloud Run backend
url.hostname = "scamshield-dashboard-xxxxx-el.a.run.app";
// Forward the request
const response = await fetch(url.toString(), {
method: request.method,
headers: request.headers,
body: request.body,
redirect: "manual", // CRITICAL: Do NOT change to "follow"
});
// Clone response and forward all headers
const newResponse = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
});
return newResponse;
},
};
2. Add a Route¶
Bind the Worker to your custom domain:
- Go to Workers & Pages > your-cloudflare-worker-name > Triggers
- Add route:
scam.example.com/* - Select the zone for
example.com
The Critical Gotcha: redirect: "manual"¶
Never change redirect to \"follow\"
The redirect: "manual" setting in the Worker's fetch() call is critical. Changing it to "follow" will break the dashboard.
Why this matters:
Streamlit uses HTTP 302 redirects for internal routing (page navigation, WebSocket upgrade negotiation). When redirect: "follow" is set:
- Streamlit returns a
302 Redirectto/_stcore/stream - Cloudflare's Worker follows the redirect server-side
- The response body is returned to the client, but the client's URL does not change
- Streamlit's JavaScript client expects the redirect to happen client-side
- The WebSocket connection fails, and the dashboard shows a blank page or connection error
With redirect: "manual":
- Streamlit returns a
302 Redirect - The Worker passes the
302response directly to the browser - The browser follows the redirect client-side (correct behavior)
- Streamlit's routing works normally
Cloudflare Settings That MUST Stay OFF¶
These Cloudflare features are incompatible with Streamlit and must be disabled for the zone or the specific route:
Rocket Loader¶
What it does: Defers loading of JavaScript by injecting a loader script.
Why it breaks Streamlit: Streamlit's frontend is a React SPA that depends on immediate JavaScript execution. Rocket Loader's deferred loading prevents the WebSocket connection from initializing, resulting in a blank page or infinite spinner.
How to disable: Zone dashboard > Speed > Optimization > Content Optimization > Rocket Loader > Off
Email Obfuscation¶
What it does: Rewrites @ symbols in page content to prevent email scraping bots.
Why it breaks Streamlit: Streamlit renders page content dynamically via JavaScript. Email obfuscation rewrites @ characters in the HTML source, corrupting UPI IDs (e.g., user@oksbi), email addresses, and other content containing @.
How to disable: Zone dashboard > Scrape Shield > Email Address Obfuscation > Off
Speed Brain (Speculative Preloading)¶
What it does: Prefetches pages the user might navigate to next.
Why it breaks Streamlit: Streamlit is a single-page application. Speculative preloading triggers additional requests that confuse Streamlit's server-side state management, causing page loads to fail or display incorrect content.
How to disable: Zone dashboard > Speed > Optimization > Speed Brain > Off
DNS Setup¶
Configure a DNS record pointing your custom domain to the Cloudflare Worker:
Option A: CNAME Record (Recommended)¶
Type: CNAME
Name: scam
Target: your-cloudflare-worker-name.<your-account>.workers.dev
Proxy: Proxied (orange cloud)
Option B: Worker Route on Existing Domain¶
If the domain is already on Cloudflare, just add the Worker route (step 2 above). No additional DNS records are needed.
Testing the Proxy¶
1. Verify the Worker Responds¶
You should see:
HTTP/2 200orHTTP/2 302(Streamlit redirect)server: cloudflarecf-ray: ...header
2. Verify Streamlit Loads¶
Open https://scam.example.com/ in a browser. You should see the Streamlit login page (PIN entry).
3. Verify WebSocket Connection¶
Open browser DevTools > Network tab. After the page loads, you should see a WebSocket connection to /_stcore/stream with status 101 Switching Protocols.
If the WebSocket connection fails:
- Check
redirect: "manual"in the Worker code - Check that Rocket Loader is OFF
- Check browser console for errors
4. Test PIN Auth¶
Enter the PIN. After successful login, verify:
- The
scamshield_authcookie is set - Refreshing the page does not require re-entering the PIN (cookie-based auth)
Updating the Worker¶
After editing the Worker code in the Cloudflare dashboard, click Save and Deploy. Changes take effect within seconds at Cloudflare's edge network.
Alternatively, use the Wrangler CLI:
Cloudflare Free Plan Limitations¶
The Cloudflare free plan supports Workers and Workers routes. The proxy setup described here works entirely on the free plan. Note that Origin Rules are a paid feature and are not used in this setup.