---
title: "What happens to localhost URLs in a coding challenge's preview"
description: "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."
updated: 2026-11-05
canonical: https://kendor.io/docs/environments/localhost-in-the-preview
---

# What happens to localhost URLs in a coding challenge's preview
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 `fetch` and `XMLHttpRequest` (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 `routing` sends that path to that port, the URL goes to the preview address
  itself. For example, `http://localhost:4000/api/users` goes to `https://<preview-address>/api/users`
  when `/api` routes 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:

```text
[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/dashboard` header 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 with `Domain=localhost` has 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 at `localhost`, such as `background: 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:

```text
[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 `routing` sending `/api` to your API, call `fetch('/api/users')`. The
  frontend and the API share the preview address. See
  [multi-service apps](/docs/environments/multi-service-apps).
- **A reference in `dev.env.vars`.** Set your frontend's API variable to the API's public URL:

```yaml file="kendor.yaml"
services:
  web:
    dev:
      command: npm run dev -- --port $PORT
      port: 5173
      env:
        vars:
          VITE_API_URL: routing.api.url
```

`routing.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.
