開發與維運

CDN – 跨域失敗排查

作者:張醫博

背景

image.png

某個客戶在阿里雲 CDN 配置了加速域名 al.p2.com ,客戶自己的主站域名 www.a.com 加載 al.p2.com 下的資源出現跨域的報錯;

瞭解跨域

跨域資源共享(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不同源服務器上的指定的資源。當一個資源從與該資源本身所在的服務器不同的域、協議或端口請求一個資源時,資源會發起一個跨域 HTTP 請求。
比如,站點 http://domain-a.com 的某 HTML 頁面通過 的 src 請求 http://domain-b.com/image.jpg。網絡上的許多頁面都會加載來自不同域的CSS樣式表,圖像和腳本等資源。
出於安全原因,瀏覽器限制從腳本內發起的跨源HTTP請求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 這意味著使用這些API的Web應用程序只能從加載應用程序的同一個域請求HTTP資源,除非響應報文包含了正確CORS響應頭。
跨域詳細介紹

配置跨域

前面瞭解跨域頭,那麼允許哪些域名、請求方式、header 能跨域,需要在 CDN 上配置跨域的相關頭信息,具體配置如下;

image.png

  • Access-Control-Allow-Origin
    (是跨域時必須配置的,其他的可選擇性配置)
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Methods
  • Access-Control-Max-Age
  • Access-Control-Expose-Headers

效果對比

配置前,沒有跨域頭只能正常的響應 200 ,但沒有跨域的響應頭;

 curl -vo /dev/null http://www.zhangyb.mobi/1.txt -H "Origin: http://wsa.com"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 101.37.183.169...
* TCP_NODELAY set
* Connected to www.zhangyb.mobi (101.37.183.169) port 80 (#0)
> GET /2.txt HTTP/1.1
> Host: www.zhangyb.mobi
> User-Agent: curl/7.54.0
> Accept: */*
> Origin: http://wsa.com
> 
< HTTP/1.1 200 OK
< Server: Tengine
< Content-Type: text/plain
< Content-Length: 0
< Connection: keep-alive
< Date: Sun, 01 Sep 2019 04:50:37 GMT
< x-oss-request-id: 5D6B4E1D96CC86879C2DF8DA
< Accept-Ranges: bytes
< ETag: "D41D8CD98F00B204E9800998ECF8427E"
< Last-Modified: Mon, 29 Jul 2019 11:23:19 GMT
< x-oss-object-type: Normal
< x-oss-hash-crc64ecma: 0
< x-oss-storage-class: Standard
< Cache-Control: public, max-age= 200
< Content-MD5: 1B2M2Y8AsgTpgAmY7PhCfg==
< Ali-Swift-Global-Savetime: 1567313437
< Via: cache15.l2em21-1[54,200-0,M], cache18.l2em21-1[58,0], cache16.cn1576[0,200-0,H], cache5.cn1576[1,0]
< Age: 22
< X-Cache: HIT TCP_MEM_HIT dirn:-2:-2
< X-Swift-SaveTime: Sun, 01 Sep 2019 04:50:37 GMT
< X-Swift-CacheTime: 200
< Timing-Allow-Origin: *
< EagleId: 6525b79915673134596861073e

配置後,既響應了 200 ,又反饋了跨域頭;

curl -vo /dev/null http://www.zhangyb.mobi/1.txt -H "Origin: http://wsa.com"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 101.37.183.147...
* TCP_NODELAY set
* Connected to www.zhangyb.mobi (101.37.183.147) port 80 (#0)
> GET /1.txt HTTP/1.1
> Host: www.zhangyb.mobi
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: Tengine
< Content-Type: text/plain
< Content-Length: 158
< Connection: keep-alive
< Date: Sun, 01 Sep 2019 04:09:42 GMT
< x-oss-request-id: 5D6B4486B3B1C7F5CA2702FE
< Accept-Ranges: bytes
< ETag: "2C87D6AFF73B013CF6480719C1F940CB"
< Last-Modified: Tue, 30 Jul 2019 11:40:52 GMT
< x-oss-object-type: Normal
< x-oss-hash-crc64ecma: 12710893214583825356
< x-oss-storage-class: Standard
< Cache-Control: public, max-age= 200
< Access-Control-Allow-Origin: http://*.taoboa.com
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: HEAD, DELETE, POST, GET, PUT
< Access-Control-Max-Age: 0
< Content-MD5: LIfWr/c7ATz2SAcZwflAyw==
< x-oss-server-time: 43
< Via: cache20.l2em21-1[39,304-0,H], cache4.l2em21-1[94,0], cache2.cn1576[100,200-0,H], cache17.cn1576[116,0]
< Ali-Swift-Global-Savetime: 1566821302
< Age: 0
< X-Cache: HIT TCP_REFRESH_HIT dirn:3:1032102356
< X-Swift-SaveTime: Sun, 01 Sep 2019 04:09:42 GMT
< X-Swift-CacheTime: 200
< Timing-Allow-Origin: *
< EagleId: 6525b7a515673109828126775e
< 
{ [158 bytes data]
100   158  100   158    0     0    367      0 --:--:-- --:--:-- --:--:--   367
* Connection #0 to host www.zhangyb.mobi left intact

特殊情況

  • 有時候會遇到 CDN 沒有配置跨域頭,但是測試發現有跨域頭,這種情況是不是也算配置了跨域頭呢?
    這種情況先固定原站測試一下,看原站是否正確的配置了跨域頭,如下面測試是原站配置了跨域頭,當原站配置的跨域頭並且返回給 CDN 情況下,CDN 就會緩存下來原站的跨域響應頭,可以圖2 測試;
curl -vo /dev/null http://li.oss-cn-hangzhou.aliyuncs.com/2.txt -H "Origin: http://wsa.com" 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 118.31.219.224...
* TCP_NODELAY set
* Connected to liupeng72.oss-cn-hangzhou.aliyuncs.com (118.31.219.224) port 80 (#0)
> GET /2.txt HTTP/1.1
> Host: li.oss-cn-hangzhou.aliyuncs.com
> User-Agent: curl/7.54.0
> Accept: */*
> Origin: http://wsa.com
> 
< HTTP/1.1 200 OK
< Server: AliyunOSS
< Date: Sun, 01 Sep 2019 05:13:48 GMT
< Content-Type: text/plain
< Content-Length: 0
< Connection: keep-alive
< x-oss-request-id: 5D6B538CDC655D951D973ED5
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET
< Access-Control-Max-Age: 0
< Accept-Ranges: bytes
< ETag: "D41D8CD98F00B204E9800998ECF8427E"
< Last-Modified: Mon, 29 Jul 2019 11:23:19 GMT
< x-oss-object-type: Normal
< x-oss-hash-crc64ecma: 0
< x-oss-storage-class: Standard
< Cache-Control: public, max-age= 200
< Content-MD5: 1B2M2Y8AsgTpgAmY7PhCfg==

即使訪問 CDN 不加跨域請求頭,也可以看到請求 CDN 結果反饋的跨域頭,但這個頭並不是在 CDN 上配置的,而是緩存的原站的響應頭給客戶端的,且一但文件緩存過期,跨域頭就失效,需要回源重新獲取,假設原站去掉了緩存頭,跨域的請求就會失敗,保險起見需要在 CDN 配置跨域頭;

圖二測試

curl -vo /dev/null http://www.zhangyb.mobi/2.txt 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 101.37.183.143...
* TCP_NODELAY set
* Connected to www.zhangyb.mobi (101.37.183.143) port 80 (#0)
> GET /2.txt HTTP/1.1
> Host: www.zhangyb.mobi
> User-Agent: curl/7.54.0
> Accept: */*
> Origin: http://wsa.com
> 
< HTTP/1.1 200 OK
< Server: Tengine
< Content-Type: text/plain
< Content-Length: 0
< Connection: keep-alive
< Date: Sun, 01 Sep 2019 05:14:46 GMT
< x-oss-request-id: 5D6B53C6880904BCDAB5C3DB
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET
< Access-Control-Max-Age: 0
< Accept-Ranges: bytes
< ETag: "D41D8CD98F00B204E9800998ECF8427E"
< Last-Modified: Mon, 29 Jul 2019 11:23:19 GMT
< x-oss-object-type: Normal
< x-oss-hash-crc64ecma: 0
< x-oss-storage-class: Standard
< Cache-Control: public, max-age= 200
< Content-MD5: 1B2M2Y8AsgTpgAmY7PhCfg==
< Via: cache15.l2em21-1[31,304-0,H], cache22.l2em21-1[60,0], cache16.cn1576[66,200-0,H], cache4.cn1576[68,0]
< Ali-Swift-Global-Savetime: 1567313437
< Age: 0
< X-Cache: HIT TCP_REFRESH_HIT dirn:-2:-2
< X-Swift-SaveTime: Sun, 01 Sep 2019 05:14:46 GMT
< X-Swift-CacheTime: 200
< Timing-Allow-Origin: *
< EagleId: 6525b79815673148867943641e

Nginx 配置跨域

原站如果用的 Nginx 做的 webserver 或者緩存服務,如果增加跨域配置呢?可以 vi /etc/nginx/nginx.conf,在對應的 location 級別或者 server 級別加入如下配置塊即可;

http {
###start####
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
###end ###
}

Leave a Reply

Your email address will not be published. Required fields are marked *