[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"academy-blogs-en-1-1-all-cors-error-explained-how-to-fix-all--*":3,"academy-blog-translations-kf88oxcm9qxulws":93,"academy-blog-faqs-kf88oxcm9qxulws-en":94},{"data":4,"page":92,"perPage":92,"totalItems":92,"totalPages":92},[5],{"alt":6,"collectionId":7,"collectionName":8,"content":9,"cover_image":10,"cover_image_l_url":11,"cover_image_m_url":12,"cover_image_path":13,"cover_image_s_url":14,"created":15,"created_by":16,"expand":17,"id":85,"keywords":86,"locale":55,"published_at":87,"scheduled_at":72,"school_blog":81,"short_description":88,"status":79,"title":89,"updated":90,"updated_by":91,"slug":82,"views":84},"Cover for CORS Error Explained: a building entrance at dusk seen from outside, with a glass security turnstile and an empty guard counter standing for the browser checking the origin, plus panels showing different origins: Vite at http:\u002F\u002Flocalhost:5173, API at http:\u002F\u002Flocalhost:3000, and the CORS header Access-Control-Allow-Origin","sclblg987654321","school_blog_translations","\u003Cp>Picture this: you have just finished your first page with Vite, running at \u003Ccode>http:\u002F\u002Flocalhost:5173\u003C\u002Fcode>, and you call \u003Ccode>fetch\u003C\u002Fcode> to load a list of users from the API a teammate is running at \u003Ccode>http:\u002F\u002Flocalhost:3000\u003C\u002Fcode>. You try the same URL with curl and the data comes back fine. But from the page, nothing shows up. You open the Console and find one long red line with the words CORS policy in the middle.\u003C\u002Fp>\u003Cp>Don't panic. Almost everyone who connects a frontend to an API for the first time hits this exact message at this exact moment. What makes it confusing is that your code looks correct and the server is clearly working. Today Superdev Academy walks through what a CORS error actually is, why the browser blocks it, whether your request ever reached the server, and most importantly where the fix belongs, because it is not in your frontend code, which is where most people start looking.\u003C\u002Fp>\u003Ch2>What the red message is actually telling you\u003C\u002Fh2>\u003Cp>We recreated the scenario above on a real machine with two small Node.js servers: one serving the page on port 5173, and one API on port 3000 with no CORS configuration at all. Opening the page in Google Chrome produced this line in the Console:\u003C\u002Fp>\u003Cpre>\u003Ccode>Access to fetch at 'http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fusers' from origin 'http:\u002F\u002Flocalhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Read it in pieces and it tells you everything. The first part says where the request went. The second says which origin your page came from. The last part gives the reason: the server's response did not include a header called \u003Ccode>Access-Control-Allow-Origin\u003C\u002Fcode>.\u003C\u002Fp>\u003Cp>Inside your JavaScript, though, the \u003Ccode>catch\u003C\u002Fcode> block only receives \u003Ccode>TypeError: Failed to fetch\u003C\u002Fcode>, with no further detail. That is not a bug. \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FGuides\u002FCORS\">MDN's CORS guide\u003C\u002Fa> explains that the specifics of a CORS failure are deliberately hidden from JavaScript for security reasons. Your code only learns that something failed, and the only place to see why is the browser's Console. So whenever you see \u003Ccode>Failed to fetch\u003C\u002Fcode>, open the Console before you start rewriting code.\u003C\u002Fp>\u003Ch2>What is an origin, and why is localhost not the same as localhost?\u003C\u002Fh2>\u003Cp>The word that appears most in these errors is origin. \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FGlossary\u002FOrigin\">MDN defines it\u003C\u002Fa> as three parts of a URL: the scheme (the protocol, such as \u003Ccode>http\u003C\u002Fcode> or \u003Ccode>https\u003C\u002Fcode>), the host (the domain name) and the port. Two URLs share an origin only when all three match. The path after them does not count.\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>\u003Ccode>http:\u002F\u002Flocalhost:5173\u003C\u002Fcode> and \u003Ccode>http:\u002F\u002Flocalhost:3000\u003C\u002Fcode>: different origins, because the ports differ\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Ccode>http:\u002F\u002Fexample.com\u003C\u002Fcode> and \u003Ccode>https:\u002F\u002Fexample.com\u003C\u002Fcode>: different origins, because the schemes differ\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Ccode>http:\u002F\u002Fexample.com\u003C\u002Fcode> and \u003Ccode>http:\u002F\u002Fwww.example.com\u003C\u002Fcode>: different origins, because the hosts differ\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Ccode>http:\u002F\u002Fexample.com\u002Fapp1\u003C\u002Fcode> and \u003Ccode>http:\u002F\u002Fexample.com\u002Fapp2\u003C\u002Fcode>: same origin, only the path differs\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>In plain terms, your page and your API can run on the same laptop under the same name, localhost, and the browser still treats them as two different sites if the ports do not match. During development that is almost always the case, since Vite uses port 5173 by default and the API usually runs on another one.\u003C\u002Fp>\u003Ch2>The Same-Origin Policy: who is the browser protecting?\u003C\u002Fh2>\u003Cp>The rule behind the message is the Same-Origin Policy. \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FSecurity\u002FSame-origin_policy\">According to MDN\u003C\u002Fa>, it is a security mechanism that restricts how a document or script from one origin can interact with resources from another. Reading data across origins is, by default, not allowed.\u003C\u002Fp>\u003Cp>MDN's own example makes the reason obvious. Say you are logged into your webmail and open some unknown site in another tab. Without this rule, a script on that site could fetch your webmail, and because the browser attaches your cookies automatically, read your inbox and send it wherever it likes.\u003C\u002Fp>\u003Cblockquote>\u003Cp>A CORS error is not there to stop you. It is there to stop other sites from reading a user's data through that user's own browser.\u003C\u002Fp>\u003C\u002Fblockquote>\u003Cp>CORS, short for Cross-Origin Resource Sharing, is how a server relaxes that rule on purpose: it uses HTTP headers to declare which origins may read its responses. Notice who does the declaring. It is the server, not the page. That one fact drives every correct fix in this article.\u003C\u002Fp>\u003Ch2>The request reached the server; the browser just would not hand over the response\u003C\u002Fh2>\u003Cp>The most common beginner assumption is that the browser stopped the request before it left the machine. In our test, the API's log showed this:\u003C\u002Fp>\u003Cpre>\u003Ccode>[api] GET \u002Fapi\u002Fusers Origin=http:\u002F\u002Flocalhost:5173\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>So the GET request did reach the server, and the server sent back its full response. The browser then checked that response, found no header allowing our origin, and refused to let our JavaScript read it. The failure is in reading the response, not in sending the request.\u003C\u002Fp>\u003Cp>That also explains the other puzzle: why curl or Postman works fine. We called the same API like this:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">curl -i -H \"Origin: http:\u002F\u002Flocalhost:5173\" http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fusers\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>It returned \u003Ccode>HTTP\u002F1.1 200 OK\u003C\u002Fcode> with the full JSON body and not a single CORS header. CORS is enforced by browsers; curl and Postman do not enforce it. A request that works in curl proves nothing about whether it will work in a browser. If you want a refresher on requests, responses and status codes first, read \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.superdevacademy.com\u002Fen\u002Fblogs\u002Fhttp-status-codes-explained\">HTTP Status Codes Explained\u003C\u002Fa> alongside this.\u003C\u002Fp>\u003Ch2>What is a preflight, and why does an OPTIONS request show up first?\u003C\u002Fh2>\u003Cp>If the Network tab shows an \u003Ccode>OPTIONS\u003C\u002Fcode> request going out before your real one, that is a preflight request: the browser asking the server in advance, \"if I send this, will you accept it?\"\u003C\u002Fp>\u003Cp>MDN's CORS guide calls requests that skip this step simple requests, and they must meet every condition. For example, the method must be \u003Ccode>GET\u003C\u002Fcode>, \u003Ccode>HEAD\u003C\u002Fcode> or \u003Ccode>POST\u003C\u002Fcode>, and any \u003Ccode>Content-Type\u003C\u002Fcode> must be one of \u003Ccode>application\u002Fx-www-form-urlencoded\u003C\u002Fcode>, \u003Ccode>multipart\u002Fform-data\u003C\u002Fcode> or \u003Ccode>text\u002Fplain\u003C\u002Fcode>. Step outside those rules, say a \u003Ccode>POST\u003C\u002Fcode> with \u003Ccode>Content-Type: application\u002Fjson\u003C\u002Fcode> or an \u003Ccode>Authorization\u003C\u002Fcode> header, and the browser sends a preflight first, carrying \u003Ccode>Access-Control-Request-Method\u003C\u002Fcode> and \u003Ccode>Access-Control-Request-Headers\u003C\u002Fcode> to describe the real request.\u003C\u002Fp>\u003Cp>In the same test we sent a JSON POST to the unconfigured API. The server log showed only \u003Ccode>OPTIONS \u002Fapi\u002Fusers\u003C\u002Fcode>, with no POST after it, and the Console said:\u003C\u002Fp>\u003Cpre>\u003Ccode>... has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>So when a request needs a preflight and the preflight fails, the real request is never sent at all. That is different from the plain GET case, where the request arrives but the response cannot be read. When you see the word preflight in the error, check how your server answers \u003Ccode>OPTIONS\u003C\u002Fcode>.\u003C\u002Fp>\u003Ch2>Fix it in the right place: headers on the server\u003C\u002Fh2>\u003Cp>Because permission comes from the server, the lasting fix lives in the API, not the page. Here is the same API with CORS configured, written with Node.js's built-in \u003Ccode>http\u003C\u002Fcode> module so you can see it really is just a few headers:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-js\">import http from 'node:http';\n\nhttp.createServer((req, res) =&gt; {\n  res.setHeader('Access-Control-Allow-Origin', 'http:\u002F\u002Flocalhost:5173');\n  res.setHeader('Vary', 'Origin');\n\n  if (req.method === 'OPTIONS') {\n    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');\n    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n    res.setHeader('Access-Control-Max-Age', '600');\n    res.writeHead(204);\n    return res.end();\n  }\n\n  res.setHeader('Content-Type', 'application\u002Fjson');\n  res.end(JSON.stringify({ users: ['Mint', 'Beam'] }));\n}).listen(3000);\u003C\u002Fcode>\u003C\u002Fpre>\u003Cul>\u003Cli>\u003Cp>\u003Ccode>Access-Control-Allow-Origin\u003C\u002Fcode>: the origin allowed to read the response, set to match your page exactly\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Ccode>Vary: Origin\u003C\u002Fcode>: MDN recommends it whenever you name a specific origin, so caches know the response depends on the requesting origin\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Ccode>Access-Control-Allow-Methods\u003C\u002Fcode> and \u003Ccode>Access-Control-Allow-Headers\u003C\u002Fcode>: tell the preflight which methods and headers are accepted\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Ccode>Access-Control-Max-Age\u003C\u002Fcode>: how many seconds the browser may cache the preflight result; 600 in this example\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>To confirm the setup, simulate the preflight with curl:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">curl -i -X OPTIONS \\\n  -H \"Origin: http:\u002F\u002Flocalhost:5173\" \\\n  -H \"Access-Control-Request-Method: POST\" \\\n  -H \"Access-Control-Request-Headers: content-type\" \\\n  http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fusers\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>We got \u003Ccode>HTTP\u002F1.1 204 No Content\u003C\u002Fcode> with \u003Ccode>Access-Control-Allow-Origin: http:\u002F\u002Flocalhost:5173\u003C\u002Fcode> and the rest of the headers. Reloading the page, both the GET and the JSON POST returned data normally, without changing a single line of frontend code.\u003C\u002Fp>\u003Cp>In real projects hardly anyone writes these headers by hand; most frameworks ship CORS middleware. To see how middleware sits in the middle of every request, read \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.superdevacademy.com\u002Fen\u002Fblogs\u002Fgo-http-middleware-control-request\">Go and HTTP Middleware\u003C\u002Fa>, which lists CORS handling as one of middleware's common jobs. If you have never built an API yourself, start with \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.superdevacademy.com\u002Fen\u002Fblogs\u002Fcreating-api-javascript-nodejs-vs-go\">Creating an API with Node.js and Go\u003C\u002Fa>.\u003C\u002Fp>\u003Ch2>During local development, Vite's proxy is a valid alternative\u003C\u002Fh2>\u003Cp>Sometimes you do not own the API, or you would rather not touch server code while building the frontend. The other correct option is to stop the browser from crossing origins at all. \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fvite.dev\u002Fconfig\u002Fserver-options\">Vite's documentation\u003C\u002Fa> describes \u003Ccode>server.proxy\u003C\u002Fcode>, which makes the dev server forward any request whose path starts with a given prefix to another server.\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-js\">\u002F\u002F vite.config.js\nimport { defineConfig } from 'vite'\n\nexport default defineConfig({\n  server: {\n    proxy: {\n      '\u002Fapi': 'http:\u002F\u002Flocalhost:3000',\n    },\n  },\n})\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Your page then calls just \u003Ccode>fetch('\u002Fapi\u002Fusers')\u003C\u002Fcode>, with no domain. The browser sees a request to \u003Ccode>localhost:5173\u003C\u002Fcode>, the same origin as the page, and Vite passes it on to port 3000 server-to-server, where CORS does not apply. We tried it with Vite 8.3.1 against the API that had no CORS headers at all: the page got its data and the Console stayed clean.\u003C\u002Fp>\u003Cp>Keep in mind that \u003Ccode>server.proxy\u003C\u002Fcode> only works while the dev server is running. Once you build for production, you have to answer the question again: will the page and the API share an origin? If not, you still need the server-side headers from the previous section.\u003C\u002Fp>\u003Ch2>Shortcuts that look like fixes but are not\u003C\u002Fh2>\u003Cp>\u003Cstrong>Can't I just disable browser security, or install an extension that turns CORS off?\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>You can, and the red line really does disappear on your machine. Only on your machine. Every real user opening your site in a normal browser still hits the same error. And a browser with this rule switched off no longer protects you from the webmail scenario above. The shortcut simply moves the problem to launch day.\u003C\u002Fp>\u003Cp>\u003Cstrong>What about \u003Ccode>Access-Control-Allow-Origin: *\u003C\u002Fcode> and be done with it?\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>The \u003Ccode>*\u003C\u002Fcode> wildcard means any origin, and it is fine for a public API that needs no login. But once a request carries cookies or other credentials, MDN's CORS guide is explicit: the server must not use \u003Ccode>*\u003C\u002Fcode> and must name a specific origin instead. We tested it with \u003Ccode>fetch\u003C\u002Fcode> and \u003Ccode>credentials: 'include'\u003C\u002Fcode> against an API answering \u003Ccode>*\u003C\u002Fcode>, and Chrome blocked it with:\u003C\u002Fp>\u003Cpre>\u003Ccode>The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>The same request without credentials went through. So \u003Ccode>*\u003C\u002Fcode> is not a shortcut for anything with a login. If you need several origins, keep an allowlist on the server, echo back the one that matches the request, and add \u003Ccode>Vary: Origin\u003C\u002Fcode>.\u003C\u002Fp>\u003Cdiv data-type=\"horizontalRule\">\u003Chr>\u003C\u002Fdiv>\u003Ch2>Summary: what a CORS error is and where to fix it\u003C\u002Fh2>\u003Cp>A CORS error is the browser telling you that a page from one origin tried to read a response from another origin without the server's permission. An origin is the scheme, host and port together, so a different port alone makes it a different origin. The rule exists to protect users, and for a simple request the request usually reached the server anyway; your code just was not allowed to read the answer.\u003C\u002Fp>\u003Cp>That leaves two correct fixes: set \u003Ccode>Access-Control-Allow-Origin\u003C\u002Fcode> and the related headers on the server, or use Vite's \u003Ccode>server.proxy\u003C\u002Fcode> during development. Next time the red line appears, read the reason at the end of the Console message, then run curl with an \u003Ccode>Origin\u003C\u002Fcode> header to see which headers the server actually sends back. Those two steps tell you where the fix belongs. For the bigger picture of how a page talks to a server, continue with \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.superdevacademy.com\u002Fen\u002Fblogs\u002Fep-30-http-requests-responses-js-go\">Handling HTTP Requests and Responses\u003C\u002Fa>.\u003C\u002Fp>\u003Cp>Follow more articles and tips for people starting out as programmers on our channels.\u003C\u002Fp>\u003Cp>Facebook: \u003Cstrong>Superdev Academy Thailand\u003C\u002Fstrong> — \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.facebook.com\u002Fsuperdev.academy.th\">https:\u002F\u002Fwww.facebook.com\u002Fsuperdev.academy.th\u003C\u002Fa>\u003C\u002Fp>\u003Cp>YouTube: \u003Cstrong>Superdev Academy Channel\u003C\u002Fstrong> — \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.youtube.com\u002F@SuperdevAcademy\">https:\u002F\u002Fwww.youtube.com\u002F@SuperdevAcademy\u003C\u002Fa>\u003C\u002Fp>\u003Cp>Instagram: \u003Cstrong>@superdevacademy\u003C\u002Fstrong> — \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.instagram.com\u002Fsuperdevacademy\u002F\">https:\u002F\u002Fwww.instagram.com\u002Fsuperdevacademy\u002F\u003C\u002Fa>\u003C\u002Fp>\u003Cp>TikTok: \u003Cstrong>@superdevacademy\u003C\u002Fstrong> — \u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.tiktok.com\u002F@superdevacademy?lang=th-TH\">https:\u002F\u002Fwww.tiktok.com\u002F@superdevacademy?lang=th-TH\u003C\u002Fa>\u003C\u002Fp>","cover_cover_9v67he6qjh.webp","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002F567jxtc2w1y0e0x\u002Fl\u002Fcover_cover_9v67he6qjh.webp","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002F567jxtc2w1y0e0x\u002Fm\u002Fcover_cover_9v67he6qjh.webp","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002F567jxtc2w1y0e0x\u002Fcover_cover_9v67he6qjh.webp","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002F567jxtc2w1y0e0x\u002Fs\u002Fcover_cover_9v67he6qjh.webp","2026-09-29 04:53:08.751Z","wfztm6bp9384v3u",{"keywords":18,"locale":49,"school_blog":59},[19,25,29,33,37,41,45],{"collectionId":20,"collectionName":21,"created":22,"created_by":16,"id":23,"name":24,"updated":22,"updated_by":16},"sclkey987654321","school_keywords","2026-09-29 04:52:24.555Z","gha7bar3aonp82k","CORS",{"collectionId":20,"collectionName":21,"created":26,"created_by":16,"id":27,"name":28,"updated":26,"updated_by":16},"2026-09-29 04:52:24.556Z","31yaem7bysicpho","CORS error",{"collectionId":20,"collectionName":21,"created":30,"created_by":16,"id":31,"name":32,"updated":30,"updated_by":16},"2026-09-29 04:52:24.557Z","j2usxffa8kr7nzy","CORS policy",{"collectionId":20,"collectionName":21,"created":34,"created_by":16,"id":35,"name":36,"updated":34,"updated_by":16},"2026-09-29 04:52:24.558Z","4z860r6k4u3vpk8","Access-Control-Allow-Origin",{"collectionId":20,"collectionName":21,"created":38,"created_by":16,"id":39,"name":40,"updated":38,"updated_by":16},"2026-09-29 04:52:24.559Z","su0ip12vpfcijrj","Same-Origin Policy",{"collectionId":20,"collectionName":21,"created":42,"created_by":16,"id":43,"name":44,"updated":42,"updated_by":16},"2026-09-29 04:52:24.560Z","wkn1bhumdnfgkor","Preflight Request",{"collectionId":20,"collectionName":21,"created":46,"created_by":16,"id":47,"name":48,"updated":46,"updated_by":16},"2026-10-01 01:48:50.233Z","b7nqw3r0g2qr2y8","cors error localhost",{"code":50,"collectionId":51,"collectionName":52,"created":53,"flag":54,"id":55,"is_default":56,"label":57,"updated":58},"en","pbc_1989393366","locales","2026-01-22 11:00:02.726Z","twemoji:flag-united-states","qv9c1llfov2d88z",false,"English","2026-04-10 15:42:46.825Z",{"category":60,"collectionId":61,"collectionName":62,"created":63,"expand":64,"id":81,"slug":82,"updated":83,"views":84},"rfxf19ot4iq992c","pbc_2105096300","school_blogs","2026-09-29 04:52:24.561Z",{"category":65},{"blogIds":66,"collectionId":67,"collectionName":68,"created":69,"created_by":70,"id":60,"image":71,"image_alt":72,"image_path":73,"image_s_url":74,"label":75,"name":76,"priority":77,"publish_at":78,"scheduled_at":72,"status":79,"updated":80,"updated_by":70},[],"sclcatblg987654321","school_category_blogs","2026-03-04 08:32:03.969Z","76qprkevbgfdps8","7acfigk1qkd_lv1k6bkji3_7h89h9h63a.webp","","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclcatblg987654321\u002Frfxf19ot4iq992c\u002F7acfigk1qkd_lv1k6bkji3_7h89h9h63a.webp","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclcatblg987654321\u002Frfxf19ot4iq992c\u002Fs\u002F7acfigk1qkd_lv1k6bkji3_7h89h9h63a.webp",{"en":76,"th":76},"Web Development",0,"2025-01-21 03:43:00.282Z","published","2026-08-18 14:48:42.218Z","kf88oxcm9qxulws","cors-error-explained-how-to-fix","2026-10-22 18:38:45.045Z",124,"567jxtc2w1y0e0x",[23,27,31,35,39,43,47],"2026-10-22 05:00:00.000Z","What a CORS error really means, why the browser blocks the response, how preflight works, and the right fix: server headers or a Vite dev proxy.","CORS Error Explained: Why Browsers Block It and How to Fix It","2026-10-22 05:00:01.727Z","ebkf8ra4nd55ual",1,{"th":82,"en":82},[95,99,103,107,111,115],{"answer":96,"id":97,"question":98},"A CORS error is the browser refusing to let a page read a response from an API on a different origin, because the server did not send an Access-Control-Allow-Origin header that allows it. In Chrome the Console says \"has been blocked by CORS policy\", while your JavaScript only receives TypeError: Failed to fetch. The full reason is visible only in the browser Console.","4392xyczb12ivqj","What is a CORS error?",{"answer":100,"id":101,"question":102},"No. A CORS error has no HTTP status code of its own, because the block happens inside the browser rather than on the server. The server often answers normally, for example with 200 OK or 204 No Content, but the browser refuses to let JavaScript read that response. MDN notes the error details are hidden from JavaScript for security, so your code only sees TypeError: Failed to fetch and the real reason appears in the browser Console.","q3kosfmfgax0rva","Does a CORS error have an HTTP status code?",{"answer":104,"id":105,"question":106},"Fix it on the server first. For an Express API, install the cors middleware from npm and call app.use(cors({ origin: 'http:\u002F\u002Flocalhost:5173' })) with an explicit origin, because left unset the origin defaults to *, which requests carrying cookies cannot use. If you turn on credentials: true, always pair it with a named origin. For a React app running on Vite, use server.proxy in vite.config.js during development. Adding CORS headers in React code does nothing.","yk4epjwsvml5jtr","How do you fix a CORS error in React or Express?",{"answer":108,"id":109,"question":110},"A CORS preflight is an OPTIONS request the browser sends before the real one when that request is not a simple request, for example a POST with Content-Type: application\u002Fjson or an Authorization header. The preflight carries Access-Control-Request-Method and Access-Control-Request-Headers. If the server's answer to the preflight fails the check, the browser never sends the real request.","441fqjxvyyuyxd1","What is a CORS preflight request?",{"answer":112,"id":113,"question":114},"You can use Access-Control-Allow-Origin: * for a public API that needs no login, but not for requests that carry cookies or other credentials. MDN states the server must name a specific origin instead. A fetch with credentials: 'include' to an API answering * is blocked by Chrome. For several origins, keep an allowlist, echo back the matching origin, and add Vary: Origin.","q6ieh580z5y18vx","Can I set Access-Control-Allow-Origin to *?",{"answer":116,"id":117,"question":118},"Add server.proxy to vite.config.js, for example '\u002Fapi': 'http:\u002F\u002Flocalhost:3000', and call fetch('\u002Fapi\u002Fusers') from the page without a domain. The browser sees a request to the page's own origin, and the Vite dev server forwards it to port 3000 for you. This works only while the dev server runs; production still needs CORS headers on the server.","36b09qpseefcno5","How do I fix a CORS error on localhost with Vite?"]