1228

Is there a way to allow multiple cross-domains using the Access-Control-Allow-Origin header?

I'm aware of the *, but it is too open. I really want to allow just a couple domains.

As an example, something like this:

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

I have tried the above code but it does not seem to work in Firefox.

Is it possible to specify multiple domains or am I stuck with just one?

4

32 回答 32

979

Sounds like the recommended way to do it is to have your server read the Origin header from the client, compare that to the list of domains you would like to allow, and if it matches, echo the value of the Origin header back to the client as the Access-Control-Allow-Origin header in the response.

With .htaccess you can do it like this:

# ----------------------------------------------------------------------
# Allow loading of external fonts
# ----------------------------------------------------------------------
<FilesMatch "\.(ttf|otf|eot|woff|woff2)$">
    <IfModule mod_headers.c>
        SetEnvIf Origin "http(s)?://(www\.)?(google.com|staging.google.com|development.google.com|otherdomain.example|dev02.otherdomain.example)$" AccessControlAllowOrigin=$0
        Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
        Header merge Vary Origin
    </IfModule>
</FilesMatch>
于 2009-12-05T00:10:06.193 回答
263

Another solution I'm using in 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");
}
于 2011-09-17T10:49:21.953 回答
126

This worked for me:

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

When put in .htaccess, it will work for sure.

于 2012-06-18T06:31:30.177 回答
95

I had the same problem with woff-fonts, multiple subdomains had to have access. To allow subdomains I added something like this to my 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>

For multiple domains you could just change the regex in SetEnvIf.

于 2011-05-18T12:46:07.030 回答
75

Here's how to echo the Origin header back if it matches your domain with Nginx, this is useful if you want to serve a font multiple sub-domains:

location /fonts {
    # this will echo back the origin header
    if ($http_origin ~ "example.org$") {
        add_header "Access-Control-Allow-Origin" $http_origin;
    }
}
于 2012-09-13T20:25:22.913 回答
33

Here is what i did for a PHP application which is being requested by AJAX

$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);
}

If the requesting origin is allowed by my server, return the $http_origin itself as value of the Access-Control-Allow-Origin header instead of returning a * wildcard.

于 2012-12-25T21:48:57.790 回答
27

For ExpressJS applications you can use:

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();
});
于 2019-01-25T13:57:04.780 回答
24

There is one disadvantage you should be aware of: As soon as you out-source files to a CDN (or any other server which doesn't allow scripting) or if your files are cached on a proxy, altering response based on 'Origin' request header will not work.

于 2010-02-20T19:18:51.397 回答
23

For Nginx users to allow CORS for multiple domains. I like the @marshall's example although his anwers only matches one domain. To match a list of domain and subdomain this regex make it ease to work with fonts:

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";
   }
}

This will only echo "Access-Control-Allow-Origin" headers that matches with the given list of domains.

于 2015-01-10T18:31:14.053 回答
21

For multiple domains, in your .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>
于 2014-03-11T16:51:01.447 回答
18

As mentioned above, Access-Control-Allow-Origin should be unique and Vary should be set to Origin if you are behind a CDN (Content Delivery Network).

Relevant part of my Nginx configuration:

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';
}
于 2015-11-09T15:59:29.703 回答
16

For IIS 7.5+ with URL Rewrite 2.0 module installed please see this SO answer

于 2015-06-27T01:57:46.703 回答
15

Here's a solution for Java web app, based the answer from yesthatguy.

I am using Jersey REST 1.x

Configure the web.xml to be aware of Jersey REST and the 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>

Here's the code for 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;
    }
}
于 2015-09-15T18:39:34.000 回答
9

Maybe I am wrong, but as far as I can see Access-Control-Allow-Origin has an "origin-list" as parameter.

By definition an origin-list is:

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

And from this, I argue different origins are admitted and should be space separated.

于 2012-10-10T11:40:59.697 回答
8

I struggled to set this up for a domain running HTTPS, so I figured I would share the solution. I used the following directive in my httpd.conf file:

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

Change example.com to your domain name. Add this inside <VirtualHost x.x.x.x:xx> in your httpd.conf file. Notice that if your VirtualHost has a port suffix (e.g. :80) then this directive will not apply to HTTPS, so you will need to also go to /etc/apache2/sites-available/default-ssl and add the same directive in that file, inside of the <VirtualHost _default_:443> section.

Once the config files are updated, you will need to run the following commands in the terminal:

a2enmod headers
sudo service apache2 reload
于 2015-02-17T00:12:56.713 回答
7

If you are having trouble with fonts, use:

<FilesMatch "\.(ttf|ttc|otf|eot|woff)$">
    <IfModule mod_headers>
        Header set Access-Control-Allow-Origin "*"
    </IfModule>
</FilesMatch>
于 2014-07-06T17:43:55.557 回答
6

PHP Code:

$httpOrigin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : null;
if (in_array($httpOrigin, [
    'http://localhost:9000', // Co-worker dev-server
    'http://127.0.0.1:9001', // My dev-server
])) header("Access-Control-Allow-Origin: ${httpOrigin}");
header('Access-Control-Allow-Credentials: true');
于 2020-01-23T14:47:42.863 回答
3

Here's an expanded option for apache that includes some of the latest and planned font definitions:

<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>
于 2015-02-26T20:46:21.753 回答
3

For a fairly easy copy / paste for .NET applications, I wrote this to enable CORS from within a global.asax file. This code follows the advice given in the currently accepted answer, reflecting whatever origin back is given in the request into the response. This effectively achieves '*' without using it.

The reason for this is that it enables multiple other CORS features, including the ability to send an AJAX XMLHttpRequest with the 'withCredentials' attribute set to 'true'.

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" , "*");
    }
}
于 2017-12-21T01:24:31.380 回答
2

HTTP_ORIGIN is not used by all browsers. How secure is HTTP_ORIGIN? For me it comes up empty in FF.
I have the sites that I allow access to my site send over a site ID, I then check my DB for the record with that id and get the SITE_URL column value (www.yoursite.com).

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

Even if the send over a valid site ID the request needs to be from the domain listed in my DB associated with that site ID.

于 2013-05-03T15:39:29.013 回答
2

PHP code example for matching subdomains.

if( preg_match("/http:\/\/(.*?)\.yourdomain.example/", $_SERVER['HTTP_ORIGIN'], $matches )) {
        $theMatch = $matches[0];
        header('Access-Control-Allow-Origin: ' . $theMatch);
}
于 2013-06-25T07:53:30.663 回答
2

To facilitate multiple domain access for an ASMX service, I created this function in the global.asax file:

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();
    }
}

This allows for CORS handling of OPTIONS verb also.

于 2016-11-11T23:59:57.473 回答
2

AWS Lambda/API Gateway

For information on how to configure multiple origins on Serverless AWS Lambda and API Gateway - albeit a rather large solution for something one would feel should be quite straightforward - see here:

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


It is currently not possible to configure multiple origins in API Gateway, see here: https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors-console.html), but the recommendation (in the answer above) is:

  • inspect the Origin header sent by the browser
  • check it against a whitelist of origins
  • if it matches, return the incoming Origin as the Access-Control-Allow-Origin header, else return a placeholder (default origin).

The simple solution is obviously enabling ALL (*) like so:

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([{

But it might be better to do this on the API Gateway side (see 2nd link above).

于 2020-03-23T12:57:05.663 回答
1

And one more answer in Django. To have a single view allow CORS from multiple domains, here is my code:

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
于 2016-09-28T11:05:28.353 回答
1

I have https://stackoverflow.com/a/7454204/13779574 this code worked well but gives an error when the user enters that page. I fixed this problem with this code.

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");
   }
}
于 2021-04-13T18:41:37.580 回答
0

Google's support answer on serving ads over SSL and the grammar in the RFC itself would seem to indicate that you can space delimit the URLs. Not sure how well-supported this is in different browsers.

于 2013-10-18T12:39:56.880 回答
0

If you try so many code examples like me to make it work using CORS, it is worth to mention that you have to clear your cache first to try if it actually works, similiar to issues like when old images are still present, even if it's deleted on the server (because it is still saved in your cache).

For example CTRL + SHIFT + DEL in Google Chrome to delete your cache.

This helped me using this code after trying many pure .htaccess solutions and this seemed the only one working (at least for me):

    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>

Also note that it is widely spread that many solutions say you have to type Header set ... but it is Header add .... Hope this helps someone having the same troubles for some hours now like me.

于 2017-02-03T08:19:11.297 回答
0

Below answer is specific to C#, but the concept should be applicable to all the different platforms.

To allow Cross Origin Requests from a web api, You need to allow Option requests to your Application and Add below annotation at controller level.

[EnableCors(UrlString,Header, Method)] Now the origins can be passed only a s string. SO if you want to pass more than one URL in the request pass it as a comma seperated value.

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

于 2019-03-15T06:23:41.927 回答
0

Only a single origin can be specified for the Access-Control-Allow-Origin header. But you can set the origin in your response according to the request. Also don't forget to set the Vary header. In PHP I would do the following:

    /**
     * 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
  }
于 2019-07-25T10:52:02.297 回答
0

i was also facing same problem. my client was on 9097,api gateway on 9098,microservice on .... Actually i was using spring cloud Api gateway
in my gateway yml file i had allowed crossorigin like-- ... allowedOrigins: "http://localhost:9097"

also in my microservice i was using @crossOrigin

when client sent request to api gateway, two "Access-Control-Allow-Origin" headers was coming in responce [one from api yml file and one from microservice @crossorigin] so browser blocked request

i solved it as--

    @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();      
}
于 2021-05-06T04:44:00.553 回答
-3

We can also set this in Global.asax file for Asp.net application.

protected void Application_BeginRequest(object sender, EventArgs e)
    {

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

    }
于 2015-03-31T11:59:45.243 回答
-14

The answer seems to be to use the header more than once. That is, rather than sending

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

send

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

On Apache, you can do this in an httpd.conf <VirtualHost> section or .htaccess file using mod_headers and this syntax:

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"

The trick is to use add rather than append as the first argument.

于 2010-11-05T22:22:20.517 回答