Bỏ qua đến nội dung chính
CORSsame-originsecurityHTTPfrontend

Same-Origin Policy và CORS: Hiểu Đúng Cho Dev

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

Xuất bản 7 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.

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:

GET /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:

OPTIONS /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

npm install cors
import 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

// ❌ 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':

await 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:

<!-- 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.

Zalo