Learning Goals
3 minBy the end of this lesson you will be able to:
- Explain in one sentence what CORS is and why the browser enforces it.
- Read the CORS console error and say which header is missing.
- Decide whether two addresses are same-origin or cross-origin by comparing protocol, host and port.
- Fix a blocked request by adding
app.use(cors()), and restrict it to one origin.
Warm-Up · Who Is Blocking This?
5 minLast lesson you added central error handling, so your API replies with tidy messages instead of crashing.
Now Priya opens a front-end page served at http://localhost:5173. It runs this fetch to her API on port 3000. The browser console shows a red error:
// page served at http://localhost:5173
fetch('http://localhost:3000/api/students')
.then((res) => res.json())
.then((data) => console.log(data))Access to fetch at 'http://localhost:3000/api/students' from origin
'http://localhost:5173' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested resource.The server logs show nothing wrong. So who refused the request?
Reveal
The browser refused it, not the server. The request left the page, but the browser checked the reply and saw no Access-Control-Allow-Origin header giving localhost:5173 permission. So it threw the response away and printed the CORS error. The API never opted in, so the browser protected the user by blocking it.
New Concept · The Guest List at the Door
12 minImagine a private event with a guest list. You may walk up, but the doorman only lets you in if your name is on the list. CORS works the same way: the browser will not let a page talk to another site's API unless that API's server has put the page's origin on its list.
CORS stands for Cross-Origin Resource Sharing. It is a browser safety rule. By default, a page can only call an API at the same origin it was served from. An origin is three parts joined together:
Same-origin (allowed)
Page: http://localhost:3000
API: http://localhost:3000/api
Protocol, host and port all match, so the browser never complains.
Cross-origin (needs permission)
Page: http://localhost:5173
API: http://localhost:3000/api
The port differs (5173 vs 3000), so this is cross-origin. CORS applies.
For two addresses to be the same origin, the protocol (http vs https), the host (localhost vs shopee.com.my) and the port (3000 vs 5173) must all match. Change any one, and it is cross-origin.
The syntax
The server opts in with the cors package. One line, added as middleware, tells the browser “this API welcomes other origins”:
app.use(cors()) // add the Access-Control-Allow-Origin headerThis is middleware, exactly like the app.use(...) you met in Lesson 6. It runs on the way in and attaches the header the browser is looking for.
Why it matters
Your future React app runs on its own port — Vite uses 5173. Your API runs on 3000. Different port means cross-origin, so without CORS the React app simply cannot call your API. Every full-stack project you build hits this wall, so knowing the fix saves hours of confusion.
Worked Example · From Blocked to Allowed
12 minFollow along in your Express project. Priya has a students API on port 3000 and a front-end page on port 5173. We will watch it fail, then fix it.
Step 1 — See the cross-origin fetch get blocked
With no CORS set up, the page on 5173 runs this fetch to the API on 3000:
fetch('http://localhost:3000/api/students')
.then((res) => res.json())
.then((data) => console.log(data))The browser console shows the block. The request never reaches your code:
Access to fetch at 'http://localhost:3000/api/students' from origin
'http://localhost:5173' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested resource.Step 2 — Install the cors package
In the terminal, inside your API project, install it with npm:
$ npm install corsStep 3 — Switch it on before your routes
Near the top of server.js, require the package and register it as middleware — before your routes, so every route gets the header:
const express = require('express')
const cors = require('cors')
const app = express()
app.use(cors()) // allow cross-origin requests
app.get('/api/students', (req, res) => {
res.json([{ name: 'Priya' }, { name: 'Wei Jie' }])
})Step 4 — Reload and watch it succeed
Restart the server, then reload the page on 5173. The same fetch now returns data, printed in the console:
[ { name: 'Priya' }, { name: 'Wei Jie' } ]The browser saw Access-Control-Allow-Origin on the reply, found the page welcome, and handed the data to your code.
Step 5 — Tighten it to a single origin
cors() with no options welcomes every origin. For a real app you usually allow only your own front-end. Pass an origin option:
app.use(cors({ origin: 'http://localhost:5173' }))What changed? Now only the page on 5173 is on the guest list. A request from any other origin gets the same CORS block Priya saw at the start.
Try It Yourself
13 minThree tasks in your Express project. Restart the server with node server.js after each change, and watch the browser console.
Task 1 — Open the door
Install the package with
npm install cors, then addconst cors = require('cors')andapp.use(cors())near the top, before your routes:const cors = require('cors') app.use(cors())Confirm a fetch from a page on a different port now succeeds.
Task 2 — One origin only
Change your line so it welcomes just one origin. Use the address your front-end page is served from:
app.use(cors({ origin: 'http://localhost:5173' }))Reload and check the allowed page still works.
Task 3 — Prove the guest list works
Keep the single-origin setting from Task 2. Now open your API address directly in a new browser tab, or fetch it from a page on a different port. Watch what the browser does, and write one sentence describing which requests get through and which are blocked.
Mini-Challenge · The One-Guest Door
8 minSet up your API so it allows CORS for only one specific origin. Combine today's idea with the middleware order rule from Lesson 6 — the cors line must sit before your routes.
It works if: a fetch from the allowed origin returns data, and a fetch from any other origin shows the CORS block in the console.
Reveal a sample answer
const express = require('express')
const cors = require('cors')
const app = express()
// allow only Priya's front-end origin — this must come before the routes
app.use(cors({ origin: 'http://localhost:5173' }))
app.get('/api/students', (req, res) => {
res.json([{ name: 'Priya' }, { name: 'Arjun' }])
})
app.listen(3000)A fetch from http://localhost:5173 gets the data. A fetch from, say, http://localhost:4000 is refused by the browser with:
Access to fetch at 'http://localhost:3000/api/students' from origin
'http://localhost:4000' has been blocked by CORS policy.The other origin is not on the guest list, so the API never sends it a matching Access-Control-Allow-Origin header. If you put app.use(cors(...)) after the route, the header is added too late and the block returns.
Recap
3 min- CORS is a browser rule: a page may only call an API at a different origin if that API opts in.
- An origin is protocol + host + port — all three must match to be same-origin.
- The block reads
No 'Access-Control-Allow-Origin' header is present. - The cors package fixes it:
npm install cors, thenapp.use(cors())before your routes. - Restrict to one origin with
cors({ origin: '...' }). (A first cross-origin call may send a quietOPTIONSpreflight check first.)
New words
- CORS — Cross-Origin Resource Sharing, the browser rule about calling other origins.
- Origin — protocol + host + port joined together (e.g.
http://localhost:3000). - Cross-origin — when the page and the API differ in protocol, host or port.
- the cors package — Express middleware that adds the CORS headers for you.
- Access-Control-Allow-Origin — the reply header that names which origins are allowed.
Homework
4 min to brief · ~20 min to doRequired
- Add
cors()to your API: runnpm install cors, require it, and registerapp.use(cors())before your routes. In one line, note what changed for the browser — what can it now do that it could not before? Bring the note and a screenshot of a working cross-origin fetch to next lesson.
Optional stretch
- Find out what a CORS preflight request is. In the Network tab, before some fetches the browser sends a quiet
OPTIONSrequest to ask permission first. Write two sentences on what it checks and why it happens before the real request.