Agent

How to Test Local AI APIs from a Browser

A local model server like Ollama works fine from curl but often fails from a browser-based tool — here's the actual CORS mechanism, and how to fix it correctly.

AlleForge TeamAugust 16, 20266 min read

Running a model locally with Ollama, LM Studio, or a similar tool gives you an HTTP API on your own machine. Testing it with curl works immediately. Testing it from a browser-based tool often doesn't — not because the tool is broken, but because of a specific, well-defined browser restriction that applies here the same way it applies to any other local API.

The actual scenario

Local AI model

Local HTTP API (e.g. Ollama at http://localhost:11434)

localhost

Browser-based API client

curl http://localhost:11434/api/generate reaches the model directly — curl is a command-line process, not a web page, so nothing about its request is subject to a browser's rules. A browser-based client sending the exact same request runs into the browser's same-origin policy first, before the request even leaves your machine's network stack in some cases, or before the response can be read even if it does leave.

Ollama's default behavior, precisely

As of current official Ollama documentation: the server binds to 127.0.0.1:11434 by default, and its OpenAI-compatible endpoint is available at http://localhost:11434/v1/. Ollama already allows cross-origin requests from 127.0.0.1 and 0.0.0.0 — additional origins have to be added explicitly via the OLLAMA_ORIGINS environment variable. Local requests require no real authentication: the OpenAI client library requires an api_key value to be present, but Ollama ignores it.

This matters for getting the failure right: Ollama isn't blocking "localhost" in some blanket sense — it already permits 127.0.0.1/0.0.0.0 origins by default. The problem is that a browser-based tool is very rarely served from 127.0.0.1 or 0.0.0.0 itself — it's typically a hosted web app running at its own domain, and that origin isn't on Ollama's default allow-list.

Why this is a cross-origin request

An origin is the combination of scheme, host, and port — https://app.example.com and http://localhost:11434 differ in all three, making a request between them cross-origin by definition, regardless of the fact that "localhost" is involved at all. Before a browser lets JavaScript read the response of a cross-origin request like this, it first sends a preflight — an automatic OPTIONS request asking the target server whether the real request is allowed. The server has to answer with specific headers, most importantly Access-Control-Allow-Origin naming the calling origin (or *). If Ollama's configured allowed origins don't include the browser tool's actual origin, the preflight response won't include it, and the browser blocks the real request before your code ever sees a response — successful or otherwise.

This is not the same failure as "the server is down" or "the port is wrong." The server can be running perfectly, on the right port, waiting for the request — and the browser will still refuse to hand the response to your JavaScript because the origin isn't permitted.

A concrete reproduction

Browser tool's origin:  https://app.example.com
Local API:              http://localhost:11434

A request from the browser tool to http://localhost:11434/api/generate is cross-origin. Without https://app.example.com on Ollama's allowed-origins list, the browser blocks the response — typically surfacing as a generic network/CORS error in the browser's console, not a clear "access denied" message from Ollama itself, which is part of why this is confusing to debug the first time.

The legitimate server-side fix

Setting OLLAMA_ORIGINS to include the calling origin (or, during local development only, a broader value) tells Ollama to answer the preflight correctly for that origin, and the browser then allows the response through:

OLLAMA_ORIGINS=https://app.example.com ollama serve

This is a real, supported fix — but it requires access to the server's own configuration and a restart, which isn't always available (a teammate's machine, a managed environment) and isn't something to reach for casually: broadening allowed origins on a local model server is a genuine, if usually small, security tradeoff (see below), not a setting to widen "just in case."

Where AlleForge fits

This is the same restriction covered in why browser-based API clients can't reach localhost — a local AI server is, from the browser's perspective, just another localhost destination. AlleForge Agent bridges it the same way: install Agent once, start it while testing your local model's API, and the request routes through a local encrypted tunnel instead of the browser's own network stack — Agent makes the actual request as a native process, outside the browser sandbox entirely, so Ollama's CORS configuration never has to change at all.

Agent's confirmed scope is local and private-network destinations — this is exactly that. It doesn't do anything different for an AI API than for any other localhost API; there's no AI-specific handling involved.

What this doesn't fix

Agent solves the browser-to-local-machine boundary specifically. It doesn't fix a model server that isn't actually running, a port that's wrong, an invalid or missing request body, an authentication problem against a cloud AI API (see AI API authentication), or the target server returning a genuine 4xx/5xx application error — those look the same with or without Agent in the loop, because they aren't CORS problems at all. And Agent isn't available on Linux yet.

Other local AI runtimes

Ollama is used here as the concrete example because its API is easy to reach and its documentation is explicit about CORS behavior, but it isn't the only local option. LM Studio, llama.cpp's built-in server, and vLLM's local mode all expose an HTTP API on localhost, and most offer an OpenAI-compatible endpoint the same way Ollama does. Their default ports and CORS/allowed-origin configuration differ from each other and from Ollama's — check each tool's own current documentation for its specifics rather than assuming Ollama's exact defaults apply. The underlying browser restriction, and Agent's fix for it, is the same regardless of which one you're running. See testing Ollama APIs from a browser and testing LM Studio APIs from a browser for the request format and CORS specifics of each.

Security notes

A few practical points worth keeping in mind, specific to running a model server locally rather than generic advice:

  • Don't broaden OLLAMA_ORIGINS (or any local server's CORS config) more than you need — a wildcard origin accepted during development is easy to forget about later.
  • A local model server with no authentication enforced is fine on your own machine; it stops being fine the moment it's reachable from anything beyond localhost. Don't bind it to 0.0.0.0 on a shared or public network without understanding exactly who else can reach it.
  • Local API keys and cloud API keys are not the same kind of secret — a local Ollama instance ignoring the api_key field is expected and harmless; a real cloud provider key (OpenAI, Anthropic, Ollama Cloud) belongs in an environment variable or server-side secret store, never hardcoded into client-side code. See AI API authentication for that distinction in full.

Sources

Ollama's default bind address, OpenAI-compatible endpoint, default CORS-allowed origins, OLLAMA_ORIGINS behavior, and local-vs-cloud authentication model are sourced from Ollama's own current official documentation (docs.ollama.com), fetched directly for this guide rather than assumed from older material.

See this in AlleForge

See AlleForge Agent