Sửa lỗi 'Expression.Error: Access to the resource is forbidden' trong Power Query Excel (403)

intermediate📊 Microsoft Excel2026-06-02| Microsoft Excel 2016/2019/2021/365, Power Query (Get & Transform Data), Windows 10/11

Error Message

DataSource.Error: Web.Contents failed to get contents from '...' (403): Forbidden. Expression.Error: Access to the resource is forbidden
#power-query#excel#403-forbidden#web-api

Chuyện gì đã xảy ra

Tôi có một Power Query đang kéo dữ liệu từ một REST API. Đã test endpoint trong Postman — 200 OK, dữ liệu trả về hoàn hảo. Nhưng Excel cứ liên tục báo lỗi này:

DataSource.Error: Web.Contents failed to get contents from 'https://api.example.com/data' (403): Forbidden.
Expression.Error: Access to the resource is forbidden

Endpoint không hề bị down. API key vẫn hợp lệ. Postman chạy được. Power Query thì không chịu.

Sau khi đào sâu vào, tôi tìm ra ba nguyên nhân gốc rễ hoàn toàn khác nhau — nhưng tất cả đều sinh ra cùng một lỗi 403 — và thứ tự loại trừ chúng rất quan trọng.

Nguyên nhân gốc rễ (theo thứ tự khả năng xảy ra)

  • Header xác thực không được gửi đi — Power Query âm thầm bỏ qua các custom header trong một số cấu hình
  • Formula Firewall chặn các truy vấn cross-source — cài đặt privacy của Power Query coi việc kết hợp hai nguồn dữ liệu là vi phạm bảo mật
  • Thông tin xác thực được lưu sai ở tầng data source — Excel cache thông tin xác thực sai và cứ tiếp tục gửi chúng
  • API phía server giới hạn IP hoặc referrer — ít gặp hơn, nhưng có thể xảy ra nếu API key bị giới hạn theo origin cụ thể

Bước 1 — Xác nhận header có thực sự được gửi đi không

Chín trên mười trường hợp, đây chính là thủ phạm. Bạn truyền API key vào header của Web.Contents, nhưng Power Query âm thầm bỏ qua chúng khi nó fallback về hệ thống xác thực tích hợp sẵn.

Mở Advanced Editor và kiểm tra M code của bạn. Pattern này trông có vẻ đúng nhưng thực tế lại không hoạt động:

// Headers có thể bị bỏ qua mà không có bất kỳ cảnh báo nào
let
    Source = Web.Contents(
        "https://api.example.com/data",
        [
            Headers = [
                #"Authorization" = "Bearer YOUR_TOKEN",
                #"Content-Type" = "application/json"
            ]
        ]
    ),
    Result = Json.Document(Source)
in
    Result

Hãy tách URL thành base URL và RelativePath. Cách này ngăn Power Query cache toàn bộ URL kèm thông tin xác thực bên trong — đó chính là thứ làm hệ thống auth bị nhầm lẫn:

let
    BaseUrl = "https://api.example.com",
    Source = Web.Contents(
        BaseUrl,
        [
            RelativePath = "data",
            Headers = [
                #"Authorization" = "Bearer YOUR_TOKEN",
                #"x-api-key" = "YOUR_API_KEY"
            ],
            ManualStatusHandling = {403, 401}
        ]
    ),
    ResponseCode = Value.Metadata(Source)[Response.Status],
    Result = if ResponseCode = 200 then Json.Document(Source)
             else error "HTTP " & Text.From(ResponseCode) & " — kiểm tra lại header xác thực"
in
    Result

ManualStatusHandling là phần bổ sung quan trọng ở đây. Thay vì Power Query chuyển lỗi 403 thành một crash mơ hồ, bạn sẽ nhận được HTTP status code thực tế — thứ bạn có thể kiểm tra và xử lý.

Bước 2 — Xóa và đặt lại thông tin xác thực data source

Excel cache thông tin xác thực theo từng URL data source. Một lần kết nối ẩn danh — hoặc với token sai — và nó cứ tiếp tục gửi những thông tin xác thực cũ đó mỗi lần refresh, dù bạn đã sửa M code rồi.

  • Trong Excel, vào Data → Get Data → Data Source Settings
  • Tìm URL trong danh sách (thường chỉ là base domain, ví dụ api.example.com)
  • Nhấn Clear Permissions — không phải Edit, hãy xóa hoàn toàn
  • Đóng và mở lại query
  • Khi được hỏi, chọn đúng loại xác thực: Anonymous nếu API key của bạn truyền qua header trong M code, hoặc Web API nếu API dùng xác thực bằng key theo chuẩn

Điều này gây nhầm lẫn cho rất nhiều người. Power Query chạy hai tầng xác thực riêng biệt: header trong M code và credential store. Khi chúng xung đột, credential store thắng. Các custom header của bạn có thể chưa bao giờ đến được server.

Bước 3 — Xử lý Formula Firewall (Privacy Levels)

Kết hợp web API của bạn với bất kỳ nguồn dữ liệu nào khác — một bảng Excel cục bộ, danh sách SharePoint, một database — sẽ kích hoạt Formula Firewall. Power Query coi sự kết hợp này là rủi ro bảo mật và chặn nó, đôi khi tạo ra chính xác cùng một lỗi 403.

Những lúc khác bạn sẽ thấy thông báo rõ ràng hơn này:

Formula.Firewall: Query 'Query1' (step 'Source') references other queries or steps,
so it may not directly access a data source. Please rebuild this data combination.

Đặt privacy level một cách tường minh để khắc phục:

  • Vào File → Options → Trust Center → Trust Center Settings → Privacy
  • Hoặc trong query editor: File → Options and Settings → Query Options → Privacy
  • Đặt privacy level của web source thành Public (hoặc Organizational trên mạng công ty)
  • Đặt nguồn file cục bộ thành Private hoặc Organizational

Trong quá trình debug, bạn có thể bỏ qua bước này hoàn toàn:

  • Query Options → Privacy → Always ignore Privacy Level settings

Đừng để tùy chọn đó bật trong môi trường production nếu bạn đang kết hợp các nguồn nhạy cảm. Nhưng đây là cách nhanh nhất để xác nhận Firewall có thực sự là vấn đề hay không, trước khi bạn mất công tinh chỉnh privacy level.

Bước 4 — Dùng query parameter thay vì ghép chuỗi URL

Một số API từ chối API key được nhúng trực tiếp vào chuỗi URL. Chúng yêu cầu key phải là một query parameter đúng nghĩa — và Power Query xử lý hai cách viết này rất khác nhau bên dưới. Hãy dùng tùy chọn Query:

let
    Source = Web.Contents(
        "https://api.example.com",
        [
            RelativePath = "v1/data",
            Query = [
                api_key = "YOUR_API_KEY",
                format = "json"
            ],
            Headers = [
                #"Accept" = "application/json"
            ]
        ]
    ),
    Result = Json.Document(Source)
in
    Result

Thêm vào đó: Power Query coi đây là nguồn tĩnh thay vì nguồn động, nên bạn sẽ không bị hỏi thêm về bảo mật mỗi lần scheduled refresh.

Bước 5 — Kiểm tra giới hạn IP phía API

Vẫn bị 403? Vấn đề có thể không nằm trong Excel chút nào. Các API doanh nghiệp thường giới hạn truy cập theo địa chỉ IP. Khi Power Query refresh qua Power BI Service hoặc SharePoint Online, request xuất phát từ Azure datacenter của Microsoft — không phải từ laptop của bạn. API nhìn thấy một IP lạ và chặn nó.

Hãy test bằng cách chạy cùng query từ một mạng khác, hoặc nhờ nhà cung cấp API kéo access log của họ để xem IP nào bị từ chối. Nếu điều đó xác nhận vấn đề, bạn cần hoặc là whitelist các dải IP datacenter của Microsoft, hoặc định tuyến các lần refresh qua một on-premises data gateway gửi request từ IP được cho phép.

Xác minh kết quả

Sau khi áp dụng một cách sửa, hãy đảm bảo nó thực sự có hiệu lực:

  • Tạm thời thêm ManualStatusHandling = {403} và hiển thị Response.Status — bạn sẽ thấy 200, không phải 403
  • Xóa phần kiểm tra status đi và thực hiện full refresh — không còn lỗi
  • Đối với scheduled refresh trong Power BI hoặc SharePoint, kích hoạt manual refresh từ service và kiểm tra lịch sử refresh
  • Mở lại Data Source Settings và xác nhận đúng loại thông tin xác thực đã được lưu cho URL của bạn

Tóm tắt nhanh

  • Header bị bỏ qua âm thầm → dùng RelativePath + ManualStatusHandling
  • Cache thông tin xác thực sai → Data Source Settings → Clear Permissions
  • Formula Firewall chặn → đặt Privacy Level hoặc tạm thời tắt Firewall
  • API key nhúng trong chuỗi URL → dùng tùy chọn Query = [...] thay thế
  • Giới hạn IP → whitelist IP datacenter Microsoft hoặc dùng on-premises gateway

Bài học rút ra

Đây là điều khiến lỗi này trở nên bực bội: API không hề bị hỏng. Postman chứng minh điều đó. Lỗi 403 hoàn toàn nằm trong cách Power Query xử lý auth, privacy setting và credential caching — không cái nào trong số đó hiển thị trong M code.

Xử lý theo thứ tự — header trước, rồi đến credential cache, rồi đến Formula Firewall — sẽ tìm ra nguyên nhân trong hầu hết mọi trường hợp. Và ManualStatusHandling thực sự bị dùng quá ít; đó là sự khác biệt giữa "Access to the resource is forbidden" và một HTTP status code thực tế mà bạn có thể chẩn đoán được.

Related Error Notes