vLLM 0.32 stops letting any website call your model server
vLLM 0.32 changes the default CORS origins from "*" to none. Who's exposed on 0.31 today, what breaks on upgrade, and the flag and check to fix it now.
Run vLLM 0.31 with default flags and it tells every browser that any website may call it. A page on some unrelated site can have its JavaScript send chat requests to the vLLM server on your laptop or your office network, and read the answers, because the server replies Access-Control-Allow-Origin: *. vLLM 0.32 turns that off.
The change landed on October 11 in a commit titled "Restrict default CORS origins" and shipped in the 0.32.0rc0 release candidate on October 12. The current stable release, 0.31.0, still has the wildcard default, and so did releases as far back as 0.6.
The default flips from everyone to no one
The --allowed-origins flag now defaults to [] instead of ["*"], in both the Python server and the Rust frontend. vLLM hands the value straight to Starlette's CORSMiddleware, together with --allowed-methods, --allowed-headers and --allow-credentials, whose defaults (["*"], ["*"] and false) haven't changed. With an empty list, a browser's preflight gets a 400 with no Access-Control-Allow-Origin header, so the browser never sends the JSON POST that would follow.
Clients that aren't browsers don't do CORS. curl, the OpenAI SDK, your gateway and any agent running on a server keep working after the upgrade exactly as before.
Who is exposed today, and who breaks on upgrade
You're exposed on 0.31 if a vLLM server runs without --api-key anywhere a browser can reach it: localhost on a developer laptop, a GPU box on the office network, a cluster service reachable over the VPN. Any page that person opens can spend your GPU time and read the output. Setting --api-key helps, because the page can't supply the key, but the key only guards the /v1, /v2, /inference and /cohere prefixes. vLLM's security guide lists other inference routes, /invocations, /score and /pooling among them, that stay open even with a key.
You break on upgrade if a web page served from a different origin calls vLLM straight from the browser: an internal chat UI, a playground on another port, a notebook front end. Those calls start failing with a CORS error in the browser console. Origins have to match exactly on scheme, host and port, so http://localhost:3000 and http://127.0.0.1:3000 count as two different origins. The Python server passes no origin pattern to Starlette, so there's no *.example.com shortcut: list every origin.
Set the flag on 0.31 now
Don't wait for 0.32. Set the origins yourself today and the upgrade becomes a no-op. The value is a JSON list:
# Only these browser origins may call the server; everyone else is denied
vllm serve Qwen/Qwen3-8B \
--api-key "$VLLM_API_KEY" \
--allowed-origins '["https://chat.internal.example"]'
If no browser should ever talk to vLLM directly, which is the normal setup behind a gateway, pass --allowed-origins '[]' and you get the 0.32 behavior on 0.31.
Then check from the outside, the way a browser would. This preflight asks whether a page on another site may send an authenticated JSON POST:
curl -si -X OPTIONS http://vllm.internal:8000/v1/chat/completions \
-H 'Origin: https://some-other-site.example' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: authorization,content-type' \
| grep -i -e '^HTTP' -e 'access-control-allow-origin'
A locked-down server answers 400 with no access-control-allow-origin line. If you see *, or the origin you sent echoed back, any website can use that server.
CORS is not access control
CORS only decides what a browser lets a page do. Requests that don't need a preflight, such as a plain GET, still reach vLLM and run. The browser just hides the response from the page. In the test for this article, GET /v1/models from a foreign origin still returns 200 under the new default, only without the header. So keep --api-key, keep the server off the public internet, and put a reverse proxy in front that exposes only the routes you use, as vLLM's security guide recommends.
Never pair ["*"] with --allow-credentials. Starlette then echoes back whichever origin asked, which is the wildcard again, now with cookies allowed. List the origins instead.
Your ingress may be answering for vLLM. If NGINX, Envoy or an API gateway in front of the server adds its own CORS headers or handles OPTIONS itself, vLLM's default never comes into play. Run the curl check against the URL your users' browsers actually hit, not against the pod.
Do this today
Run that preflight against every vLLM endpoint you own, using the hostname a browser would see. Wherever it comes back with *, add --allowed-origins with the exact origins of your UIs, or '[]' if you have none, and roll the deployment.
Checked against the vLLM v0.32.0rc0, v0.31.0 and v0.6.0 source (cli_args.py, middleware/register.py) and the security guide in the repo. The CORS behavior was reproduced with Starlette 1.6.0 and uvicorn 0.53.0 in a stand-in app that wires CORSMiddleware with vLLM's arguments; the code and tests are in articles/examples/vllm-cors in this site's repo. InferPrice didn't run vLLM itself or test it on GPU hardware.
Sources
- vLLM commit fcae5c9: "[Security] Restrict default CORS origins" (#61009), Oct 11, 2026
- vLLM v0.32.0rc0:
FrontendArgsin cli_args.py (allowed_origins, api_key) - vLLM v0.32.0rc0: CORS middleware registration (register.py)
- vLLM v0.31.0: cli_args.py with the
["*"]default - vLLM security guide (docs/usage/security.md at v0.32.0rc0): API key limitations and reverse proxy advice
- Starlette 1.6.0: CORSMiddleware source
- MDN: Cross-Origin Resource Sharing (CORS)
Need GPU and LLM API prices in your own scripts or dashboards? The daily-verified data behind this site is available as a price API on Apify and a full price history ($9).