Your frontend calls an API, and the browser console lights up red: blocked by CORS policy. The request never reached your code — the browser killed it before it left. Annoying, especially when the exact same call works fine in Postman or curl.
Good news: learning how to fix CORS error problems comes down to one HTTP header and knowing where to set it. This guide covers the fix on the frontend, inside your app, and on the server — Nginx and Apache — plus how to confirm it's gone and keep it gone.
Quick answer: A CORS error means the browser blocked a cross-origin request because the server holding the data didn't send an
Access-Control-Allow-Originheader that matches your site's origin. Fix it on the server that owns the API — add the correct CORS response headers in your app, Nginx, or Apache config so it explicitly allows your frontend's origin. Turning off browser security is never the real fix.
What a "Blocked by CORS Policy" Error Actually Means
In the support tickets our team handles at a host like Hostaccent, a blocked CORS request is the number-one "my API is down" false alarm — the API is usually running fine and just missing one header. CORS stands for Cross-Origin Resource Sharing, and it's a browser security rule, not a bug in your code. (MDN's Cross-Origin Resource Sharing reference is the canonical spec if you want every detail.)
When your page at app.example.com requests data from api.example.com, those are two different origins — a different subdomain, domain, or port, even though DNS may resolve them to the same box. The browser makes the API prove it's willing to share by checking one response header: access-control-allow-origin.
If that header is missing, or its value doesn't match your page's origin, the browser refuses to pass the data to your JavaScript. You'll often see the request return a 200 status in the Network tab — and your code still throws. That's the tell: the response arrived, but the browser quarantined it.
This is also why curl and Postman never complain. CORS is a browser rule, enforced after the response lands. Command-line tools don't enforce it, so they'll happily show you the data your browser just blocked.
What Causes CORS Errors (Ranked by How Often We See Them)
Not every CORS error has the same root cause. Ranked by how often they land in our queue, here's what's usually behind that red console line.
- No
Access-Control-Allow-Originheader at all. By far the most common. The API was built for same-origin use, then a separate frontend started calling it, and nobody added CORS headers. - The header exists but the origin doesn't match. You allowed
https://example.com, but your app runs onhttps://www.example.comorhttp://localhost:3000. A trailing slash,httpvshttps, or a missingwwwall count as a mismatch. - The preflight
OPTIONSrequest fails. For anything beyond a simple GET — custom headers likeAuthorization, or aPUT/DELETEmethod — the browser fires anOPTIONSrequest first. If your server doesn't answer it with the right headers and a 2xx status, the real request never runs. - Credentials plus a wildcard. If you send cookies (
credentials: 'include'), you cannot useAccess-Control-Allow-Origin: *. The browser demands an exact origin plusAccess-Control-Allow-Credentials: true. - A proxy or CDN strips the header. Occasionally the app sets the header correctly, but something upstream removes it before it reaches the browser.
Pro Tip: Open the failed request in your browser's Network tab and read the response headers, not the request headers. If
access-control-allow-originisn't listed there, your server never sent it — that's your fix location, every single time.
How to Fix CORS Error Problems on Nginx and Apache
The cleanest place to fix CORS is the layer that owns the API response. If you run your own server, that's usually Nginx or Apache. On our own Nginx → Apache + NVMe stack, we set CORS headers at the web-server layer so every app behind it behaves consistently — no per-app guesswork. Hostaccent's managed servers handle these headers at the Nginx layer, but the approach works on any VPS with root access.
Nginx
Add the headers inside the location block that serves your API, and answer the preflight OPTIONS request explicitly:
nginxlocation /api/ { add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always; ## Handle the preflight OPTIONS request if ($request_method = OPTIONS) { add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always; add_header 'Content-Length' 0; return 204; } }
The always flag matters — without it, Nginx skips the header on 4xx/5xx responses, and your error handling breaks in confusing ways. Reload with nginx -t && systemctl reload nginx, and the change is live in seconds. The official Nginx add_header documentation covers the trickier cases.
Apache
Apache needs mod_headers enabled (a2enmod headers, then restart). Then, in your vhost or .htaccess:
apache## Requires mod_headers: a2enmod headers && systemctl restart apache2 <IfModule mod_headers.c> Header set Access-Control-Allow-Origin "https://app.example.com" Header set Access-Control-Allow-Methods "GET, POST, OPTIONS" Header set Access-Control-Allow-Headers "Authorization, Content-Type" </IfModule>
Restart Apache and test. If you're on shared hosting without mod_headers access, you'll have to set the header in your application code instead — see Apache's mod_headers documentation for the directive reference.
In your app (Node/Express example)
Sometimes the app is the right place — especially if allowed origins are dynamic. Whether your API runs on PHP, Node, or Python, the header is the same:
javascript// npm install cors const cors = require('cors'); app.use(cors({ origin: 'https://app.example.com' }));
If you're wiring up a Node API behind Nginx, our Deploy Node.js App on Linux VPS: PM2 + Nginx Beginner Guide walks through the full stack. Whatever layer you choose, set the header in exactly one place. We regularly see tickets where CORS headers are set in both Nginx and the app, producing a duplicated Access-Control-Allow-Origin — which browsers reject outright.
Fixing a CORS Error on localhost (Dev Environment)
During development, you'll hit CORS constantly — http://localhost:3000 calling an API on another port is textbook cross-origin. A CORS error on localhost is expected, not a sign something is broken. Don't reach for a "disable CORS" extension or --disable-web-security. Those hide the problem, and it reappears in production.
Use a dev proxy instead. Create React App, Vite, and Next.js all support one:
javascript// vite.config.js export default { server: { proxy: { '/api': { target: 'http://localhost:5000', changeOrigin: true } } } }
The proxy makes the browser treat the request as same-origin, so no CORS check fires. Your production build then talks to the real API, where you've set proper server headers.
If you're testing inside containers, the same origin rules apply between services — our Install Docker on Ubuntu VPS: Secure Production Setup guide covers keeping those origins consistent. And if you control the API locally, just add http://localhost:3000 to the allowed list — then remember to remove it before production.
How to Confirm the Fix — and Stop It Coming Back
Once you've set the header, confirm it before celebrating. The fastest check is a preflight simulation with curl:
bash## Simulate a preflight request and inspect the CORS response headers curl -I -X OPTIONS https://api.example.com/data \ -H "Origin: https://app.example.com" \ -H "Access-Control-Request-Method: POST"
Look for access-control-allow-origin in the output. If it's there and matches your origin, the browser will accept it too — the full behaviour of that header is documented in MDN's Access-Control-Allow-Origin reference. This approach is current as of 2026 across all major browsers.
To stop CORS errors recurring:
- Set the header in one layer only. Duplicates cause fresh failures.
- List exact origins; avoid
*in production, especially with credentials. - Document which origins are allowed so the next deploy doesn't silently break it.
When we migrate customer sites, we repeatedly see CORS headers hard-coded to a staging domain and never updated — the site goes live, and every API call breaks. A quick origin audit during deploy prevents it. On managed infrastructure like Hostaccent's, header rules live in version-controlled server config with automated backups, so a bad edit is a rollback away rather than a late-night emergency. A solid Linux VPS Security Baseline (Ubuntu 24.04) in 30 Min and Linux VPS Backup Automation with Rsync + Cron make that rollback painless.
Insider Insight: If CORS "randomly" works and then breaks, check for a duplicated
Access-Control-Allow-Originheader — set in both your app and your server. Browsers silently reject responses that carry the header twice, and it's brutal to spot without reading raw headers.
Key takeaways:
- A CORS error is the browser blocking a response, not your API failing.
- The fix is a correct
Access-Control-Allow-Originheader on the server that owns the API. - Handle the preflight
OPTIONSrequest for anything beyond a simple GET. - Never disable browser security — use a dev proxy for localhost.
- Once you know how to fix CORS error problems at the header level, it takes seconds, not hours.
When to Let Your Server Handle CORS For You
Now that you know how to fix CORS error problems at the source, the real question is where your server config lives. On shared hosting you often can't touch mod_headers or the Nginx layer — there's no control-panel toggle for it, so you're stuck setting headers in app code and hoping nothing upstream strips them.
That's the case for a managed VPS. Hostaccent's VPS Basic plan runs $7.99/mo on an Nginx → Apache stack with 50 GB NVMe SSD storage, SSL, caching, and full root access — so you control CORS headers directly, backed by 99.9% uptime. Honest caveat: a VPS means you manage the server. If you want zero server admin, shared hosting is simpler — you'll just have less control over headers like these. Persistent latency after a fix? Our How to Fix High TTFB in WordPress (2026 Guide) covers the server-side side of speed.
Frequently Asked Questions
How to fix CORS error problems on a live server?
Add the correct Access-Control-Allow-Origin header on the server that serves your API — in your Nginx location block, your Apache vhost, or your app code. Reload the server, then confirm with a curl preflight request. Set the header in one layer only to avoid duplicates.
Why does my API work in Postman but fail with a CORS error?
CORS is enforced by the browser, not by the server. Postman and curl don't apply CORS rules, so they show the response fine. Your browser blocks the same response because the API didn't return an access-control-allow-origin header matching your page's origin.
Can I fix a CORS error from the frontend only?
Not properly. CORS permission comes from the server's response headers, so a pure frontend fix isn't possible for production. In development, a dev proxy sidesteps it. On managed hosts like Hostaccent — a UK-registered company operating since 2012 with UK-based support — you set the header in server config instead.
What does "blocked by CORS policy" mean?
It means the browser stopped your JavaScript from reading a cross-origin response because the server didn't grant permission. The data may have arrived with a 200 status, but without a matching Access-Control-Allow-Origin header, the browser refuses to expose it to your code.
Is disabling CORS in the browser safe?
No. Disabling browser security or using a "CORS unblock" extension only hides the issue on your machine — real users still get blocked. It also removes a genuine protection against malicious cross-site requests. Fix the server headers instead; it's the only fix that reaches production.
How do I fix a CORS error on localhost?
Use your dev server's built-in proxy (Vite, Create React App, or Next.js) so requests look same-origin, or add http://localhost:3000 to your API's allowed origins during development. Avoid disabling web security — remove any localhost origin before you deploy to production.











Discussion
Have a question or tip about this topic? Share it below — your comment will appear after review.