Has been blocked by CORS policy: Nguyên nhân và cách sửa

Khi tích hợp Zalo API, hệ thống đăng nhập hoặc một API bên ngoài vào website, lập trình viên có thể gặp thông báo Has been blocked by CORS policy trong Console của trình duyệt. Request đã được tạo từ JavaScript nhưng trình duyệt không cho ứng dụng đọc response vì máy chủ đích chưa chấp nhận nguồn gọi hiện tại.

Đây không phải lỗi riêng của Zalo và cũng không chỉ là lỗi JavaScript. CORS là cơ chế bảo mật của trình duyệt nhằm hạn chế một website tự ý truy cập dữ liệu từ nguồn khác. Muốn xử lý đúng, cần xác định frontend chạy ở đâu, API thuộc domain nào, server trả về header gì và request có phát sinh preflight hay không.

Has been blocked by CORS policy là lỗi gì?

Thông báo Has been blocked by CORS policy xuất hiện khi trình duyệt phát hiện request khác nguồn nhưng response không đáp ứng quy tắc Cross-Origin Resource Sharing. Website chạy tại một origin, còn API được gọi nằm tại origin khác. Nếu máy chủ API không cho phép origin của website, trình duyệt sẽ chặn việc truy cập response.

Một origin được xác định bởi giao thức, tên miền và cổng. Chỉ cần một thành phần khác nhau thì request có thể bị xem là cross-origin. Ví dụ:

  • https://app.example.comhttps://api.example.com
  • http://example.comhttps://example.com
  • https://example.com:443https://example.com:8443

CORS không phải lúc nào cũng ngăn request rời khỏi trình duyệt. API có thể vẫn nhận request và ghi log, nhưng trình duyệt không cho frontend đọc kết quả. Vì vậy, thấy server xử lý thành công chưa có nghĩa cấu hình CORS đã đúng.

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

Has been blocked by CORS policy - Has been blocked by CORS policy là lỗi gì?
Has been blocked by CORS policy – Has been blocked by CORS policy là lỗi gì?

Khi JavaScript dùng fetch, XMLHttpRequest hoặc thư viện HTTP để gọi API khác origin, trình duyệt kiểm tra response có Access-Control-Allow-Origin phù hợp hay không. Nếu header thiếu, sai hoặc không khớp origin hiện tại, response sẽ bị chặn.

Với request dùng PUT, PATCH, DELETE, header tùy chỉnh hoặc Content-Type: application/json, trình duyệt thường gửi một request OPTIONS trước. Đây là preflight, dùng để hỏi server xem method và header dự kiến có được phép hay không.

Vì sao gặp lỗi Has been blocked by CORS policy?

Nguyên nhân phổ biến nhất là API chưa cấu hình CORS hoặc chỉ cho phép một số domain. Trong quá trình tích hợp Zalo API, lỗi còn xuất hiện khi frontend gọi trực tiếp endpoint cần bảo mật, trong khi endpoint đó được thiết kế cho kết nối server-to-server.

Server thiếu Access-Control-Allow-Origin

Nếu response không có Access-Control-Allow-Origin, trình duyệt không biết server có chấp nhận website hiện tại hay không. Header hợp lệ có thể là:

Access-Control-Allow-Origin: https://app.example.com

Với tài nguyên công khai, không gửi cookie hoặc thông tin xác thực, server có thể dùng:

Access-Control-Allow-Origin: *

Không nên dùng dấu * cho mọi API chỉ để xóa lỗi. Cấu hình này mở quyền truy cập quá rộng và không dùng được với request có credential.

Preflight OPTIONS không được xử lý

Has been blocked by CORS policy - Server thiếu Access-Control-Allow-Origin
Has been blocked by CORS policy – Server thiếu Access-Control-Allow-Origin

Request chính có thể dùng POST, nhưng lỗi thực tế nằm ở request OPTIONS. Nếu web server, reverse proxy hoặc firewall trả về 404, 405 hay 500 cho OPTIONS, trình duyệt sẽ dừng trước khi gửi request chính.

Server cần phản hồi preflight bằng các header phù hợp:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization

Response OPTIONS có thể trả về mã 204 và không cần body. Header phải khớp với method và các header frontend chuẩn bị sử dụng.

Origin không khớp cấu hình

Server cho phép https://example.com nhưng ứng dụng chạy tại https://www.example.com vẫn có thể gặp Has been blocked by CORS policy. Hai địa chỉ này là hai origin khác nhau.

Tương tự, http://localhost:3000 không giống http://127.0.0.1:3000; cổng 3000 cũng khác 5173. Khi kiểm tra, hãy lấy chính xác giá trị header Origin mà trình duyệt gửi lên.

Credential và wildcard xung đột

Khi frontend gửi cookie, session hoặc đặt credentials: "include", server phải trả về origin cụ thể, không thể dùng:

Access-Control-Allow-Origin: *

Server cũng phải thêm:

Access-Control-Allow-Credentials: true

Nếu thiếu header này hoặc vẫn dùng wildcard, trình duyệt sẽ chặn response.

Redirect, proxy hoặc CDN làm mất header

CORS có thể đúng trong ứng dụng nhưng sai sau khi đi qua Nginx, Apache, CDN hoặc API Gateway. Redirect từ HTTP sang HTTPS, domain cũ sang domain mới hoặc endpoint không có dấu gạch chéo sang endpoint có dấu gạch chéo cũng có thể tạo response trung gian thiếu header.

Hãy kiểm tra các mã 301, 302, 307, 308, 401, 403 và 500 trong tab Network. Header CORS nên xuất hiện cả trên response lỗi nếu frontend cần đọc nội dung lỗi.

Cách kiểm tra lỗi CORS chính xác

Has been blocked by CORS policy - Redirect, proxy hoặc CDN làm mất header
Has been blocked by CORS policy – Redirect, proxy hoặc CDN làm mất header

Mở Developer Tools, chuyển sang tab Network và lọc theo endpoint. Kiểm tra cả request chính lẫn request OPTIONS. Console chỉ báo lỗi tổng quát, còn Network cho thấy status code, request header, response header và redirect.

Các điểm cần đối chiếu gồm:

  • Giá trị Origin trình duyệt gửi lên
  • Access-Control-Allow-Origin trong response
  • Method có nằm trong Access-Control-Allow-Methods
  • Authorization, Content-Type hoặc header tùy chỉnh có được cho phép
  • Request có gửi cookie hoặc credential
  • Preflight trả về status code nào
  • Response có bị chuyển sang domain khác

Có thể dùng curl để mô phỏng preflight:

curl -i -X OPTIONS “https://api.example.com/resource” -H “Origin: https://app.example.com” -H “Access-Control-Request-Method: POST” -H “Access-Control-Request-Headers: Content-Type, Authorization”

Nếu response thiếu header cần thiết, vấn đề nằm ở server hoặc proxy. Nếu curl đúng nhưng trình duyệt vẫn lỗi, hãy kiểm tra cache, service worker, redirect và origin thực tế.

Cách fix bằng header phía server

Giải pháp bền vững nhất là cấu hình CORS tại server sở hữu API. Frontend không thể tự thêm Access-Control-Allow-Origin vào response của máy chủ khác. Thêm header này vào request cũng không có tác dụng vì trình duyệt kiểm tra header response.

Ví dụ với Node.js và Express

Có thể dùng middleware cors và chỉ cho phép các domain tin cậy:

import express from “express”;
import cors from “cors”;

const app = express();
const allowedOrigins = [
“https://app.example.com”,
“http://localhost:5173”
];

app.use(cors({
origin(origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
return callback(null, true);
}
return callback(new Error(“Origin không được phép”));
},
methods: [“GET”, “POST”, “PUT”, “DELETE”, “OPTIONS”],
allowedHeaders: [“Content-Type”, “Authorization”],
credentials: true
}));

app.use(express.json());

Danh sách origin nên lấy từ biến môi trường để tách development, staging và production. Không nên phản chiếu mọi giá trị Origin về response khi chưa kiểm tra whitelist.

Ví dụ với PHP

PHP có thể kiểm tra origin rồi trả về header tương ứng:

try {
const response = await fetch(“https://api.example.com/profile”, {
method: “POST”,
headers: {
“Content-Type”: “application/json”,
“Authorization”: `Bearer ${process.env.ACCESS_TOKEN}`
},
body: JSON.stringify(req.body)
});

const data = await response.json();
res.status(response.status).json(data);
} catch (error) {
res.status(502).json({ message: “Không thể kết nối API đích” });
}
});

Proxy vừa khắc phục **Has been blocked by CORS policy**, vừa hỗ trợ kiểm soát rate limit, log và lỗi. Tuy nhiên, proxy phải xác thực người dùng, giới hạn endpoint được gọi và không được biến thành open proxy nhận URL tùy ý.

Tiện ích tắt CORS hoặc proxy công cộng chỉ nên dùng để chẩn đoán tạm thời. Chúng không phải giải pháp production và có thể làm lộ token hoặc dữ liệu người dùng.

## Lưu ý bảo mật khi cấu hình CORS cho Zalo API

CORS không thay thế xác thực và phân quyền. API cho phép origin cụ thể vẫn phải kiểm tra access token, quyền người dùng và dữ liệu đầu vào. Cấu hình quá rộng có thể tạo điều kiện để website lạ gửi request từ trình duyệt người dùng.

Khi tích hợp Zalo API hoặc dữ liệu khách hàng, nên:

– Chỉ cho phép đúng domain frontend
– Tách whitelist cho local, staging và production
– Không đặt app secret hoặc token dài hạn trong frontend
– Không dùng `Access-Control-Allow-Origin: *` với dữ liệu riêng tư
– Chỉ cho phép method và header thật sự cần
– Xác thực request và kiểm tra quyền tại backend
– Thiết lập rate limit, log và cảnh báo bất thường
– Trả header CORS trên cả response thành công và thất bại

Cần phân biệt lỗi CORS với lỗi API. Endpoint có thể trả 401 vì token hết hạn, nhưng response 401 thiếu header CORS khiến trình duyệt chỉ hiển thị lỗi CORS. Hãy bảo đảm response 4xx và 5xx cũng có header phù hợp, sau đó xử lý vấn đề xác thực riêng.

## Quy trình xử lý Has been blocked by CORS policy hiệu quả

Khi gặp **Has been blocked by CORS policy**, không nên thử ngẫu nhiên nhiều header hoặc tắt bảo mật của trình duyệt. Trước hết, xác định request nên gọi từ frontend hay phải chuyển sang backend. Tiếp theo, kiểm tra preflight, origin, method, credential, redirect và từng lớp proxy.

Nếu sở hữu API, hãy cấu hình whitelist tại server, xử lý `OPTIONS`, cho phép đúng method và header, đồng thời thêm `Vary: Origin` khi hỗ trợ nhiều nguồn. Nếu không sở hữu API, hãy dùng backend proxy an toàn thay vì cố vượt CORS từ trình duyệt. khám phá thêm tại zalo chat web.

Xử lý đúng lỗi này không chỉ giúp request hoạt động mà còn giữ ranh giới bảo mật giữa frontend, backend và API bên ngoài. Với ứng dụng tích hợp Zalo API, gọi API nhạy cảm từ server thường an toàn, dễ kiểm soát và ổn định hơn việc đưa toàn bộ logic kết nối vào trình duyệt.

Rate this post

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *