Proxy Behavior & Limitations
When a request arrives at your tunnel URL, Broch proxies it to your local service. The core problem the proxy has to solve is that the browser is talking to myapp-yourname.tunnels.yourcompany.com, but your local app thinks it is running at localhost:3000. Without intervention, this mismatch causes redirects to break, cookies to be rejected, and CSRF validation to fail — because responses contain localhost references the browser cannot use.
The proxy bridges this gap by rewriting specific headers in both directions. It does not rewrite response bodies — only headers.
What the Proxy Rewrites
Section titled “What the Proxy Rewrites”Request headers (inbound → your local app)
Section titled “Request headers (inbound → your local app)”| Header | Why |
|---|---|
Host | Rewritten to localhost:{port} so your local framework accepts the request. Most dev servers reject requests with an unrecognized Host. |
X-Forwarded-Host | Set to the tunnel hostname so your app can construct correct absolute URLs if it reads forwarded headers. |
X-Forwarded-Proto | Set to https (the tunnel scheme) so your app generates correct redirect URIs and enforces HTTPS where needed. |
X-Forwarded-For | Set to the client IP chain so your app can perform IP-based checks (e.g. webhook allowlists). |
Referer | Rewritten from the tunnel URL back to the local URL. Frameworks like Django, Rails, and Flask-WTF compare the Referer origin against the Host header for CSRF validation — without this rewrite they see a mismatch and return 403. |
Response headers (your local app → browser)
Section titled “Response headers (your local app → browser)”| Header | Why |
|---|---|
Location, Content-Location | Localhost URLs rewritten to the tunnel URL. Without this, a local app redirecting to http://localhost:3000/login would send the browser to an address it cannot reach. |
Set-Cookie Domain attribute | Localhost/loopback values rewritten to the tunnel hostname. Browsers silently reject cookies with Domain=localhost when the page is served from the tunnel URL. |
What the Proxy Does Not Rewrite
Section titled “What the Proxy Does Not Rewrite”The proxy rewrites headers only — not response bodies. HTML, JavaScript, CSS, and JSON responses are forwarded as-is. This matches the behavior of similar tools (ngrok, Cloudflare Tunnel).
A few specific situations require configuration on your local app as a result.
Absolute URLs in response bodies
Section titled “Absolute URLs in response bodies”If your app generates absolute URLs hardcoded to http://localhost:PORT — in HTML, JavaScript, CSS, or JSON responses — those URLs will not be rewritten to the tunnel hostname. Links and API calls in the browser will point back to localhost and fail for anyone accessing via the tunnel.
Fix: Configure your framework to trust X-Forwarded-Host so server-rendered absolute URLs use the tunnel hostname automatically:
| Framework | Configuration |
|---|---|
| Django | USE_X_FORWARDED_HOST = True in settings.py |
| Flask | app.wsgi_app = ProxyFix(app.wsgi_app, x_host=1, x_proto=1) |
| Rails | config.action_dispatch.trusted_proxies or ActionDispatch::RemoteIp |
| Express / Node | app.set('trust proxy', 1) |
| ASP.NET Core | app.UseForwardedHeaders() with ForwardedHeaders.XForwardedHost |
Most frameworks have a similar setting — look for “trusted proxies” or “forwarded headers” in your framework’s documentation.
Content-Security-Policy blocking tunnel requests
Section titled “Content-Security-Policy blocking tunnel requests”If your app emits a Content-Security-Policy header with directives locked to http://localhost:PORT (e.g. connect-src, default-src), the browser will enforce that policy against the tunnel hostname and block requests. The proxy cannot rewrite CSP headers.
Fix: Configure your app to emit a CSP that allows the tunnel origin, or disable the CSP during tunnel testing.
SameSite=Strict cookies
Section titled “SameSite=Strict cookies”Cookies set with SameSite=Strict will not be sent when a user navigates to the tunnel URL from an external link or a different origin. The proxy cannot change this — it is correct browser behavior.
Most session cookies use SameSite=Lax, which works fine through a tunnel. Only Strict is affected.
Multi-port apps
Section titled “Multi-port apps”If your app spans multiple ports — for example, a frontend on 5173 making API calls to a backend on 8080 — you need a separate tunnel for each port. Hardcoded cross-port references will not resolve through a single tunnel.
Fix: Run a tunnel for each port and update your frontend’s API base URL to the backend tunnel URL for the duration of the session:
broch share frontend --target http://localhost:5173broch share api --target http://localhost:8080