Cách sửa lỗi CORS
“Blocked by CORS policy” là cơ chế bảo vệ người dùng của trình duyệt, không phải lỗi trong mã của bạn. Cách sửa luôn nằm ở máy chủ nhận yêu cầu. Hướng dẫn này giải thích trình duyệt kiểm tra những gì và đưa ra cấu hình chạy được cho các trường hợp phổ biến.
CORS thực sự là gì
Trình duyệt sẽ chặn JavaScript trên https://app.example.com đọc phản hồi từ một origin khác (scheme + host + port), chẳng hạn https://api.example.com, trừ khi máy chủ đó cho phép. Máy chủ cho phép bằng các header phản hồi Access-Control-Allow-*. Yêu cầu gửi từ curl, Postman hay backend của bạn không bị ảnh hưởng, nên mới có chuyện “chạy được trên Postman nhưng lỗi trên trình duyệt”.
Đọc thông báo lỗi
- “No ‘Access-Control-Allow-Origin’ header is present” — máy chủ không gửi header CORS nào, hoặc trả về trang lỗi (404, 500) mà không kèm header đó.
- “Response to preflight request doesn’t pass access control check” — yêu cầu
OPTIONSbị lỗi: máy chủ không xử lý, trả về lỗi hoặc thiếu header. - “The value of the ‘Access-Control-Allow-Origin’ header must not be the wildcard ‘*’ when credentials mode is ‘include’” — bạn đang gửi cookie, nên máy chủ phải trả về đúng origin của yêu cầu.
Hãy xem yêu cầu lỗi trong DevTools → Network, hoặc kiểm tra header phản hồi thực tế bằng HTTP Header Checker.
Yêu cầu đơn giản và preflight
Một yêu cầu được coi là “đơn giản” nếu là GET, HEAD hoặc POST chỉ có header cơ bản và phần thân dạng form hoặc văn bản thuần. Mọi yêu cầu khác, như Content-Type: application/json, header Authorization, PUT hay DELETE, sẽ khiến trình duyệt gửi trước một yêu cầu OPTIONS gọi là preflight. Máy chủ của bạn phải phản hồi nó với trạng thái 204 hoặc 200 cùng các header sau:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 600
Cấu hình chạy được
Express với gói cors:
const cors = require('cors');
app.use(cors({
origin: ['https://app.example.com'],
credentials: true, // chỉ bật khi bạn gửi cookie
methods: ['GET', 'POST', 'PUT', 'DELETE'],
}));
Nginx:
location /api/ {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "https://app.example.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
add_header Access-Control-Max-Age 600;
return 204;
}
add_header Access-Control-Allow-Origin "https://app.example.com" always;
proxy_pass http://backend;
}
Tham số always rất quan trọng: nếu thiếu, Nginx sẽ bỏ header ở các phản hồi lỗi, và trình duyệt sẽ báo lỗi CORS thay vì lỗi 500 thật sự. Để tạo header cho các máy chủ khác (Vercel, Netlify, Cloudflare Workers, Apache), hãy dùng CORS Header Generator.
Cookie và thông tin xác thực
Để gửi cookie qua nhiều origin, yêu cầu cần có credentials: 'include' (fetch) và máy chủ phải trả về Access-Control-Allow-Credentials: true cùng đúng origin, tuyệt đối không dùng *. Bản thân cookie cần có SameSite=None; Secure. Khi bạn trả về origin lấy từ một danh sách, hãy gửi thêm Vary: Origin để cache không trả header của site này cho site khác.
Những việc không nên làm
- Không phản hồi lại mọi giá trị
Originkhi đã bật credentials, vì điều đó cho phép mọi website đọc dữ liệu của người dùng bạn. - Đừng “sửa” CORS bằng tiện ích trình duyệt hay proxy công khai; cách này chỉ che giấu vấn đề trên máy của bạn.
- Nếu API là của bạn và nằm cùng site, dùng reverse proxy (cùng origin) sẽ loại bỏ hoàn toàn CORS.
Tái hiện yêu cầu của trình duyệt bằng API Tester, hoặc chuyển nó thành mã bằng cURL to Code.