Access-Control-Allow-Origin多个源域?

时间:2009-10-31 03:27:44

标签: .htaccess http cors xmlhttprequest cross-domain

有没有办法允许使用Access-Control-Allow-Origin标题的多个跨域?

我知道*,但它太开放了。我真的想只允许一些域名。

举个例子,像这样:

Access-Control-Allow-Origin: http://domain1.example, http://domain2.example

我已经尝试过上面的代码,但它似乎不适用于Firefox。

是否可以指定多个域,或者我只坚持一个?

33 个答案:

答案 0 :(得分:796)

听起来像推荐的方法是让服务器从客户端读取Origin头,将其与您想要允许的域列表进行比较,如果匹配,则回显{{1 }}作为响应中的Origin标头返回客户端。

使用Access-Control-Allow-Origin,你可以这样做:

.htaccess

答案 1 :(得分:194)

我在PHP中使用的另一个解决方案:

$http_origin = $_SERVER['HTTP_ORIGIN'];

if ($http_origin == "http://www.domain1.com" || $http_origin == "http://www.domain2.com" || $http_origin == "http://www.domain3.com")
{  
    header("Access-Control-Allow-Origin: $http_origin");
}

答案 2 :(得分:103)

这对我有用:

SetEnvIf Origin "^http(s)?://(.+\.)?(domain\.example|domain2\.example)$" origin_is=$0 
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is

当放入.htaccess时,它肯定会有效。

答案 3 :(得分:86)

我遇到了与woff-fonts相同的问题,多个子域必须具有访问权限。为了允许子域名,我在httpd.conf中添加了类似的内容:

SetEnvIf Origin "^(.*\.example\.com)$" ORIGIN_SUB_DOMAIN=$1
<FilesMatch "\.woff$">
    Header set Access-Control-Allow-Origin "%{ORIGIN_SUB_DOMAIN}e" env=ORIGIN_SUB_DOMAIN
</FilesMatch>

对于多个域,您只需更改SetEnvIf中的正则表达式。

答案 4 :(得分:60)

以下是如果将您的域与Nginx匹配,如何回显Origin头,如果您想为多个子域提供字体,这非常有用:

location /fonts {
    # this will echo back the origin header
    if ($http_origin ~ "example.org$") {
        add_header "Access-Control-Allow-Origin" $http_origin;
    }
}

答案 5 :(得分:24)

以下是我为AJAX请求的PHP应用程序所做的事情

$request_headers        = apache_request_headers();
$http_origin            = $request_headers['Origin'];
$allowed_http_origins   = array(
                            "http://myDumbDomain.example"   ,
                            "http://anotherDumbDomain.example"  ,
                            "http://localhost"  ,
                          );
if (in_array($http_origin, $allowed_http_origins)){  
    @header("Access-Control-Allow-Origin: " . $http_origin);
}

如果我的服务器允许请求源,请将$http_origin本身作为Access-Control-Allow-Origin标头的值返回,而不是返回*通配符。

答案 6 :(得分:19)

您应该注意一个缺点:只要您将文件外包到CDN(或任何其他不允许编写脚本的服务器)或者您的文件缓存在代理上,就会根据以下内容更改响应“Origin”请求标头不起作用。

答案 7 :(得分:18)

对于多个域,请在.htaccess

<IfModule mod_headers.c>
    SetEnvIf Origin "http(s)?://(www\.)?(domain1.example|domain2.example)$" AccessControlAllowOrigin=$0$1
    Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
    Header set Access-Control-Allow-Credentials true
</IfModule>

答案 8 :(得分:14)

对于安装了URL Rewrite 2.0模块的IIS 7.5+,请参阅this SO answer

答案 9 :(得分:14)

对于Nginx用户,允许CORS用于多个域。我喜欢@ marshall的例子,尽管他的答案只匹配一个域名。要匹配域和子域列表,此正则表达式可以轻松使用字体:

location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ {
   if ( $http_origin ~* (https?://(.+\.)?(domain1|domain2|domain3)\.(?:me|co|com)$) ) {
      add_header "Access-Control-Allow-Origin" "$http_origin";
   }
}

这只会回应&#34; Access-Control-Allow-Origin&#34;与给定的域列表匹配的标头。

答案 10 :(得分:11)

这是基于yesthatguy的答案的Java Web应用程序解决方案。

我正在使用Jersey REST 1.x

配置web.xml以了解Jersey REST和CORSResponseFilter

 <!-- Jersey REST config -->
  <servlet>    
    <servlet-name>JAX-RS Servlet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param> 
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
      <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
      <param-value>com.your.package.CORSResponseFilter</param-value>
    </init-param>   
    <init-param>
        <param-name>com.sun.jersey.config.property.packages</param-name>
        <param-value>com.your.package</param-value>
    </init-param>        
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>JAX-RS Servlet</servlet-name>
    <url-pattern>/ws/*</url-pattern>
  </servlet-mapping>

这是CORSResponseFilter的代码

import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;


public class CORSResponseFilter implements ContainerResponseFilter{

@Override
public ContainerResponse filter(ContainerRequest request,
        ContainerResponse response) {

    String[] allowDomain = {"http://localhost:9000","https://my.domain.example"};
    Set<String> allowedOrigins = new HashSet<String>(Arrays.asList (allowDomain));                  

    String originHeader = request.getHeaderValue("Origin");

    if(allowedOrigins.contains(originHeader)) {
        response.getHttpHeaders().add("Access-Control-Allow-Origin", originHeader);

        response.getHttpHeaders().add("Access-Control-Allow-Headers",
                "origin, content-type, accept, authorization");
        response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true");
        response.getHttpHeaders().add("Access-Control-Allow-Methods",
                "GET, POST, PUT, DELETE, OPTIONS, HEAD");
    }

    return response;
}

}

答案 11 :(得分:9)

如上所述,Access-Control-Allow-Origin应该是唯一的,Vary如果您位于CDN(内容分发网络)后面,则应设置为Origin

我的Nginx配置的相关部分:

if ($http_origin ~* (https?://.*\.mydomain.example(:[0-9]+)?)) {
  set $cors "true";
}
if ($cors = "true") {
  add_header 'Access-Control-Allow-Origin' "$http_origin";
  add_header 'X-Frame-Options' "ALLOW FROM $http_origin";
  add_header 'Access-Control-Allow-Credentials' 'true';
  add_header 'Vary' 'Origin';
}

答案 12 :(得分:7)

也许我错了,但据我所知,Access-Control-Allow-Origin有一个"origin-list"作为参数。

definition origin-list是:

origin            = "origin" ":" 1*WSP [ "null" / origin-list ]
origin-list       = serialized-origin *( 1*WSP serialized-origin )
serialized-origin = scheme "://" host [ ":" port ]
                  ; <scheme>, <host>, <port> productions from RFC3986

从此,我认为不同的起源被承认,应该空格分开

答案 13 :(得分:5)

我努力为运行HTTPS的域设置这个,所以我想我会分享解决方案。我在 httpd.conf 文件中使用了以下指令:

    <FilesMatch "\.(ttf|otf|eot|woff)$">
            SetEnvIf Origin "^http(s)?://(.+\.)?example\.com$" AccessControlAllowOrigin=$0
            Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
    </FilesMatch>

example.com更改为您的域名。在 httpd.conf 文件中的<VirtualHost x.x.x.x:xx>内添加此内容。请注意,如果您的VirtualHost有端口后缀(例如:80),那么此指令将不适用于HTTPS,因此您还需要转到 / etc / apache2 / sites-available / default-ssl 并在<VirtualHost _default_:443>部分内的该文件中添加相同的指令。

更新配置文件后,您需要在终端中运行以下命令:

a2enmod headers
sudo service apache2 reload

答案 14 :(得分:4)

如果您遇到字体问题,请使用:

<FilesMatch "\.(ttf|ttc|otf|eot|woff)$">
    <IfModule mod_headers>
        Header set Access-Control-Allow-Origin "*"
    </IfModule>
</FilesMatch>

答案 15 :(得分:4)

PHP代码:

elif

答案 16 :(得分:2)

对于ExpressJS应用程序,您可以使用:

app.use((req, res, next) => {
    const corsWhitelist = [
        'https://domain1.example',
        'https://domain2.example',
        'https://domain3.example'
    ];
    if (corsWhitelist.indexOf(req.headers.origin) !== -1) {
        res.header('Access-Control-Allow-Origin', req.headers.origin);
        res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
    }

    next();
});

答案 17 :(得分:2)

AWS Lambda / API网关

有关如何在无服务器AWS Lambda和API网关上配置多个来源的信息-尽管对于一个人来说相当大的解决方案应该很简单-请参见此处:

https://stackoverflow.com/a/41708323/1624933


当前无法在API网关中配置多个来源,请参见此处:https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors-console.html),但是建议(在上面的答案中)是:

  • 检查浏览器发送的Origin标头
  • 根据来源白名单进行检查
  • 如果匹配,则返回传入的Origin作为Access-Control-Allow-Origin标头,否则返回一个占位符(默认来源)。

简单的解决方案显然可以实现 全部(*)像这样:

exports.handler = async (event) => {
    const response = {
        statusCode: 200,
        headers: {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS
        },
        body: JSON.stringify([{

但是最好在API网关端执行此操作(请参见上面的第二个链接)。

答案 18 :(得分:1)

这是apache的扩展选项,包括一些最新的和计划的字体定义:

<FilesMatch "\.(ttf|otf|eot|woff|woff2|sfnt|svg)$">
    <IfModule mod_headers.c>
        SetEnvIf Origin "^http(s)?://(.+\.)?(domainname1|domainname2|domainname3)\.(?:com|net|org)$" AccessControlAllowOrigin=$0$1$2
        Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
        Header set Access-Control-Allow-Credentials true
    </IfModule>
</FilesMatch>

答案 19 :(得分:1)

为了促进ASMX服务的多域访问,我在global.asax文件中创建了这个函数:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    string CORSServices = "/account.asmx|/account2.asmx";
    if (CORSServices.IndexOf(HttpContext.Current.Request.Url.AbsolutePath) > -1)
    {
        string allowedDomains = "http://xxx.yyy.example|http://aaa.bbb.example";

        if(allowedDomains.IndexOf(HttpContext.Current.Request.Headers["Origin"]) > -1)
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", HttpContext.Current.Request.Headers["Origin"]);

        if(HttpContext.Current.Request.HttpMethod == "OPTIONS")
            HttpContext.Current.Response.End();
    }
}

这允许对OPTIONS动词进行CORS处理。

答案 20 :(得分:1)

对于.NET应用程序的相当容易的复制/粘贴,我写了这个来从global.asax文件中启用CORS。此代码遵循当前接受的答案中给出的建议,反映请求中给出的任何原点返回到响应中。这有效地实现了'*'而不使用它。这样做的原因是它支持多种其他CORS功能,包括能够发送带有'withCredentials'属性设置为'true'的AJAX XMLHttpRequest。

void Application_BeginRequest(object sender, EventArgs e)
{
    if (Request.HttpMethod == "OPTIONS")
    {
        Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
        Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
        Response.AddHeader("Access-Control-Max-Age", "1728000");
        Response.End();
    }
    else
    {
        Response.AddHeader("Access-Control-Allow-Credentials", "true");

        if (Request.Headers["Origin"] != null)
            Response.AddHeader("Access-Control-Allow-Origin" , Request.Headers["Origin"]);
        else
            Response.AddHeader("Access-Control-Allow-Origin" , "*");
    }
}

答案 21 :(得分:1)

一种更灵活的方法是使用Apache 2.4的表达式。您可以匹配域,路径以及几乎所有其他请求变量。尽管对于所有人而言,响应都是*,但接收到该响应的唯一请求者还是满足要求的请求。

<IfModule mod_headers.c>
    <If "%{HTTP:Host} =~ /\\bcdndomain\\.example$/i && %{HTTP:Origin} =~ /\\bmaindomain\\.example$/i">
        Header set Access-Control-Allow-Origin "*"
    </If>
</IfModule>

答案 22 :(得分:1)

Django还有一个答案。要使用单个视图允许来自多个域的CORS,这是我的代码:

def my_view(request):
    if 'HTTP_ORIGIN' in request.META.keys() and request.META['HTTP_ORIGIN'] in ['http://allowed-unsecure-domain.com', 'https://allowed-secure-domain.com', ...]:
        response = my_view_response() # Create your desired response data: JsonResponse, HttpResponse...
        # Then add CORS headers for access from delivery
        response["Access-Control-Allow-Origin"] = request.META['HTTP_ORIGIN']
        response["Access-Control-Allow-Methods"] = "GET" # "GET, POST, PUT, DELETE, OPTIONS, HEAD"
        response["Access-Control-Max-Age"] = "1000"  
        response["Access-Control-Allow-Headers"] = "*"  
        return response

答案 23 :(得分:1)

用于匹配子域的PHP代码示例。

if( preg_match("/http:\/\/(.*?)\.yourdomain.example/", $_SERVER['HTTP_ORIGIN'], $matches )) {
        $theMatch = $matches[0];
        header('Access-Control-Allow-Origin: ' . $theMatch);
}

答案 24 :(得分:1)

所有浏览器都不使用HTTP_ORIGIN。 How secure is HTTP_ORIGIN?对我来说,它在FF中是空的 我有允许访问我的网站的网站通过网站ID发送,然后检查我的数据库以获取具有该ID的记录并获得SITE_URL列值(www.yoursite.com)。

header('Access-Control-Allow-Origin: http://'.$row['SITE_URL']);

即使通过有效站点ID发送,请求也必须来自与该站点ID关联的数据库中列出的域。

答案 25 :(得分:0)

如果您尝试使用像我这样的代码示例来使其使用CORS,那么值得一提的是,您必须首先清除缓存以尝试它是否真的有效,类似于旧图像仍然存在时的问题,即使它已在服务器上删除(因为它仍保存在缓存中)。

例如,Google Chrome中的 CTRL + SHIFT + DEL 可删除您的缓存。

这有助于我在尝试了许多纯.htaccess解决方案后使用此代码,这似乎是唯一有效的(至少对我而言):

    Header add Access-Control-Allow-Origin "http://google.com"
    Header add Access-Control-Allow-Headers "authorization, origin, user-token, x-requested-with, content-type"
    Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"

    <FilesMatch "\.(ttf|otf|eot|woff)$">
        <IfModule mod_headers.c>
            SetEnvIf Origin "http(s)?://(www\.)?(google.com|staging.google.com|development.google.com|otherdomain.com|dev02.otherdomain.net)$" AccessControlAllowOrigin=$0
            Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
        </IfModule>
    </FilesMatch>

另请注意,很多解决方案都说您必须输入Header set ...,但它是Header add ...。希望这能帮助像我一样有几个小时遇到同样麻烦的人。

答案 26 :(得分:0)

Google对serving ads over SSLgrammar in the RFC itself的支持回答似乎表明您可以空间分隔网址。不确定这在不同浏览器中的支持程度如何。

答案 27 :(得分:0)

下面的答案特定于C#,但该概念应适用于所有不同的平台。

要允许来自Web api的跨源请求,您需要允许对应用程序的Option请求,并在控制器级别添加以下注释。

[EnableCors(UrlString,Header,Method)] 现在,原点只能传递s字符串。因此,如果您要在请求中传递多个URL,请将其作为逗号分隔值传递。

UrlString =“ https://a.hello.com,https://b.hello.com

答案 28 :(得分:0)

只能为Access-Control-Allow-Origin标头指定一个原点。但是您可以根据要求在响应中设置原点。同样不要忘记设置Vary标头。在PHP中,我将执行以下操作:

    /**
     * Enable CORS for the passed origins.
     * Adds the Access-Control-Allow-Origin header to the response with the origin that matched the one in the request.
     * @param array $origins
     * @return string|null returns the matched origin or null
     */
    function allowOrigins($origins)
    {
        $val = $_SERVER['HTTP_ORIGIN'] ?? null;
        if (in_array($val, $origins, true)) {
            header('Access-Control-Allow-Origin: '.$val);
            header('Vary: Origin');

            return $val;
        }

        return null;
    }

  if (allowOrigins(['http://localhost', 'https://localhost'])) {
      echo your response here, e.g. token
  }

答案 29 :(得分:0)

我有 https://stackoverflow.com/a/7454204/13779574 这段代码运行良好,但在用户进入该页面时出现错误。 我用这段代码解决了这个问题。

if (isset($_SERVER['HTTP_ORIGIN'])) {
   $http_origin = $_SERVER['HTTP_ORIGIN'];
   if ($http_origin == "http://localhost:3000" || $http_origin == "http://api.loc/"){  
      header("Access-Control-Allow-Origin: $http_origin");
   }
}

答案 30 :(得分:0)

我也遇到了同样的问题。 我的客户端在 9097 上,api 网关在 9098 上,微服务在 .... 实际上我正在使用spring cloud Api网关
在我的网关 yml 文件中,我允许跨域 - ... allowedOrigins: "http://localhost:9097"

也在我的微服务中使用@crossOrigin

当客户端向 api 网关发送请求时,响应了两个“Access-Control-Allow-Origin”标头 [一个来自 api yml 文件,一个来自微服务 @crossorigin] 所以浏览器阻止了请求

我解决了--

    @Bean
public RouteLocator getRL(RouteLocatorBuilder builder) {
    
return  builder.routes()
    
        .route(p-> 
         "/friendlist","/guest/**"
                )
         .filters(f ->{
             //f.removeResponseHeader("Access-Control-Allow-Origin");
             //f.addResponseHeader("Access-Control-Allow-Origin","http://localhost:9097");
             f.setResponseHeader("Access-Control-Allow-Origin","http://localhost:9097");
             return f;
         })
        .uri("lb://OLD-SERVICE")
        
        
    ).build();      
}

答案 31 :(得分:-2)

我们也可以在Asp.net应用程序的Global.asax文件中设置它。

protected void Application_BeginRequest(object sender, EventArgs e)
    {

    // enable CORS
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "https://www.youtube.com");

    }

答案 32 :(得分:-4)

答案似乎是不止一次使用标题。也就是说,而不是发送

Access-Control-Allow-Origin: http://domain1.example, http://domain2.example, http://domain3.example

发送

Access-Control-Allow-Origin: http://domain1.example
Access-Control-Allow-Origin: http://domain2.example
Access-Control-Allow-Origin: http://domain3.example

在Apache上,您可以使用httpd.conf<VirtualHost> .htaccess部分或mod_headers文件中执行此操作,并使用以下语法:

Header add Access-Control-Allow-Origin "http://domain1.example"
Header add Access-Control-Allow-Origin "http://domain2.example"
Header add Access-Control-Allow-Origin "http://domain3.example"

诀窍是使用add而不是append作为第一个参数。