DEV Community

Orchid Files
Orchid Files

Posted on

Why localhost doesn't work as OpenAI Base URL in Cursor — and how to fix it

TL;DR: Cursor doesn't call your OpenAI Base URL from your local machine. It routes every request through its backend servers on api2.cursor.sh, so http://localhost:8082 is unreachable — the chat hangs and your proxy logs stay empty. To connect Ollama, LM Studio, LiteLLM, or a custom subscription proxy, you must expose your local port through a public URL — a Cloudflare tunnel is the simplest way, and it is what the Ungate extension automates.


When I first wrote a local Fastify proxy to connect my Claude subscription to Cursor, the setup felt trivial: start the server on port 8082, open Cursor Settings → Models → Override OpenAI Base URL, paste http://localhost:8082/v1, and hit save. I typed a prompt into chat, hit Enter, and watched the status indicator spin forever.

I opened terminal logs expecting to see incoming HTTP requests. Nothing. I ran tcpdump on my loopback interface — zero packets from Cursor.

I spent an hour double-checking port bindings and firewall rules before realizing the issue wasn't in my code. It is in how Cursor is built.

 

How Cursor actually routes OpenAI Base URL requests

Unlike VS Code extensions, Aider, or Continue.dev — which send LLM API calls directly from your local Node process — Cursor processes chat and agent context on its own remote servers (api2.cursor.sh).

When you select a model and send a message, the request path is:

Cursor UI
   ↓
Cursor's cloud backend
   ↓
Override OpenAI Base URL
   ↓
LLM Provider
Enter fullscreen mode Exit fullscreen mode

Because the third step originates from Cursor's cloud backend over the public internet, http://localhost:8082/v1 resolves to the backend server's own loopback interface — not your laptop.

This breaks every local setup out of the box. Whether you are trying to route Cursor through Ollama (http://localhost:11434), LiteLLM (http://localhost:4000), vLLM, or a custom proxy, Cursor's backend simply cannot reach your machine's localhost. The safe version of the same idea, cursor localhost base url not working, is exactly the symptom this post explains.

 

Why it looks like a broken proxy

When http://localhost:8082/v1 is configured in Cursor Settings:

  1. The Cursor UI sends your prompt and context to api2.cursor.sh.
  2. The Cursor backend tries to POST to http://localhost:8082/v1/chat/completions from its cloud servers.
  3. The connection is refused or times out on the remote server.
  4. Cursor UI shows "Reconnecting..." or a generic request error (500, 404, or a timeout).
  5. Your local proxy logs stay 100% empty because no TCP connection ever touched your machine.

This makes debugging infuriating. The empty logs suggest your proxy isn't listening, when in reality the request died thousands of miles away before reaching your network.

 

Prove it to yourself in 2 minutes

You can confirm this exact behavior right now without changing your proxy code.

First, check that your local server works:

curl http://localhost:8082/health
# → 200 OK
Enter fullscreen mode Exit fullscreen mode

Second, start an ad-hoc Cloudflare tunnel to expose that port publicly:

cloudflared tunnel \
  --config /dev/null \
  --url http://localhost:8082
Enter fullscreen mode Exit fullscreen mode

Grab the generated https://<random>.trycloudflare.com URL from terminal output and test it from outside your local network (for example, from your phone over LTE):

curl https://<random>.trycloudflare.com/health
# → 200 OK
Enter fullscreen mode Exit fullscreen mode

Paste that https://... address into Cursor Settings → Models → Override OpenAI Base URL. Send a prompt in Cursor chat. Your local proxy logs will instantly light up with incoming requests.

Switch the setting back to http://localhost:8082/v1, and it immediately hangs again. Same machine, same code, different origin.

 

Why a public tunnel is required by design

To let Cursor's cloud backend talk to a local process on your laptop, you need a public endpoint that routes back to your machine.

A Cloudflare tunnel creates an outbound-only WebSocket connection from your machine to Cloudflare's edge network:

Cursor backend
   ↓
https://<tunnel>.trycloudflare.com
   ↓
Cloudflare Edge
   ↓
Local machine (port 8082)
   ↑
outbound-only connection
Enter fullscreen mode Exit fullscreen mode

Because your machine initiates the connection outward, you don't need port forwarding, static IPs, or open inbound firewall ports.

 

Tunnel configuration pitfalls that look like localhost bugs

Once people switch to a tunnel URL, they sometimes hit a new set of errors that look identical to the localhost issue:

  • Cloudflare returns 404 instead of reaching your proxy: If you have cloudflared installed locally, it may silently read ~/.cloudflared/config.yml. If that config contains ingress rules with a catch-all http_status:404 from a previous named tunnel, your quick tunnel will return Cloudflare's 404 page. Prevent this by passing --config /dev/null (see the command below).
  • HTTP 429 Too Many Requests: Anonymous trycloudflare.com quick tunnels share rate limits per IP. For permanent setups, use a named tunnel tied to a free Cloudflare account.
  • Port mismatch: Your proxy is listening on 8082, but cloudflared was started pointing at 8080.
  • 404 from Cloudflare vs 404 from proxy: If curl https://<tunnel>/health returns 404 with a server: cloudflare header, the tunnel isn't reaching your port. If it returns a JSON error, the request reached your proxy.

To force a quick tunnel to ignore ~/.cloudflared/config.yml entirely:

cloudflared tunnel \
  --config /dev/null \
  --url http://localhost:8082 \
  --edge-ip-version 4
Enter fullscreen mode Exit fullscreen mode

 

Step-by-step debugging checklist

If requests in Cursor chat hang or fail when using a custom Base URL:

  1. Verify protocol: Is your Override OpenAI Base URL set to https://? Plain http://localhost will never work.
  2. Test external connectivity: Run curl https://<your-tunnel-url>/health from a non-local network (e.g., mobile hot spot).
  3. Check Cloudflare response headers: If curl returns a 404, check the headers. If server: cloudflare is present, fix the tunnel flags (--config /dev/null).
  4. Inspect proxy authentication: Only after confirming the tunnel reaches your machine should you inspect OAuth tokens, API keys, or provider formats.

There is one more unrelated Cursor quirk: it silently unchecks the Override OpenAI Base URL setting every few hours, which silently sends requests through your API tokens instead. If your proxy suddenly stops receiving requests, check that checkbox first — I explain this bug in my post about using Claude and ChatGPT subscriptions in Cursor.

 

How Ungate automates this in Cursor

Setting up cloudflared manually every time you launch Cursor gets tedious. That's why I built Ungate — an open-source Cursor extension that connects Claude and ChatGPT subscriptions directly to native Cursor chat.

Ungate handles the entire proxy lifecycle automatically:

  • Starts a local proxy on an isolated port.
  • Spawns a platform-specific cloudflared binary with --config /dev/null to bypass local config conflicts.
  • Generates a local proxy API key so public tunnel URLs cannot be abused by unauthorized third parties.
  • Displays live proxy logs, request analytics, and tunnel status right inside Cursor's UI.
  • Auto-re-enables the Base URL setting when Cursor silently disables it.

You can install Ungate by searching for @id:orchidfiles.ungate in Cursor Extensions, or read the full setup guide here: How to use Claude and ChatGPT subscriptions in Cursor.

GitHub repository: https://github.com/orchidfiles/ungate

My Blog: orchidfiles.com
Telegram Channel: @orchidfiles

Top comments (0)