localhost:5173 / dev-server-requests

How the Vite dev server handles requests to and from localhost:5173

Three settings cover three different requests: server.proxy forwards a request from 5173 to your backend, server.cors decides which origins may read a response from 5173, and server.allowedHosts decides which hostnames may reach 5173 at all.

Three requests, three settings

  • Your app on 5173 calls your backend — that is server.proxy.
  • Something on another origin calls 5173 — that is server.cors.
  • A request arrives under a hostname the server does not know — that is server.allowedHosts.

Read the error, not the guess

The three produce distinguishable errors, and the common mistake is to reach for CORS on all of them.

  • Blocked by CORS policy, in the browser console — the response came back without the header the browser wanted.
  • Blocked request. This host is not allowed. — a plain sentence from Vite, before your app ran.
  • 404 or an HTML page where JSON was expected — the request was never forwarded; the dev server answered it itself.

The proxy is usually the right answer

Forwarding /api through the dev server makes the request same-origin, and a same-origin request has no CORS problem to solve. That is why the proxy replaces the CORS question instead of answering it — the browser never sees a cross-origin call in the first place.

vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    }
  }
}

What cors allows by default

The default is not open and not closed. Vite allows localhost, 127.0.0.1 and [::1] — on any port, over http or https — and rejects everything else. A second dev server on another localhost port is therefore allowed without configuration. A staging hostname is not.

default
server.cors = {
  origin: /^https?:\/\/(?:(?:[^:]+\.)?localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/
}

Why the default got stricter

An open dev server lets any page you visit read the files it serves, including your source. Narrowing the default origin closed that. Snippets from before the change set cors to true, which puts it back — if you copy one, you are copying the problem it was tightened against.

allowedHosts is not about origins

It reads the Host header, not the Origin header, and it runs before anything else. A request can be perfectly acceptable under your CORS settings and still be rejected here, because the two look at different parts of the same request. Tunnels and custom local domains hit this first.

Deciding in one pass

  • The call goes out to your own backend — proxy it, and stop there.
  • The call comes in from a page you control on another origin — widen cors for that origin only.
  • The request never gets that far and Vite answers with one sentence — add the hostname to allowedHosts.

# faq

Questions

Should I enable CORS or use the proxy?

Use the proxy for your own backend. It makes the request same-origin, so there is no CORS to configure.

Does server.cors affect requests my app makes?

No. It applies to requests other origins make to the dev server. Outgoing calls from your app are governed by the other server.

Why is my proxy returning HTML instead of JSON?

Because the path did not match the proxy rule, so the dev server handled it and returned the SPA fallback document.

Can allowedHosts cause a CORS error?

No. It rejects the request outright with a plain message before any CORS header is involved.

# next

Related

  • Proxying API requests from the Vite dev serverserver.proxy forwards any request whose path starts with a given prefix to another server, so the browser only ever talks to localhost:5173 and no CORS error appears.
  • Rewriting the path when the Vite proxy forwards a requestrewrite is called with req.url — the full path including the query string — and whatever it returns replaces req.url before the request is forwarded, so it has to come back as a path that still starts with a slash.
  • When the Vite proxy does not forward the requestA proxy that appears to do nothing has either never seen the request — because the browser addressed the backend directly, or because a middleware ahead of it answered first — or it saw it and no key matched, and DEBUG=vite:proxy tells the two apart in one line.
  • Proxying WebSocket connections through the Vite dev serverA WebSocket through the dev server needs ws: true (or a target starting with ws:// or wss://) on its proxy entry, because upgrade requests are handled by a separate listener that ignores every rule without one.
  • CORS errors around localhost:5173The Vite dev server only accepts cross-origin requests from localhost, 127.0.0.1 and ::1 by default — anything else has to be allowed explicitly, and setting cors to true opens it to every website.
  • Blocked request. This host is not allowed.The Vite dev server only answers to localhost, hostnames ending in .localhost, and IP addresses — every other hostname has to be listed in server.allowedHosts.
  • Reaching the Vite dev server from anything other than localhostThe Vite dev server binds to localhost only and rejects unknown hostnames, so a second device, a container or a tunnel needs two separate settings: server.host to bind wider, and server.allowedHosts to accept the name being used.
  • localhost:5173/login: how the dev server answers app routesWith the default appType "spa", the Vite dev server answers any GET or HEAD request that accepts HTML and matches no file — such as localhost:5173/login — with index.html and status 200, so the route is resolved by your client-side router, not by Vite.
  • localhost:5173 — the port itselfWhat the address means, common dev ports, and the autocomplete fix.

# sources

Checked against the Vite documentation on 2026-09-11.