What happens to localhost URLs in a coding challenge's preview
Browser code that calls http://localhost:4000 is sent to port 4000 in the sandbox, with CORS, redirects and cookies handled. For the few cases it can't reach, use a relative path or routing.<service>.url.
A project's frontend often calls its API at http://localhost:4000, from an env file such as
NEXT_PUBLIC_API_URL or hardcoded in the code. In a Kendor preview the page runs in the viewer's
browser, where localhost is their own computer, not the sandbox. Kendor redirects those calls to
the right port in the sandbox, so most projects work without edits. This article covers what is
redirected, what isn't, and the explicit way to build URLs when you'd rather not rely on it.
What Kendor redirects
Any URL whose host is localhost, 127.0.0.1, 0.0.0.0 or [::1], on any port, made from the
preview page or from its web workers:
- requests with
fetchandXMLHttpRequest(and so axios and most other HTTP libraries) - WebSockets, including socket.io and hot reload, and server-sent events (
EventSource) - images, scripts, stylesheets, video, iframes, links and forms, whether they're in the served HTML, set from JavaScript, or inserted as markup
- redirects from your server to
http://localhost:…
It doesn't matter where the URL comes from: an env variable baked into the bundle, a hardcoded string, or one built at runtime.
Where a redirected URL goes
- If the challenge's
routingsends that path to that port, the URL goes to the preview address itself. For example,http://localhost:4000/api/usersgoes tohttps://<preview-address>/api/userswhen/apiroutes to the service on port 4000. Frontend and API then share one address, so there's no CORS at all. - Otherwise it goes to that port's own address,
https://4000-<preview-address>/api/users, the same address the Processes view opens for that port.
The first time a port is redirected, the browser console says so:
[kendor] localhost:4000 is the sandbox's port 4000 here, reached at https://4000-<preview-address>.
CORS, redirects and cookies
A call from the preview to another port's address is cross-origin, so Kendor makes it look the way it did on your machine:
- CORS. Your API receives
Origin: http://localhost:3000(the port the page came from), so an allow-list written for local development passes. Its answer is mapped back to the preview address. Only calls from the challenge's own preview get this; any other site's requests are left as they are. - Redirects. A
Location: http://localhost:3000/dashboardheader is rewritten to the preview address. - Cookies. The preview shows inside the editor, on another site, where browsers drop ordinary
cookies. Kendor marks your app's cookies as partitioned (
SameSite=None; Secure; Partitioned), so a cookie login works there too. A cookie withDomain=localhosthas that domain removed.
Server-side code needs none of this. Inside the sandbox, localhost already reaches the right
process, so a Next.js server component or an API calling another service on http://localhost:4000
works as it does on your machine.
What isn't redirected
- A CSS
url()pointing atlocalhost, such asbackground: url(http://localhost:4000/bg.png). - Workers created from a blob or data URL, rather than from a script file.
- Page navigation set in code, such as
location.href = 'http://localhost:3000/login', in Firefox. Chrome, Edge and Safari are covered. - OAuth callback URLs registered with a provider such as Google or GitHub as
localhost. Update them at the provider, or sign in with a test account another way. - A server that listens only on
127.0.0.1. The preview can't reach it at all. Listen on$HOST(0.0.0.0) instead.
When the page still loads something from localhost, the browser console says which URL:
[kendor] This page loaded http://localhost:4000/bg.png (css). That address is the viewer's own
computer, not the sandbox, and Kendor couldn't redirect this one.
Chrome may also ask the viewer for permission to access their local network. Both mean the same thing: change that URL as described below.
The explicit way
Instead of relying on the redirect, you can give the browser the right address yourself. Either way works in the sandbox and on your machine.
- A relative path. With
routingsending/apito your API, callfetch('/api/users'). The frontend and the API share the preview address. See multi-service apps. - A reference in
dev.env.vars. Set your frontend's API variable to the API's public URL:
services:
web:
dev:
command: npm run dev -- --port $PORT
port: 5173
env:
vars:
VITE_API_URL: routing.api.urlrouting.api.url resolves to the preview address plus the API's route, before any service starts.
Every dev command also gets KENDOR_PUBLIC_HOST, the preview's host name, for code that builds
URLs itself. A port's own address is that host with <port>- in front.
Turning it off
If your app maps its own URLs and you want its pages served exactly as written, send the header
x-kendor-no-rewrite on its HTML responses. Kendor then leaves those pages alone.