★ Reading this for free? Get 20 structured AI courses + per-chapter AI tutor — the first chapter of every course free, no card.Start free in 30 seconds
Local AI

Ollama 403 Forbidden: The OLLAMA_ORIGINS CORS Fix

August 23, 2026
12 min read
Local AI Master Research Team

Want to go deeper than this article?

Free account unlocks the first chapter of all 25 courses — RAG, agents, MCP, voice AI, MLOps, real GitHub repos.

📚AI Learning Path

Ollama’s running. Here’s what to build with it. Go from “ollama run” to RAG apps, agents, and fine-tuned models — structured and hands-on. First chapter free.

Start free
Or own it for life — Lifetime $149, pay once

A 403 from Ollama is not a connection problem and not an auth problem — it is Ollama's CORS middleware rejecting the Origin header your browser attached, before the request ever reaches the API handler. curl works because curl sends no Origin at all. The fix is to add your app's exact origin to OLLAMA_ORIGINS and restart the server — for example OLLAMA_ORIGINS=chrome-extension://* for a browser extension, or OLLAMA_ORIGINS=http://192.168.1.50:3000 for a web UI on another machine. Two things trip people up. First, on Linux export OLLAMA_ORIGINS=... does nothing, because the daemon runs under systemd and never reads your shell. Second, there is a second middleware that also returns a bare 403 based on the Host header, which is what breaks reverse proxies and tunnels — no Origin involved, and no amount of OLLAMA_ORIGINS will fix it.

The reason this gets filed as a bug rather than found as a setting is that nothing in the response says "CORS". You get a naked 403 with no body. The browser console says the fetch failed. The same URL pasted into curl returns JSON. Nothing about that sequence points at an environment variable.

Everything below was read from the Ollama source on main on 23 August 2026 — envconfig/config.go for the origin list and server/routes.go for the middleware — and from the official FAQ. Bug reports are linked by number so you can check whether yours has moved since.

Why Does curl Work but the Browser Get 403

Because curl does not send an Origin header and a browser always does. That single difference is the whole diagnosis, and you can prove it in one command without touching any config:

curl -i http://localhost:11434/api/tags
curl -i -H "Origin: http://example.com" http://localhost:11434/api/tags

The first returns 200 OK and your model list. The second returns 403 Forbidden with an empty body. Same host, same port, same path, same running server.

That is not a quirk of our setup — it is the exact reproduction in ollama/ollama issue #6021, "API returns 403 Forbidden when Origin http header is set" (opened 28 July 2024, still open at the time of writing). The reporter's failing case was a plain POST to /v1/chat/completions with -H "Origin: abc", triggered in the wild by chatGPTBox. Note what that tells you: the rejection is not limited to preflight OPTIONS requests. Any request carrying a disallowed Origin is aborted with 403, including the real POST.

So the mental model to carry into the rest of this page:

  • No Origin header → the CORS middleware has nothing to check → request proceeds. This is curl, Python requests, a Go client, your terminal.
  • Origin header present and on the allow-list → request proceeds, and the response carries Access-Control-Allow-Origin.
  • Origin header present and not on the allow-list → 403, empty body, no explanation.

Every browser-based client is in the third bucket by default unless its origin happens to be localhost. Web apps, extensions, Obsidian plugins, Electron and Tauri shells, VS Code webviews — all of them attach an origin, and only some of those origins are pre-approved.

Reading articles is good. Building is better.

Free account = 20+ free chapters across 25 courses, with a per-chapter AI tutor. No card. Cancel anytime if you ever upgrade.

Which 403 Do You Have, Origin or Host

Ollama has two independent middlewares that both abort with a bare 403, and they have completely different fixes. From server/routes.go on main, they are registered in this order:

r.Use(
	cors.New(corsConfig),
	allowedHostsMiddleware(s.addr),
)

The first is CORS and looks at Origin. The second is a DNS-rebinding guard and looks at Host. Telling them apart takes two curls:

TestResponseWhat it means
curl -i http://localhost:11434/api/tags200Baseline. Server is healthy.
Same, plus -H "Origin: http://example.com"403Origin rejection. OLLAMA_ORIGINS is your fix.
Same, plus -H "Host: ollama.example.com"403Host rejection. OLLAMA_ORIGINS will not help.
Both headers removed, still 403Not this page. See the note below.

If the third row is the one that fires, skip ahead to the reverse-proxy section — that is a different bug with a different fix, and it is the one that catches people running Ollama behind nginx, Caddy, ngrok, zrok or Cloudflare Tunnel.

One more 403 that is not this page at all: requests to Ollama's hosted cloud models can return 403 for account and plan reasons rather than CORS ones — #16773 asks how to tell in advance whether a model will 403 on a free plan, and #15990 and #15707 are cloud-auth 403s from Claude Code and GitHub Copilot. If the model name in your request ends in :cloud, you are in that family, not this one.

What Origins Does Ollama Already Allow

Seventeen origins are hard-coded and always present, whether or not you set anything. This matters because half the confusion on the tracker comes from people not knowing what is already allowed. Here is the function that builds the list, verbatim from envconfig/config.go on main:

func AllowedOrigins() (origins []string) {
	if s := Var("OLLAMA_ORIGINS"); s != "" {
		origins = strings.Split(s, ",")
	}

	for _, origin := range []string{"localhost", "127.0.0.1", "0.0.0.0"} {
		origins = append(origins,
			fmt.Sprintf("http://%s", origin),
			fmt.Sprintf("https://%s", origin),
			fmt.Sprintf("http://%s", net.JoinHostPort(origin, "*")),
			fmt.Sprintf("https://%s", net.JoinHostPort(origin, "*")),
		)
	}

	origins = append(origins,
		"app://*",
		"file://*",
		"tauri://*",
		"vscode-webview://*",
		"vscode-file://*",
	)

	return origins
}

Count it: three hosts × four scheme/port combinations = 12, plus 5 fixed scheme wildcards = 17 default entries.

Default entryCovers
http://localhost, https://localhostThe bare hostname on the default port
http://localhost:*, https://localhost:*localhost on any port — this is why a Vite dev server on 5173 just works
http://127.0.0.1, https://127.0.0.1, and the :* pairThe same, written as an IP. Browsers treat these as different origins from localhost
http://0.0.0.0, https://0.0.0.0, and the :* pairRarely useful as a browser origin, present for symmetry
app://*Electron apps that serve from a custom app:// scheme
file://*A local HTML file opened directly from disk
tauri://*Tauri shells on macOS and Linux
vscode-webview://*, vscode-file://*VS Code webview panels

Read that table for what is missing, because that absence is the whole reason you are here:

  • chrome-extension://, moz-extension://, safari-web-extension:// — no browser extension origin is allowed by default. This is the single most common 403.
  • Any LAN address. http://192.168.1.50:3000 is not localhost. A web UI on a second machine is rejected.
  • Any real hostname. http://nas.local:8080, https://chat.yourdomain.com — rejected.
  • Tauri on Windows, which does not use the tauri:// scheme. Issue #10507 ("Fully support Tauri in OLLAMA_ORIGINS (Specifically missing Windows support)", open) explains that Windows Tauri apps present as http(s)://tauri.localhost because of WebView constraints, so the tauri://* default misses them entirely.

The list has also grown over time. The output pasted into issue #6389 back in 2024 ends at tauri://* with no VS Code entries. Do not trust any published list, including this one, over your own server's startup log — the last section shows you how to read it.

Does OLLAMA_ORIGINS Replace the Defaults or Add to Them

It adds. Your values are prepended, then all 17 defaults are appended on top, unconditionally. Look again at the function above: the if block seeds origins from the environment variable, and every append after it runs regardless. There is no branch that skips the defaults.

That is exactly what issue #6389, "OLLAMA_ORIGINS environment variables appends instead of sets" (opened 16 August 2024, still open), documents. The reporter set OLLAMA_ORIGINS=*://localhost,*://127.0.0.1 expecting to narrow the list, and got their two entries followed by the full default set:

OLLAMA_ORIGINS:[*://localhost *://127.0.0.1 http://localhost https://localhost
http://localhost:* https://localhost:* http://127.0.0.1 https://127.0.0.1
http://127.0.0.1:* https://127.0.0.1:* http://0.0.0.0 https://0.0.0.0
http://0.0.0.0:* https://0.0.0.0:* app://* file://* tauri://*]

A closed request, #8118, asked for the update policy to be changed to override. It was not.

The practical consequences, both directions:

  • Adding an origin always works. You never need to re-list http://localhost:* to keep it — it is already there. Just add what is missing.
  • Removing an origin is impossible through this variable. You cannot use OLLAMA_ORIGINS to stop file://* pages or localhost from reaching your server. If you need that, you need a reverse proxy in front, which is the subject of our guide to securing an Ollama server.

One more detail from the same function: Var() is strings.Trim(strings.TrimSpace(os.Getenv(key)), "\"'"), so surrounding single or double quotes are stripped for you. Quoting the value in a systemd unit or a shell is safe.

Reading articles is good. Building is better.

Free account = 20+ free chapters across 25 courses, with a per-chapter AI tutor. No card. Cancel anytime if you ever upgrade.

What Origin Is Your Client Actually Sending

Stop guessing and read it off the request. In Chrome or Firefox, open DevTools → Network, trigger the failing call, click it, and read the Origin request header. Whatever string is there is exactly what has to appear in OLLAMA_ORIGINS — character for character, scheme included, no trailing slash.

For clients where DevTools is not available, this table covers the common shapes:

ClientOrigin it sendsOLLAMA_ORIGINS value that works
Chrome / Edge extensionchrome-extension://<32-char id>chrome-extension://*
Firefox extensionmoz-extension://<uuid>moz-extension://*
Safari web extensionsafari-web-extension://<uuid>safari-web-extension://*
Local dev serverhttp://localhost:5173Already allowed — do nothing
Web UI on another machinehttp://192.168.1.50:3000http://192.168.1.50:3000
Web UI behind a domainhttps://chat.yourdomain.comhttps://chat.yourdomain.com
Tauri app, macOS/Linuxtauri://localhostAlready allowed via tauri://*
Tauri app, Windowshttp://tauri.localhosthttp://tauri.localhost (issue #10507)
Electron app with a custom schemeapp://.Already allowed via app://*
A local .html filefile:// or nullfile://* is allowed; a null origin is not

The extension line is the one the official FAQ addresses directly, and its recommended value is a comma-separated set of all three:

OLLAMA_ORIGINS=chrome-extension://*,moz-extension://*,safari-web-extension://* ollama serve

That form — variable in front of the command — only applies to a server you start yourself in that terminal. It is useless if a service manager or a desktop app is already running the daemon, which is the next section's problem.

A note on the null origin. Sandboxed iframes, some data: URLs and certain redirect chains send the literal string null as the origin. There is no wildcard that matches it and adding null to a scheme-checked allow-list is not viable, so if your client sends null, change how the client is loaded rather than fighting the server.

How Do You Set OLLAMA_ORIGINS on Each Platform

Setting the variable in your shell and setting it for the running daemon are different operations on every platform. This is where most "I set it and it still 403s" reports come from.

Linux with systemd. Do not export it. The daemon runs as the ollama system user under systemd and never sees your shell environment. Use a drop-in:

sudo systemctl edit ollama

Add under [Service]:

[Service]
Environment="OLLAMA_ORIGINS=chrome-extension://*,http://192.168.1.50:3000"

Then sudo systemctl daemon-reload && sudo systemctl restart ollama. If any part of that is unfamiliar — where the unit file lives, why the override is a separate file, how to confirm the daemon actually received it — that is a whole mechanism in itself and we wrote it up separately: Ollama systemd service: env vars that don't work.

macOS. The menu-bar app is the server, and it does not inherit your .zshrc. The FAQ's answer is launchctl setenv, then restart the application:

launchctl setenv OLLAMA_ORIGINS "chrome-extension://*"

There is a trap here worth knowing about, recorded in issue #14511 ("Fix: bind: address already in use + 403 from Chrome Extension (macOS)", closed). The reporter tried the inline form instead — OLLAMA_ORIGINS="..." ollama serve — and got:

Error: listen tcp 127.0.0.1:11434: bind: address already in use

The LaunchAgent-managed daemon was already holding the port, so the new server never started and the old one, without the variable, kept answering with 403. The thread's sequence is to find it with launchctl list | grep -i ollama, remove it with launchctl remove com.ollama.ollama, confirm the port is free with lsof -i :11434, and only then start a server with the origins you want. If you see that bind error, your variable did not fail — your server did not start.

Windows. Quit Ollama from the taskbar tray first. Then open Settings, search for environment variables, choose Edit environment variables for your account, add OLLAMA_ORIGINS, click OK, and start Ollama again from the Start menu. A running process cannot pick up a variable edited after it launched.

Docker. The official image already sets OLLAMA_HOST=0.0.0.0:11434 in its final stage and does not bake in an OLLAMA_ORIGINS value, so a -e flag is all you need:

docker run -d -e OLLAMA_ORIGINS="chrome-extension://*,http://192.168.1.50:3000" -p 11434:11434 -v ollama:/root/.ollama --name ollama ollama/ollama

docker run -e overrides an image ENV of the same name, so if you think a baked-in value is winning — the complaint in issue #9359, "The variable 'ollama_origins' in the Docker image cannot be overwritten" (closed) — check the startup log before concluding that. What you are almost certainly looking at is the 17 appended defaults sitting after your value, not a variable you cannot change. If you run Ollama and Open WebUI as separate containers, our Ollama + Open WebUI Docker setup covers the network wiring that makes the origin question mostly disappear.

Why Does Ollama Refuse to Start After You Set It

Because a malformed origin is a fatal startup panic, not a warning. If Ollama stops responding entirely after you edit the variable, do not assume the edit had no effect — assume it had too much.

The CORS layer validates every entry at startup. An origin that contains no * must begin with a recognised scheme, and if it does not, the server dies with this message, quoted from PR #14813:

bad origin: origins must contain '*' or include http://,https://,chrome-extension://,safari-extension://,moz-extension://,ms-browser-extension://

Three ways to trigger it, all of them things people do:

  1. A trailing comma. OLLAMA_ORIGINS="http://example.com," splits into two entries, the second of which is an empty string. That is exactly what PR #14813 (opened 13 March 2026, open at the time of writing) exists to fix — it filters empty entries so that append-style patterns like OLLAMA_ORIGINS="http://example.com,$OLLAMA_ORIGINS" stop crashing. Until it merges, keep your list free of trailing and doubled commas.
  2. A bare hostname. OLLAMA_ORIGINS=example.com has no scheme and no wildcard. Write http://example.com or https://example.com.
  3. Spaces after commas. OLLAMA_ORIGINS="http://a.com, http://b.com" produces a second entry that begins with a space. Split is on the comma only; nothing trims the pieces. No spaces.

There is a second, separate panic in the same layer — only one * is allowed — for a single entry containing more than one wildcard. http://*.example.com:* is two, and will not load.

The tell for all of these is that curl http://localhost:11434 stops answering entirely rather than returning 403. On Linux the message will be in journalctl -u ollama; on macOS it is in ~/.ollama/logs/server.log; in Docker it is in docker logs.

Is OLLAMA_ORIGINS Safe to Set to a Wildcard

OLLAMA_ORIGINS=* will make your 403 go away and it is the wrong fix on any machine you care about. It is worth being precise about why, because the usual "that's insecure" hand-wave does not explain the actual attack.

Ollama has no authentication of its own. The only thing standing between a random web page you happen to visit and your local Ollama instance is the browser's same-origin policy — and OLLAMA_ORIGINS=* is you instructing Ollama to waive it for everyone. After that, any site open in any tab can script requests to http://localhost:11434: enumerate your models via /api/tags, run inference on your hardware, pull multi-gigabyte models onto your disk, or delete the ones you have. None of that needs a network exposure or an open port to the outside world. The tab is the vector.

What to do instead, in order of preference:

  1. List the exact origins. OLLAMA_ORIGINS=chrome-extension://abcdefghijklmnopqrstuvwxyz123456 is more work than * exactly once, at setup.
  2. Scheme-wildcard rather than total-wildcard. chrome-extension://* is a large improvement on * — it still trusts every installed extension, but not every website.
  3. Put a reverse proxy in front if the client is anything other than your own machine, and do authentication there. Our securing Ollama guide has working nginx configs for bearer tokens, basic auth and SSO, and it is the right page once other people are involved.

The same caution scales with OLLAMA_HOST. If you have set both OLLAMA_HOST=0.0.0.0 and OLLAMA_ORIGINS=*, you have an unauthenticated inference server reachable from your whole network with the browser's last safety check switched off.

Why Does a Reverse Proxy or Tunnel Get 403

This 403 has nothing to do with CORS and OLLAMA_ORIGINS cannot fix it. It is a DNS-rebinding guard that inspects the Host header, and it is the reason a setup that works perfectly on localhost breaks the moment you put nginx, Caddy, ngrok, zrok or Cloudflare Tunnel in front of it.

Here is the middleware, verbatim from server/routes.go on main:

func allowedHost(host string) bool {
	host = strings.ToLower(host)

	if host == "" || host == "localhost" {
		return true
	}

	if hostname, err := os.Hostname(); err == nil && host == strings.ToLower(hostname) {
		return true
	}

	tlds := []string{
		"localhost",
		"local",
		"internal",
	}

	// check if the host is a local TLD
	for _, tld := range tlds {
		if strings.HasSuffix(host, "."+tld) {
			return true
		}
	}

	return false
}

And the wrapper that calls it aborts with c.AbortWithStatus(http.StatusForbidden) when it returns false. Two structural details from that wrapper decide whether you are affected at all:

  • If Ollama is bound to a non-loopback address, the whole check is skipped. The middleware returns early when the listen address is not loopback. So a server on 0.0.0.0:11434 never performs the host check — only the default 127.0.0.1 binding does.
  • If the Host header parses as an IP, it is allowed when that IP is loopback, private, unspecified or one of the machine's own addresses. Host: 192.168.1.50:11434 passes. Host: chat.yourdomain.com does not.

Putting those together gives you the accepted set for a default loopback-bound server: an empty Host, localhost, the machine's own hostname, anything ending in .localhost, .local or .internal, and any loopback/private/local IP literal. Everything else is 403, with or without an Origin header.

That is the mechanism behind issue #3269, "Error 403 with zrok and other reverse proxies" (closed), where zrok share public localhost:11434 returned 403 through the tunnel while direct localhost requests were fine, and behind #4262, "403 using zrok". A tunnel forwards its own public hostname in Host; Ollama sees a name it does not recognise and refuses.

Two fixes, pick one:

location / {
  proxy_pass http://127.0.0.1:11434;
  proxy_set_header Host localhost;
  proxy_read_timeout 600s;
  proxy_buffering off;
}

Rewriting Host to localhost at the proxy is the surgical option and keeps the guard doing its job for everything else. The alternative is to bind Ollama to 0.0.0.0 or a specific LAN address, which disables the host check entirely — do that only behind a firewall or a proxy that authenticates, and read the exposure warning in our connection refused on port 11434 walkthrough first, since that page covers what changing the bind address does to everything else.

Tunnels have their own Host-rewrite settings rather than a proxy config file; check your tunnel's documentation for the option that presents localhost to the origin server.

How Do You Confirm the Origin List Ollama Is Using

Ollama prints its entire effective configuration at startup, including the fully resolved origin list. This is the check that ends every argument about whether a variable took effect, and almost nobody knows it exists. From server/routes.go:

slog.Info("server config", "env", envconfig.Values())

So on Linux:

journalctl -u ollama --no-pager | grep "server config"

On macOS:

grep "server config" ~/.ollama/logs/server.log

In Docker: docker logs ollama 2>&1 | grep "server config".

Inside that line you will find OLLAMA_ORIGINS:[...] with every entry, in order — your values first, the defaults after. That output is the ground truth. If your origin is not in that bracket, the variable never reached the process, and you have an environment problem rather than a CORS problem. If it is in there and you still get 403, compare the string character by character against the Origin header in DevTools; a trailing slash or an http vs https mismatch is a different origin as far as the browser is concerned.

The complete list of Ollama's environment variables is in ollama serve --help, and our complete Ollama guide covers the ones that matter day to day.

FAQ

Why does Ollama return 403 to my Chrome extension but not to curl?

Because curl sends no Origin header and an extension always sends chrome-extension://<id>. No extension scheme is in Ollama's 17 default origins, so the CORS middleware aborts the request with 403 before it reaches the API. Set OLLAMA_ORIGINS=chrome-extension://* — or better, the specific extension ID — and restart the server.

Does OLLAMA_ORIGINS replace the default allowed origins?

No. envconfig.AllowedOrigins() seeds the list from your variable and then appends all 17 defaults unconditionally, so your entries are added rather than substituted. Issue #6389 documents this and it is still open. You can widen the allow-list with this variable; you cannot narrow it.

What is the correct OLLAMA_ORIGINS syntax?

A comma-separated list with no spaces and no trailing comma, where every entry either contains a * or starts with a recognised scheme — for example chrome-extension://*,http://192.168.1.50:3000. Surrounding quotes are stripped automatically. A malformed entry does not warn, it panics the server at startup with bad origin: origins must contain '*' or include http://,https://,....

I set OLLAMA_ORIGINS on Linux and nothing changed. Why?

Almost certainly because you exported it in a shell. The Linux daemon runs under systemd as the ollama user with its own environment and never reads your shell. Add it as an Environment= line in a systemctl edit ollama drop-in, run daemon-reload, restart, and confirm with the server config log line above.

Is it safe to set OLLAMA_ORIGINS to a wildcard?

Not on a machine you care about. Ollama has no authentication, so the browser's same-origin policy is the only thing stopping any website you have open from listing your models, running inference on your GPU or pulling models onto your disk. OLLAMA_ORIGINS=* removes that. List real origins, or scheme wildcards at worst, and put a proxy with auth in front if other people are involved.

Why do I still get 403 through nginx when the origin is allowed?

Because that is the other 403. Ollama's allowedHostsMiddleware rejects requests whose Host header is not localhost, the machine's hostname, a .localhost/.local/.internal name, or a private IP literal — and it only runs when Ollama is bound to a loopback address. Add proxy_set_header Host localhost; to your proxy block, or bind Ollama to 0.0.0.0 and secure it another way.

Do I need OLLAMA_ORIGINS for a Python or Node backend?

No. Server-side HTTP clients do not send an Origin header, so the CORS middleware never engages. If a Python script is getting 403, look at whether something in your stack is adding Origin deliberately, or whether you are hitting the Host check through a proxy. Our Ollama Python API guide covers the client-side patterns that work without any CORS configuration at all.

Sources

🎯
AI Learning Path

Ollama’s running. Here’s what to build with it.

Go from “ollama run” to RAG apps, agents, and fine-tuned models — structured and hands-on. First chapter free.

Or own it for life — Lifetime $149 $599, pay once
Once your hardware is sorted

Stop piecing Ollama together from blog posts

Ollama Mastery is 15 chapters end to end — install, model choice, Modelfiles, GPU offload, the API, and the 20 errors that actually happen. Plus 24 more courses.

$149 once unlocks everything, forever — about $0.27/chapter for life. Prefer to spread it out? Pro is $79/year (saves 27%) or $8.99/month.
Secure checkout by Lemon Squeezy — your card never touches this siteInstant access the moment you payFirst chapter of every course is free — try before you buy

Liked this? 20 full AI courses are waiting.

From fundamentals to RAG, agents, MCP servers, voice AI, and production deployment with real GitHub repos. First chapter free, every course.

Reading now
Join the discussion

Local AI Master Research Team

Creator of Local AI Master. I've built datasets with over 77,000 examples and trained AI models from scratch. Now I help people achieve AI independence through local AI mastery.

Build Real AI on Your Machine

RAG, agents, NLP, vision, and MLOps - chapters across 25 courses that take you from reading about AI to building AI.

Want structured AI education?

25 courses, 519+ chapters, from $9. Understand AI, don't just use it.

AI Learning Path
More on Ollama
See the full Best Ollama Models 2026 guide.

Comments (0)

No comments yet. Be the first to share your thoughts!

📅 Published: August 23, 2026🔄 Last Updated: August 23, 2026✓ Manually Reviewed

Ready to Go Beyond Tutorials?

20 structured courses with hands-on chapters - build RAG chatbots, AI agents, and ML pipelines on your own hardware.

🎯
AI Learning Path

Go from reading about AI to building with AI

20 structured courses. Hands-on projects. Runs on your machine. Start free.

Or own it for life — Lifetime $149 $599, pay once

Was this helpful?

LM

Written by the Local AI Master Team

The team behind Local AI Master

We build Local AI Master around practical, testable local AI workflows: model selection, hardware planning, RAG systems, agents, and MLOps. The goal is to turn scattered tutorials into a structured learning path you can follow on your own hardware.

✓ Local AI Curriculum✓ Hands-On Projects✓ Open Source Contributor
📚
Free · no account required

Grab the AI Starter Kit — career roadmap, cheat sheet, setup guide

No spam. Unsubscribe with one click.

🎯
AI Learning Path

Ollama’s running. Here’s what to build with it.

Go from “ollama run” to RAG apps, agents, and fine-tuned models — structured and hands-on. First chapter free.

Or own it for life — Lifetime $149 $599, pay once
Free Tools & Calculators