Bỏ qua menu, vào nội dung chính

Same-origin policy và CORS: hiểu đúng để cấu hình đúng

Same-origin policy và CORS giải thích đầy đủ: simple vs preflight request, credentials, wildcard pitfall. Code Express CORS đúng cho production.

3 phút đọc

Same-origin policy và CORS là 2 khái niệm dev gặp hằng ngày nhưng dễ hiểu sai. Bài này giải thích từ đầu, code Express, cảnh báo các pitfall thường gặp.

Trang MDN về CORS
Trang MDN về CORS (developer.mozilla.org), ảnh chụp ngày 27/09/2026.

Same-origin policy

Origin = scheme + host + port. Cùng origin: https://alodev.vn:443 = https://alodev.vn:443. Khác:

  • http vs https
  • alodev.vn vs api.alodev.vn (subdomain khác)
  • :443 vs:3000 (port khác)

SOP block:

  • JS đọc response cross-origin (XHR, fetch)
  • JS đọc cross-origin iframe DOM
  • Cookie cross-domain (trừ khi explicit set)

SOP KHÔNG block:

  • Image, script, css cross-origin (load + execute được)
  • Form submit cross-origin
  • Link redirect

CORS - exception cho fetch/XHR

API anh muốn JS từ alodev.vn fetch api.alodev.vn -> cần CORS cho phép.

Simple request - không preflight

Method GET/POST/HEAD + content-type form/text + header chuẩn -> browser gửi thẳng:

textGET /users HTTP/1.1
Host: api.alodev.vn
Origin: https://alodev.vn

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://alodev.vn
Content-Type: application/json

{"users": [...]}

Server set Access-Control-Allow-Origin match -> browser cho JS đọc response.

Preflight request

Method PUT/DELETE/PATCH HOẶC custom header HOẶC content-type application/json -> browser gửi OPTIONS trước:

textOPTIONS /users HTTP/1.1
Host: api.alodev.vn
Origin: https://alodev.vn
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://alodev.vn
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

Server OK -> browser gửi request thật. Cache preflight 24h (Max-Age) tránh OPTIONS mỗi request.

Implement CORS Express

bashnpm install cors
javascriptimport cors from 'cors'

// Đơn giản nhất - cho phép mọi origin (CHỈ cho public API không có auth)
app.use(cors())

// Production: whitelist
const ALLOWED = ['https://alodev.vn', 'https://www.alodev.vn']
app.use(cors({
  origin: (origin, callback) => {
    if (!origin || ALLOWED.includes(origin)) callback(null, true)
    else callback(new Error('Not allowed by CORS'))
  },
  credentials: true,                           // gửi cookie
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  exposedHeaders: ['X-Total-Count'],           // header browser được đọc
  maxAge: 86400,                               // cache preflight
}))

Credentials pitfall

javascript// ❌ Wildcard + credentials - browser reject
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Credentials', 'true')

// ✓ Origin cụ thể với credentials
res.setHeader('Access-Control-Allow-Origin', req.headers.origin) // verify whitelist trước
res.setHeader('Access-Control-Allow-Credentials', 'true')
res.setHeader('Vary', 'Origin')                                   // tránh cache nhầm

Frontend cũng cần credentials: 'include':

javascriptawait fetch('https://api.alodev.vn/users', {
  credentials: 'include',  // gửi cookie cross-origin
})

CORS không thay CSRF protection

CORS chỉ block JS đọc response. Request vẫn được gửi:

html<!-- evil.com - CORS block JS đọc response, nhưng request đã thực thi -->
<form action="https://api.alodev.vn/transfer" method="POST">
  <input name="amount" value="1000000">
</form>
<script>document.forms[0].submit()</script>
<!-- Cookie tự động gửi nếu SameSite=None. CORS không liên quan -->

Đó là lý do cần CSRF token + SameSite cookie. CORS chỉ là một phần defense.

Debug CORS error

Browser console phổ biến: "Access to fetch at... blocked by CORS policy: No 'Access-Control-Allow-Origin' header".

  1. Network tab -> tìm request OPTIONS
  2. Check response headers - có Access-Control-Allow-* không?
  3. Check origin matching exact (case-sensitive, trailing slash)
  4. Credentials: phải Origin cụ thể, không wildcard

Kết luận

Same-origin policy là foundation security web - protect cookie, password manager, history. CORS là exception controlled. Hiểu preflight + credentials là 2 chỗ dễ sai nhất. Setup express cors lib với whitelist + credentials đúng. Tham khảo CSRF token để hoàn thiện defense layer.

Nhận bài mới

Để lại email để nhận bài mới của series Kiến thức lập trình, hoặc theo dõi bằng RSS.

RSS Kiến thức lập trình

Email chỉ dùng để gửi bài mới, xem Chính sách bảo mật.

Trao đổi về dự án

Cần tư vấn?