《Nginx通過CORS實現跨域》要點:
本文介紹了Nginx通過CORS實現跨域,希望對您有用。如果有疑問,可以聯系我們。
CORS是一個W3C標準,全稱是跨域資源共享(Cross-origin resource sharing).它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制.
當前幾乎所有的瀏覽器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)都可通過名為跨域資源共享(Cross-Origin Resource Sharing)的協議支持AJAX跨域調用.
Chrome,Firefox,Opera,Safari都使用的是XMLHttpRequest2對象,IE使用XDomainRequest.
簡單來說就是跨域的目標服務器要返回一系列的Headers,通過這些Headers來控制是否同意跨域.跨域資源共享(CORS)也是未來的跨域問題的標準解決方案.
CORS提供如下Headers,Request包和Response包中都有一部分.
其中最敏感的就是Access-Control-Allow-Origin這個Header, 它是W3C標準里用來檢查該跨域請求是否可以被通過.(Access Control Check).如果需要跨域,解決方法就是在資源的頭中加入Access-Control-Allow-Origin 指定你授權的域.
假設您的應用已經在example.com上了,而您想要從www.example2.com提取數據.一般情況下,如果您嘗試進行這種類型的AJAX調用,請求將會失敗,而瀏覽器將會出現源不匹配的錯誤.利用CORS后只需www.example2.com 服務端添加一個HTTP Response頭,就可以允許來自example.com的請求.
將Access-Control-Allow-Origin添加到某網站下或整個域中的單個資源
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Credentials: true (可選)
將允許任何域向您提交請求
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true (可選)
如果服務器端已啟用了CORS,那么提交跨域請求就和普通的XMLHttpRequest請求沒什么區別.例如現在example.com可以向www.example2.com提交請求.
var xhr = new XMLHttpRequest();
// xhr.withCredentials = true; //如果需要Cookie等
xhr.open('GET', 'http://www.example2.com/hello.json');
xhr.onload = function(e) {
?var data = JSON.parse(this.response);
?...
}
xhr.send();
要實現CORS跨域,服務端需要下圖中這樣一個流程
流程如下
用偽代碼表示
location /pub/(.+) {
? ?if ($http_origin ~ <允許的域(正則匹配)>) {
? ? ? ?add_header 'Access-Control-Allow-Origin' "$http_origin";
? ? ? ?add_header 'Access-Control-Allow-Credentials' "true";
? ? ? ?if ($request_method = "OPTIONS") {
? ? ? ? ? ?add_header 'Access-Control-Max-Age' 86400;
? ? ? ? ? ?add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE';
? ? ? ? ? ?add_header 'Access-Control-Allow-Headers' 'reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type';
? ? ? ? ? ?add_header 'Content-Length' 0;
? ? ? ? ? ?add_header 'Content-Type' 'text/plain, charset=utf-8';
? ? ? ? ? ?return 204;
? ? ? ?}
? ?}
? ?# 正常nginx配置
? ?......
}
在nginx.conf里找到server項,并在里面添加如下配置
location /{
add_header 'Access-Control-Allow-Origin' 'http://example.com';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
...
}
如果需要允許來自任何域的訪問,可以這樣配置
add_header Access-Control-Allow-Origin *;
注釋如下
第一條指令:授權從example.com的請求(必需)
第二條指令:當該標志為真時,響應于該請求是否可以被暴露(可選)
第三條指令:允許腳本訪問的返回頭(可選)
第四條指令:指定請求的方法,可以是GET, POST, OPTIONS, PUT, DELETE等(可選)
重啟Nginx
$ service nginx reload
測試跨域請求
$ curl -I -X OPTIONS -H "Origin: http://example.com" http://www.example2.com
成功時,響應頭是如下所示
HTTP/1.1 200 OK
Server: nginx
Access-Control-Allow-Origin: example.com
由于Access-Control-Allow-Origin參數只允許配置單個域名或者*
,當我們需要允許多個域名跨域訪問時可以用以下幾種方法來實現.
如需要允許用戶請求來自www.example.com、m.example.com、wap.example.com訪問www.example2.com域名時,返回頭Access-Control-Allow-Origin,具體配置如下
在nginx.conf里面,找到server項,并在里面添加如下配置
map $http_origin $corsHost {
? ?default 0;
? ?"~http://www.example.com" http://www.example.com;
? ?"~http://m.example.com" http://m.example.com;
? ?"~http://wap.example.com" http://wap.example.com;
}
server
{
? ?listen 80;
? ?server_name www.example2.com;
? ?root /usr/share/nginx/html;
? ?location /
? ?{
? ? ? ?add_header Access-Control-Allow-Origin $corsHost;
? ?}
}
如需要允許用戶請求來自localhost、www.example.com或m.example.com的請求訪問xxx.example2.com域名時,返回頭Access-Control-Allow-Origin,具體配置如下
在Nginx配置文件中xxx.example2.com域名的location /下配置以下內容
set $cors '';
if ($http_origin ~* 'https?://(localhost|www\.example\.com|m\.example\.com)') {
? ? ? ?set $cors 'true';
}
if ($cors = 'true') {
? ? ? ?add_header 'Access-Control-Allow-Origin' "$http_origin";
? ? ? ?add_header 'Access-Control-Allow-Credentials' 'true';
? ? ? ?add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
? ? ? ?add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Mx-ReqToken,X-Requested-With';
}
if ($request_method = 'OPTIONS') {
? ? ? ?return 204;
}
如需要允許用戶請求來自*.example.com訪問xxx.example2.com域名時,返回頭Access-Control-Allow-Origin,具體配置如下
在Nginx配置文件中xxx.example2.com域名的location /下配置以下內容
if ( $http_origin ~ http://(.*).example.com){
? ? ? ? set $allow_url $http_origin;
? ?}
? ?#CORS(Cross Orign Resource-Sharing)跨域控制配置
? ?#是否允許請求帶有驗證信息
? ?add_header Access-Control-Allow-Credentials true;
? ?#允許跨域訪問的域名,可以是一個域的列表,也可以是通配符*
? ?add_header Access-Control-Allow-Origin $allow_url;
? ?#允許腳本訪問的返回頭
? ?add_header Access-Control-Allow-Headers 'x-requested-with,content-type,Cache-Control,Pragma,Date,x-timestamp';
? ?#允許使用的請求方法,以逗號隔開
? ?add_header Access-Control-Allow-Methods 'POST,GET,OPTIONS,PUT,DELETE';
? ?#允許自定義的頭部,以逗號隔開,大小寫不敏感
? ?add_header Access-Control-Expose-Headers 'WWW-Authenticate,Server-Authorization';
? ?#P3P支持跨域cookie操作
? ?add_header P3P 'policyref="/w3c/p3p.xml", CP="NOI DSP PSAa OUR BUS IND ONL UNI COM NAV INT LOC"';
如需要允許用戶請求來自xxx1.example.com或xxx1.example1.com訪問xxx.example2.com域名時,返回頭Access-Control-Allow-Origin,具體配置如下
在Nginx配置文件中xxx.example2.com域名的location /下配置以下內容
location / {
? ?if ( $http_origin ~ .*.(example|example1).com ) {
? ?add_header Access-Control-Allow-Origin $http_origin;
? ?}
}
默認Access-Control-Allow-Origin開啟跨域請求只支持GET、HEAD、POST、OPTIONS請求,使用DELETE發起跨域請求時,瀏覽器出于安全考慮會先發起OPTIONS請求,服務器端接收到的請求方式就變成了OPTIONS,所以引起了服務器的405 Method Not Allowed.
解決方法
首先要對OPTIONS請求進行處理
if ($request_method = 'OPTIONS') {
? ?add_header Access-Control-Allow-Origin *;
? ?add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
? ?#其他頭部信息配置,省略...
? ?return 204;
}
當請求方式為OPTIONS時設置Allow的響應頭,重新處理這次請求.這樣發出請求時第一次是OPTIONS請求,第二次才是DELETE請求.
# 完整配置參考
# 將配置文件的放到對應的server {}里
add_header Access-Control-Allow-Origin *;
location / {
? ?if ($request_method = 'OPTIONS') {
? ? ? ?add_header Access-Control-Allow-Origin *;
? ? ? ?add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
? ? ? ?return 204;
? ?}
? ?index index.php;
? ?try_files $uri @rewriteapp;
}
The following Nginx configuration enables CORS, with support for preflight requests.
#
# Wide-open CORS config for nginx
#
location / {
? ? if ($request_method = 'OPTIONS') {
? ? ? ?add_header 'Access-Control-Allow-Origin' '*';
? ? ? ?add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
? ? ? ?#
? ? ? ?# Custom headers and headers various browsers *should* be OK with but aren't
? ? ? ?#
? ? ? ?add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
? ? ? ?#
? ? ? ?# Tell client that this pre-flight info is valid for 20 days
? ? ? ?#
? ? ? ?add_header 'Access-Control-Max-Age' 1728000;
? ? ? ?add_header 'Content-Type' 'text/plain charset=UTF-8';
? ? ? ?add_header 'Content-Length' 0;
? ? ? ?return 204;
? ? }
? ? if ($request_method = 'POST') {
? ? ? ?add_header 'Access-Control-Allow-Origin' '*';
? ? ? ?add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
? ? ? ?add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
? ? }
? ? if ($request_method = 'GET') {
? ? ? ?add_header 'Access-Control-Allow-Origin' '*';
? ? ? ?add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
? ? ? ?add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
? ? }
}
if ($request_method = 'OPTIONS') { ?
? ?add_header 'Access-Control-Allow-Origin' 'https://docs.domain.com'; ?
? ?add_header 'Access-Control-Allow-Credentials' 'true'; ?
? ?add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS'; ?
? ?add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,token'; ?
? ?return 204;
}
if ($request_method = 'POST') { ?
? ?add_header 'Access-Control-Allow-Origin' 'https://docs.domain.com'; ?
? ?add_header 'Access-Control-Allow-Credentials' 'true'; ?
? ?add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS'; ?
? ?add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,token'; ?
} ?
if ($request_method = 'GET') { ?
? ?add_header 'Access-Control-Allow-Origin' 'https://docs.domain.com'; ?
? ?add_header 'Access-Control-Allow-Credentials' 'true'; ?
? ?add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS'; ?
? ?add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,token'; ?
}
在httpd配置或.htaccess文件中添加如下語句
SetEnvIf Origin "^(.*\.example\.com)$" ORIGIN_SUB_DOMAIN=$1 ?
Header set Access-Control-Allow-Origin "%{ORIGIN_SUB_DOMAIN}e" env=ORIGIN_SUB_DOMAIN
通過在服務端設置Access-Control-Allow-Origin響應頭
<?php
header("Access-Control-Allow-Origin: *");
?>
<?php
header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
?>
由于瀏覽器實現只支持了單個origin、*、null,如果要配置多個訪問源,可以在代碼中處理如下
<?php
$allowed_origins ? = array( ?
? ? ? ? ? ? ? ? ? ? ? ? ? ?"http://www.example.com" ? , ?
? ? ? ? ? ? ? ? ? ? ? ? ? ?"http://app.example.com" ?, ?
? ? ? ? ? ? ? ? ? ? ? ? ? ?"http://cms.example.com" ?, ?
? ? ? ? ? ? ? ? ? ? ? ? ?); ?
if (in_array($_SERVER['HTTP_ORIGIN'], $allowed_origins)){ ? ?
? ?@header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']); ?
}
?>
<meta http-equiv="Access-Control-Allow-Origin" content="*">
http://t.cn/RZEYPmD
http://t.cn/RhcAN2d
http://to-u.xyz/2016/06/30/nginx-cors/
http://coderq.github.io/2016/05/13/cross-domain/
作者:Mike
文章出處:運維之美
Nginx與httpd對比
nginx 利用 proxy 反向代理解決 Ajax 跨域訪問,瀏覽器不顯示跳轉后的鏈接