Sửa lỗi CORS trên AWS API Gateway: Thiếu Header 'Access-Control-Allow-Origin'

beginner☁️ AWS2026-03-31| AWS API Gateway (REST API), AWS Lambda, Các trình duyệt hiện đại (Chrome/Firefox/Safari)

Error Message

Access to fetch at 'https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/resource' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
#api-gateway#cors#lambda#aws#http

Sự ức chế khi bị chặn bởi CORS

Bạn vừa mới triển khai một API mới toanh bằng AWS API Gateway và Lambda. Nó hoạt động hoàn hảo trên Postman. Dữ liệu JSON trả về sạch sẽ, mã trạng thái là 200 OK tuyệt đẹp, và bạn đã sẵn sàng ăn mừng. Nhưng ngay khi bạn gọi API đó từ frontend React hoặc Vue tại https://example.com, trình duyệt sẽ chặn đứng bạn lại.

Console của bạn bỗng nhiên hiện đầy dòng chữ đỏ rực:

Access to fetch at 'https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/resource' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Điều này xảy ra do cơ chế Cross-Origin Resource Sharing (CORS). Hãy coi nó như một nhân viên bảo vệ trong trình duyệt. Trừ khi máy chủ của bạn cung cấp một "giấy phép" rõ ràng thông qua các HTTP headers, trình duyệt sẽ không cho phép frontend chạm vào dữ liệu.

Bước 1: Kiểm tra tab Network

Trước khi bắt đầu can thiệp vào mã nguồn, hãy mở DevTools của trình duyệt (F12) và chuyển đến tab Network. Kích hoạt lệnh gọi API một lần nữa. Thông thường, bạn sẽ thấy hai yêu cầu riêng biệt:

  • Yêu cầu OPTIONS (The Preflight): Trình duyệt về cơ bản đang hỏi máy chủ: "Tôi gửi yêu cầu POST từ domain này có an toàn không?"
  • Yêu cầu thực tế: Đây là lệnh gọi GET hoặc POST thực sự của bạn. Nó chỉ được thực hiện nếu máy chủ cho phép yêu cầu OPTIONS.

Nếu yêu cầu OPTIONS thất bại với lỗi 403 hoặc 404, hoặc thiếu header Access-Control-Allow-Origin, yêu cầu thứ hai thậm chí sẽ không bao giờ xảy ra.

Bước 2: Cấu hình CORS trong API Gateway Console

Bước đầu tiên là hướng dẫn API Gateway cách tự động phản hồi các yêu cầu Preflight đó.

  • Mở AWS Management Console và đi tới API Gateway.
  • Chọn API của bạn và điều hướng đến Resource cụ thể (ví dụ: /resource).
  • Trong menu Actions (hoặc nút CORS chuyên dụng trong các phiên bản giao diện mới hơn), chọn Enable CORS.
  • Đặt Access-Control-Allow-Origin thành '*' để kiểm tra nhanh, nhưng hãy sử dụng 'https://example.com' để đảm bảo bảo mật cho môi trường production.
  • Xác minh rằng OPTIONS đã được chọn trong danh sách các phương thức.
  • Nhấp vào Enable CORS and replace existing CORS headers.

Đừng bỏ qua việc triển khai (deployment). Đây là sai lầm số 1 mà các kỹ sư thường mắc phải. Các thay đổi trong console sẽ không có hiệu lực cho đến khi bạn nhấp vào Actions > Deploy API và chọn stage của mình (như prod hoặc dev). Nếu bạn không triển khai, API của bạn vẫn đang chạy cấu hình cũ bị lỗi.

Bước 3: Sửa các Header trong Lambda Proxy Integration

Bạn có đang sử dụng Lambda Proxy Integration không? Nếu có, các cài đặt console bạn vừa thay đổi chỉ sửa được yêu cầu OPTIONS. Chúng không tác động đến phản hồi dữ liệu thực tế từ hàm Lambda của bạn.

Trong thiết lập Proxy Integration, Lambda của bạn nắm quyền kiểm soát hoàn toàn. Nếu mã nguồn của bạn không trả về các CORS headers một cách rõ ràng trong đối tượng phản hồi, trình duyệt vẫn sẽ chặn bạn. Việc bạn đã nhấp bao nhiêu nút trong console AWS không còn quan trọng nữa.

Ví dụ: Cách sửa trong Node.js

exports.handler = async (event) => {
    return {
        statusCode: 200,
        headers: {
            "Access-Control-Allow-Origin": "https://example.com",
            "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
            "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key"
        },
        body: JSON.stringify({ message: "It works!" }),
    };
};

Ví dụ: Cách sửa trong Python

import json

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'headers': {
            'Access-Control-Allow-Origin': 'https://example.com',
            'Access-Control-Allow-Headers': 'Content-Type',
            'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
        },
        'body': json.dumps({'message': 'Success!'})
    }

Bước 4: Xử lý các trạng thái lỗi

Một cái bẫy tinh vi: điều gì xảy ra khi Lambda của bạn gặp lỗi (crash)? Nếu khối catch của bạn trả về lỗi 500 nhưng quên thêm các CORS headers, trình duyệt sẽ ẩn lỗi 500 đó đi và thay bằng thông báo "CORS Error". Điều này khiến các nhà phát triển mất công tìm kiếm vô ích trong phần cài đặt API Gateway, trong khi vấn đề thực sự nằm ở lỗi trong mã nguồn của họ.

Hãy luôn bao bọc logic của bạn trong try/catch và đảm bảo các phản hồi lỗi bao gồm cùng một header Access-Control-Allow-Origin. Nếu bạn sử dụng Cognito hoặc API Keys, bạn cũng cần kiểm tra phần "Gateway Responses" trong API Gateway để đảm bảo các lỗi 401 hoặc 403 cũng truyền đi các CORS headers.

Xác minh bằng cURL

Đừng tải lại trình duyệt liên tục để kiểm tra nữa. Hãy sử dụng curl trong terminal của bạn để xem chính xác những gì máy chủ đang gửi về:

# Kiểm tra Preflight (OPTIONS)
curl -v -X OPTIONS https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/resource \
-H "Origin: https://example.com" \
-H "Access-Control-Request-Method: POST"

# Kiểm tra dòng này trong đầu ra:
# < Access-Control-Allow-Origin: https://example.com

Danh sách kiểm tra tóm tắt

  • Postman không nói lên tất cả: Nó không thực thi CORS, vì vậy đây là một bài kiểm tra kém hiệu quả về khả năng tương thích của trình duyệt.
  • Proxy Integration là thủ công: Mã nguồn Lambda của bạn phải trả về các headers cho các phương thức GET/POST.
  • Triển khai là chìa khóa: Không triển khai, không có thay đổi. Chấm hết.
  • Lỗi thường "ngụy trang" dưới dạng CORS: Nếu Lambda thất bại, việc thiếu header sẽ che giấu mã trạng thái thực sự.

Related Error Notes