CRUMB a card from devarno-cloud

Proxy Mode

smo1 advanced 6 min read

ELI5

Normally, a short link sends you somewhere else — your browser sees the new address. Proxy mode is different: it is like looking through a periscope. You stay on the short link URL, but you are actually seeing content from another website. The edge worker fetches the destination for you, rewrites any internal links so they still go through the short URL, and streams the response back without revealing the real destination.

Technical Deep Dive

Redirect vs. Proxy

AspectRedirect modeProxy mode
URL barShows destinationStays on smo1.io/:slug
Status code302 Found200 OK (streamed)
SEODestination indexedShort URL indexed
Path preservationN/A (new URL)/slug/path/path
Cookie scopingDestination domainsmo1.io
Use caseStandard short linksWhite-label, hiding destination

Proxy Sequence

%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#e8f4f8', 'primaryTextColor': '#2d3748', 'primaryBorderColor': '#90cdf4', 'lineColor': '#718096', 'secondaryColor': '#f0fff4', 'tertiaryColor': '#fefcbf'}}}%%
sequenceDiagram
autonumber
actor U as User
participant Z as zoomies-edge
participant D as Destination Server
U->>Z: GET smo1.io/:slug/dashboard
Z->>Z: Resolve link (KV / API)
Z->>Z: Rewrite path: /dashboard
Z->>Z: Set Host header to destination
Z->>D: GET https://destination.com/dashboard
D-->>Z: 200 OK + HTML/CSS/JS
Z->>Z: Rewrite Location headers
Z->>Z: Strip Set-Cookie domains
Z-->>U: 200 OK (streamed)
Note over U,Z: URL bar still shows smo1.io/:slug/dashboard

Path Rewriting

Original request: smo1.io/myapp/dashboard?tab=settings

Rewritten request to destination:

  • Path: /dashboard?tab=settings (slug prefix removed)
  • Host: destination.com
  • Headers: Original headers forwarded, except Host is replaced

Sub-path handling: ALL /:slug/* route in zoomies-edge catches any path under the slug and applies the same rewriting rules.

Header Manipulation

Outgoing (Worker → Destination):

  • Host → destination hostname
  • X-Forwarded-For → original client IP
  • X-Forwarded-Protohttps
  • Original User-Agent, Accept, Accept-Language preserved

Incoming (Destination → Worker → User):

  • Location headers rewritten: https://destination.com/loginhttps://smo1.io/myapp/login
  • Set-Cookie domain stripped: domain=destination.com → removed (cookie scopes to smo1.io)
  • Content-Encoding preserved for streaming

redirect: manual

The Worker uses fetch(url, { redirect: 'manual' }) instead of the default 'follow':

const response = await fetch(destinationUrl, {
method: request.method,
headers: modifiedHeaders,
body: request.body,
redirect: 'manual', // Critical!
});

Why manual?

  • If the destination returns a 302, the Workers runtime would normally follow it automatically
  • This would bypass the Worker’s header rewriting and path preservation
  • With manual, the Worker receives the 302 response, rewrites the Location header, and returns it to the browser — maintaining the slug prefix

When a proxied destination sets a cookie:

Set-Cookie: session=abc123; Domain=destination.com; Path=/; Secure

The Worker strips the Domain attribute:

Set-Cookie: session=abc123; Path=/; Secure

Without a Domain attribute, the browser scopes the cookie to the current host (smo1.io), preventing the destination from seeing cookies intended for the proxy path.

Limitations

LimitationReason
Relative asset URLs may breakThe browser resolves /style.css against smo1.io, not the destination
Absolute URLs in JS may bypass proxyFrontend routing in SPAs may navigate to destination.com directly
WebSocket connections are not proxiedWorkers runtime does not support WebSocket proxying in this configuration
Large file uploads may timeoutWorkers have a 100 MB response size limit and 30-second CPU limit

Workaround for broken assets: The destination should use absolute URLs or be designed to work behind a reverse proxy.

Key Terms

  • Reverse proxy → Server that fetches content from another server and presents it as its own
  • Path rewriting → Removing the slug prefix so the destination receives the correct sub-path
  • redirect: manual → Fetch option that prevents automatic HTTP redirect following
  • Domain stripping → Removing the Domain attribute from Set-Cookie headers to scope cookies to the proxy host
  • White-label → Presenting third-party content under your own domain and branding

Q&A

Q: Why would someone use proxy mode instead of redirect? A: To hide the destination URL (e.g., affiliate links, private documents), to maintain brand consistency (the user never leaves your domain), or to add a layer of access control (protection + proxy).

Q: Can proxy mode bypass CORS? A: No. CORS is enforced by the browser based on the origin in the address bar (smo1.io). If the proxied content makes XHR/fetch requests to the destination, those are cross-origin and subject to CORS. The Worker does not magically disable browser security.

Q: What happens if the destination is down? A: The Worker receives an error from fetch() and returns a 502 Bad Gateway or 504 Gateway Timeout to the user. The error page can be customised in the Worker.

Q: Does proxy mode support POST requests? A: Yes. The ALL /:slug/* route forwards all HTTP methods (GET, POST, PUT, PATCH, DELETE) to the destination with the same body and headers.

Examples

Think of proxy mode like a personal shopper:

  • You tell the shopper “Get me a jacket from Store X, but I do not want anyone to know I shop there” (hide destination)
  • The shopper goes to Store X, picks up the jacket, and brings it back to you in an unmarked bag (slug URL)
  • If Store X says “Go to our accessories department” (redirect), the shopper intercepts the directions and rewrites them so you still go through the shopper (Location header rewrite)
  • If Store X gives you a loyalty card (Set-Cookie), the shopper removes the Store X branding from the card so it looks like a generic gift card (domain stripping)
  • The shopper cannot help you if Store X’s clothes have “Made at Store X” labels sewn into every seam (absolute URLs in HTML/JS)

neighbors on the map