CORS là gì? Tìm hiểu Cross-Origin Resource Sharing từ A đến Z

CORS là gì? Tìm hiểu Cross-Origin Resource Sharing từ A đến Z

9 phút đọc

CORS Cross-Origin Resource Sharing là một cơ chế bảo mật của trình duyệt giúp kiểm soát việc truy cập tài nguyên giữa các domain khác nhau. Nếu bạn từng gặp lỗi

1. CORS là gì?#

CORS (Cross-Origin Resource Sharing) là một cơ chế cho phép một trang web truy cập tài nguyên từ một domain khác với domain của chính trang web đó. Nói cách khác, CORS là “người gác cổng” kiểm soát các request cross-origin trong trình duyệt.

Ví dụ thực tế:#

Frontend:  https://myapp.com
API:       https://api.myapp.com  ← Cross-origin!

Khi frontend tại myapp.com gọi API tại api.myapp.com, đây là một cross-origin request. Nếu không có CORS, trình duyệt sẽ chặn request này.

Origin là gì?#

Một origin được xác định bởi 3 yếu tố:

Hai URL có cùng origin khi cả 3 yếu tố trên đều giống hệt nhau.

URL 1 URL 2 Cùng Origin?
https://site.com/page1 https://site.com/page2
https://site.com http://site.com ✗ (khác protocol)
https://site.com https://api.site.com ✗ (khác domain)
https://site.com https://site.com:8080 ✗ (khác port)

2. Tại sao cần CORS? — Same-Origin Policy#

Để hiểu CORS, trước tiên phải hiểu Same-Origin Policy (SOP) — chính sách bảo mật nền tảng của web.

Same-Origin Policy là gì?#

SOP là quy tắc bảo mật mặc định của trình duyệt: một script từ origin A không thể đọc dữ liệu từ origin B. Điều này ngăn chặn:

  • Trang web độc hại đọc dữ liệu từ tài khoản ngân hàng của bạn (đã đăng nhập ở tab khác)
  • Một website lạ gửi request đến API nội bộ của công ty bạn
  • Kẻ tấn công đánh cắp thông tin cá nhân qua cross-site scripting

Vấn đề phát sinh#

SOP rất tốt cho bảo mật, nhưng trong thực tế phát triển web hiện đại, chúng ta cần cross-origin requests một cách hợp pháp:

  • Frontend (React, Vue) chạy trên localhost:3000, gọi API tại localhost:8000
  • Microservices triển khai trên nhiều domain khác nhau
  • CDN phục vụ static assets từ domain riêng
  • Nhúng widget, font, hoặc iframe từ bên thứ ba

→ CORS ra đời để giải quyết mâu thuẫn này: cho phép server “nới lỏng” SOP một cách có kiểm soát.


3. CORS hoạt động như thế nào?#

CORS hoạt động thông qua các HTTP headers. Khi trình duyệt gửi một cross-origin request, server phản hồi với các header CORS để cho biết liệu request có được phép hay không.

Luồng hoạt động cơ bản:#

┌──────────┐     Request + Origin      ┌──────────┐
│ Browser  │ ──────────────────────────→│  Server  │
│          │                            │          │
│ (kiểm    │  Response + CORS Headers   │ (quyết   │
│  tra     │ ←───────────────────────── │  định)   │
│  CORS)   │                            │          │
└──────────┘                            └──────────┘
  1. Trình duyệt tự động thêm header Origin vào request
  2. Server kiểm tra origin và trả về CORS headers (nếu được phép)
  3. Trình duyệt kiểm tra CORS headers và quyết định cho phép/từ chối

Nếu server không trả về CORS headers phù hợp, trình duyệt sẽ chặn response và báo lỗi CORS trong console.


4. Preflight Request là gì?#

Preflight request là một request OPTIONS được trình duyệt tự động gửi trước request thực tế để kiểm tra xem server có cho phép cross-origin request hay không.

Khi nào preflight được kích hoạt?#

Preflight được gửi khi request không phải là simple request. Một request được coi là “simple” khi thỏa mãn tất cả điều kiện sau:

Điều kiện Simple Request
Method GET, HEAD, POST
Content-Type application/x-www-form-urlencoded, multipart/form-data, text/plain
Headers Chỉ các header “an toàn” (Accept, Accept-Language, Content-Language, v.v.)

Bất kỳ sai khác nào cũng kích hoạt preflight. Ví dụ:

  • Gửi Content-Type: application/jsonpreflight
  • Gửi header Authorizationpreflight
  • Dùng method PUT, PATCH, DELETEpreflight

Luồng Preflight:#

┌──────────┐  OPTIONS /api/data            ┌──────────┐
│ Browser  │ ──────────────────────────────→│  Server  │
│          │  Access-Control-Allow-Origin   │          │
│          │ ←──────────────────────────────│          │
│          │  (nếu OK)                      │          │
│          │  POST /api/data (request thật) │          │
│          │ ──────────────────────────────→│          │
│          │  Response + CORS Headers       │          │
│          │ ←──────────────────────────────│          │
└──────────┘                                └──────────┘

5. Các header CORS quan trọng#

Response Headers (Server → Browser)#

Header Mô tả Ví dụ
Access-Control-Allow-Origin Chỉ định origin nào được phép truy cập * hoặc https://myapp.com
Access-Control-Allow-Methods Các HTTP method được phép GET, POST, PUT, DELETE
Access-Control-Allow-Headers Các request header được phép Content-Type, Authorization
Access-Control-Allow-Credentials Cho phép gửi cookie/credentials true
Access-Control-Max-Age Thời gian cache preflight (giây) 3600
Access-Control-Expose-Headers Các response header JS được đọc X-Total-Count, Link

Request Headers (Browser → Server)#

Header Mô tả
Origin Origin của trang web gửi request (tự động)
Access-Control-Request-Method Method sẽ dùng trong request thật (preflight)
Access-Control-Request-Headers Headers sẽ dùng trong request thật (preflight)

Ví dụ Response với CORS Headers:#

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400

6. CORS trong thực tế#

Cấu hình CORS với Express.js (Node.js)#

const express = require('express');
const cors = require('cors');
const app = express();

// Cách 1: Cho phép tất cả origins (chỉ dùng cho dev)
app.use(cors());

// Cách 2: Cấu hình chi tiết (khuyên dùng cho production)
app.use(cors({
  origin: ['https://myapp.com', 'https://admin.myapp.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400
}));

Cấu hình CORS với Django (Python)#

# settings.py
INSTALLED_APPS = [
    ...
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...
]

CORS_ALLOWED_ORIGINS = [
    "https://myapp.com",
    "https://admin.myapp.com",
]
CORS_ALLOW_CREDENTIALS = True

Cấu hình CORS với Spring Boot (Java)#

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("https://myapp.com")
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowedHeaders("*")
            .allowCredentials(true)
            .maxAge(3600);
    }
}

Cấu hình CORS với Nginx (Reverse Proxy)#

location /api/ {
    proxy_pass http://backend:8000;

    add_header Access-Control-Allow-Origin $http_origin;
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
    add_header Access-Control-Allow-Headers "Content-Type, Authorization";
    add_header Access-Control-Allow-Credentials "true";

    if ($request_method = 'OPTIONS') {
        return 204;
    }
}

7. Các lỗi CORS thường gặp và cách khắc phục#

Lỗi 1: Access-Control-Allow-Origin header missing#

Access to fetch at 'https://api.example.com/data' from origin
'https://myapp.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

Nguyên nhân: Server không trả về CORS headers.
Cách khắc phục: Cấu hình server để trả về Access-Control-Allow-Origin phù hợp.

Lỗi 2: Origin không được phép#

The 'Access-Control-Allow-Origin' header has a value
'https://allowed.com' that is not equal to the supplied origin.

Nguyên nhân: Origin của bạn không nằm trong danh sách được phép.
Cách khắc phục: Thêm origin vào whitelist của server.

Lỗi 3: Preflight bị từ chối#

Response to preflight request doesn't pass access control check:
It does not have HTTP ok status.

Nguyên nhân: Server không xử lý OPTIONS request hoặc trả về lỗi.
Cách khắc phục: Đảm bảo server xử lý OPTIONS request và trả về 2xx.

Lỗi 4: Credentials + Wildcard Origin#

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

Nguyên nhân: Không thể dùng * khi credentials: true.
Cách khắc phục: Chỉ định origin cụ thể thay vì wildcard *.

Lỗi 5: Header không được phép#

Request header field X-Custom-Header is not allowed
by Access-Control-Allow-Headers in preflight response.

Nguyên nhân: Request chứa header không có trong Access-Control-Allow-Headers.
Cách khắc phục: Thêm header vào danh sách cho phép.


8. CORS vs JSONP vs Proxy — So sánh giải pháp#

Giải pháp Ưu điểm Nhược điểm Khi nào dùng?
CORS Chuẩn W3C, bảo mật, linh hoạt Cần cấu hình server Giải pháp chính cho mọi cross-origin API
JSONP Hỗ trợ trình duyệt cũ Chỉ GET, rủi ro XSS Legacy systems (không khuyến nghị)
Proxy Server Không cần cấu hình CORS Thêm chi phí, độ trễ Gọi API bên thứ ba không hỗ trợ CORS
PostMessage An toàn cho iframe Chỉ cho window/iframe Giao tiếp giữa các window/iframe

9. Best Practices bảo mật khi cấu hình CORS#

1. Không dùng wildcard * trong production#

// ❌ Nguy hiểm
Access-Control-Allow-Origin: *

// ✓ An toàn
Access-Control-Allow-Origin: https://myapp.com

2. Whitelist origin cụ thể#

const allowedOrigins = ['https://myapp.com', 'https://admin.myapp.com'];

app.use(cors({
  origin: (origin, callback) => {
    if (allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  }
}));

3. Chỉ cho phép các method cần thiết#

// ❌ Dư thừa
methods: '*'

// ✓ Cụ thể
methods: ['GET', 'POST']

4. Validate Origin trên server-side#

CORS là cơ chế phía trình duyệt, không phải firewall. Luôn validate authentication/authorization độc lập — CORS không thay thế cho API security.

5. Dùng credentials: true cẩn thận#

Chỉ bật khi thực sự cần gửi cookie/token qua cross-origin requests, và luôn chỉ định origin cụ thể.

6. Xử lý preflight hiệu quả#

Set Access-Control-Max-Age hợp lý để giảm số lượng preflight requests, giảm tải cho server và tăng tốc độ tải trang.


10. Tổng kết#

Khái niệm Giải thích
CORS Cơ chế cho phép cross-origin requests một cách an toàn
Same-Origin Policy Chính sách mặc định chặn cross-origin requests
Preflight Request Request OPTIONS kiểm tra quyền trước khi gửi request thật
Access-Control-Allow-Origin Header quan trọng nhất — xác định origin được phép

Key takeaways:#

  1. CORS là cơ chế bảo mật của trình duyệt, không phải của server
  2. Server quyết định cho phép origin nào thông qua CORS headers
  3. Preflight request là cách trình duyệt “xin phép” trước các request phức tạp
  4. Không dùng * trong production — luôn chỉ định origin cụ thể
  5. CORS không thay thế authentication — luôn bảo vệ API bằng token/session

Tài liệu tham khảo:


Mai Hoàn

Mai Hoàn

Tác giả

Founder & CEO tại vnDev Co., Ltd. Hơn 10 năm kinh nghiệm trong lĩnh vực phát triển phần mềm, thiết kế website và digital marketing. Đam mê chia sẻ kiến thức về công nghệ, SEO và khởi nghiệp. Triết lý làm việc: 'Công nghệ phải phục vụ con người, không phải ngược lại.'

Đồng hành cùng doanh nghiệp của Bạn trong kỷ nguyên AI — Là sứ mệnh của Chúng tôi!

© 2008-2025 vnDev Co., Ltd. All rights reserved.

8a7a7a0