Cross-Origin Resource Sharing (CORS) 跨來源資源共用錯誤解決方法

Fix the CORS Error

最近Y cheung 遇到了兩個典型CORS error問題,XMLHttpRequest 和 Fetch 請求了跨來源資源被瀏覽器阻擋,記錄一下解決方法。

什麼是Cross-Origin Resource Sharing (CORS) 跨來源資源共用

Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources. CORS also relies on a mechanism by which browsers make a "preflight" request to the server hosting the cross-origin resource, in order to check that the server will permit the actual request. In that preflight, the browser sends headers that indicate the HTTP method and headers that will be used in the actual request.

簡單來說,就是當你的站點對別的站點發起資源請求的時候,這個http請求就叫做跨來源HTTP請求(cross-origin HTTP request)。網域(domain)、通訊協定(protocol)或通訊埠(port)不同都視為別的站點。

基於安全考量,你的跨來源資源請求會受到限制。XMLHttpRequest 請求和 Fetch 請求默認遵守同源政策(same-origin policy),也就是Y cheung最近遇到的問題所在。

網頁上一般會遇到CORS錯誤的情況

  • XMLHttpRequest 請求
  • Fetch 請求
  • 加載 Web Fonts
  • 加載 WebGL textures
  • 在canvas中使用drawImage() 圖片或視頻幀
  • 使用了圖片的 CSS shape

了解更多有關於CORS的信息可以參看《MDN Web Docs:Cross-Origin Resource Sharing (CORS)

解決AWS S3上的svg圖片因CORS Error 無法顯示問題

網站圖片包括svg圖片都放在AWS S3上,就SVG圖片無法顯示,檢查console找到以下錯誤信息:

CORS error log
Access to XMLHttpRequest at 'https://s3.ap-southeast-1.amazonaws.com/public/icons/icon_select_map.svg' from 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is prevent on the request resource.

此CORS policy error 觸發了XMLHttpRequest同源政策限制,解決辦法就是在AWS S3上正確設置CORS,參看《AWS S3使用者指南:設定跨來源資源分享 (CORS)》

解決React fetch 自定义header 请求 CORS Error問題

前端 React 用 fetch 發起請求:

const headers = {
  'Content-Type': 'application/json',
  Origin: 'http://google.com',
  Accept: '*/*',
  'Sec-Fetch-Dest': 'empty',
  'Sec-Fetch-Mode': 'cors',
  'Sec-Fetch-Site': 'same-origin',
  mode: 'cors',
};

export const getSummary = params => {
  const url = new URL(`${API_URL}`);
  const bodyParam = fiterout(JSON.stringify(params));
  return fetch(url, {
    method: 'POST',
    headers,
    body: bodyParam,
  }).then(r => r.json());
};

由于自定義的請求頭部 header 觸發了 fetch 同源政策限制(Content-Type、Accept等):

CORS error log
Access to fetch at 'http://localhost' from origin 'http://localhost:3006' has been blocked by CORS policy: Request header field mode is not allowed by Access-Control-Allow-Headers in preflight response.

Y Cheung 的解決辦法是在接收請求的endpoint上加上相應的header,例如:

<?php
if ( isset( $_SERVER['HTTP_ORIGIN'] ) ) {
	header( "Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}" );
	header( 'Access-Control-Allow-Credentials: true' );
}
if ( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ) {
	if ( isset( $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'] ) ) {
		header( "Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS" );
	}
	if ( isset( $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'] ) ) {
		header( "Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}" );
	}
}
Y Cheung
Blogger, Programer & Traveler.
Shanghai