1150

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?

sideshowbarker
  • 62,215
  • 21
  • 143
  • 153
Thomas J Bradley
  • 11,764
  • 3
  • 15
  • 8
  • 12
    [Rather than allowing a space-separated list of origins, (origin-list-or-null) is either a single origin or the string "null".](http://www.w3.org/TR/cors/#access-control-allow-origin-response-header) (thanks @maxpolk) – sam Oct 01 '13 at 01:30
  • 3
    Using the most recent Firefox, neither comma seperated, nor space seperated domains did work. Matching against a list of domains and putting a single host in the headers is still better security and does work properly. – Daniel W. Mar 26 '14 at 16:58
  • 2
    If you're struggling with this for HTTPS, I found a [solution](http://stackoverflow.com/questions/1653308/access-control-allow-origin-multiple-origin-domains/28552592#28552592). – Alex W Feb 17 '15 at 00:14
  • @sam or "*" ? `shared based by returning the value of the Origin request header, "*", or "null"` – The Red Pea Sep 29 '17 at 22:45
  • 12
    **_important note_**: allowing only cretain domains in the `Access-Control-Allow-Origin` header does **not** mean that other domains cannot trigger a method on this endpoint (e.g. REST API method). It just means that disallowed origins cannot use the result in javascript (browser ensures this). For restricting access to an endpoint for specific domains use a server-side request filter that e.g. returns HTTP 401 for disallowed domains. – klues Nov 21 '18 at 12:41
  • 2
    You should always append `Vary: Origin` header when you want to use multiple URLs, see: https://fetch.spec.whatwg.org/#cors-protocol-and-http-caches – Null Jun 07 '19 at 20:30

32 Answers32

931

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>
kmgdev
  • 2,527
  • 24
  • 37
yesthatguy
  • 9,474
  • 1
  • 14
  • 4
245

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");
}
Nikolay Ivanov
  • 4,603
  • 1
  • 24
  • 20
122

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.

Patrick Mevzek
  • 7,662
  • 15
  • 28
  • 43
Jay Dave
  • 1,663
  • 1
  • 13
  • 9
92

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.

the Tin Man
  • 150,910
  • 39
  • 198
  • 279
Staugaard
  • 1,037
  • 7
  • 5
70

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;
    }
}
mjallday
  • 8,858
  • 8
  • 48
  • 70
32

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.

Patrick Mevzek
  • 7,662
  • 15
  • 28
  • 43
Rakib
  • 9,946
  • 11
  • 59
  • 93
  • Should probably check that `$request_headers['Origin'];` exists, otherwise any direct requests are going to trigger an E_NOTICE. – MrWhite Jan 12 '21 at 18:10
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>
Patrick Mevzek
  • 7,662
  • 15
  • 28
  • 43
George
  • 5,648
  • 5
  • 43
  • 63
21

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.

Mark
  • 227
  • 2
  • 2
20

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.

Adriano Rosa
  • 6,547
  • 1
  • 20
  • 24
19

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();
});
eyecatchUp
  • 8,385
  • 4
  • 48
  • 61
16

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

Community
  • 1
  • 1
Paco Zarate
  • 1,665
  • 13
  • 13
15

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';
}
Amruta-Pani
  • 847
  • 9
  • 21
hernvnc
  • 477
  • 6
  • 11
13

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

}
Patrick Mevzek
  • 7,662
  • 15
  • 28
  • 43
duvo
  • 1,488
  • 2
  • 17
  • 29
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.

Patrick Mevzek
  • 7,662
  • 15
  • 28
  • 43
drAlberT
  • 18,939
  • 5
  • 29
  • 36
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
Alex W
  • 33,401
  • 9
  • 92
  • 97
  • I like this option and combined/modified it with the implementation that @George has. Sometimes servers don't have a2enmod available, so all you have to do is check your main httpd.conf to see if the line: LoadModule headers_module modules/mod_headers.so is uncommented. – Mike Kormendy Feb 26 '15 at 20:09
  • My origin had a port number, so I modified the regular expression to include that: `^http(s)?://(.+\.)?example\.com(:\d+)?$` – indiv May 01 '15 at 21:43
6

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>
noun
  • 3,502
  • 2
  • 23
  • 26
5

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');
Liakos
  • 229
  • 2
  • 10
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>
Mike Kormendy
  • 3,127
  • 1
  • 21
  • 21
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" , "*");
    }
}
QA Collective
  • 1,610
  • 15
  • 26
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.

Community
  • 1
  • 1
mathius1
  • 1,321
  • 11
  • 16
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);
}
Patrick Mevzek
  • 7,662
  • 15
  • 28
  • 43
blak3r
  • 14,939
  • 14
  • 72
  • 91
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.

Patrick Mevzek
  • 7,662
  • 15
  • 28
  • 43
Derek Wade
  • 589
  • 6
  • 11
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).

timhc22
  • 6,059
  • 5
  • 39
  • 56
  • 2
    `Access-Control-Allow-Credentials: true` is not allowed with wildcard `Access-Control-Allow-Origin: *`. Set a specific `` instead. – Tom Apr 18 '20 at 14:16
  • @Tom, yea, not sure why that was in there, I can't remember, but I might have copied it from the defaults that were added on AWS? Thanks for pointing that out though. – timhc22 Apr 20 '20 at 16:49
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
Silvain
  • 379
  • 4
  • 17
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.

Bob Aman
  • 31,735
  • 9
  • 67
  • 95
  • 'serving ads over ssl' links to the spec http://www.w3.org/TR/cors/#access-control-allow-origin-response-header which adds a note, "In practice the origin-list-or-null production is more constrained. Rather than allowing a space-separated list of origins, it is either a single origin or the string "null". – spazm Apr 29 '15 at 19:09
  • While it's important to note that detail, when a specification says "In practice", it doesn't mean that it's only valid to do it that way. It means that if you do it that way, you may run into problems because the majority of implementors either implement the spec incorrectly or incompletely. The specification does allow for a space-separated list of origins, which you can see here in the EBNF under `origin-list`: http://tools.ietf.org/html/rfc6454#section-7.1 – Bob Aman May 04 '15 at 08:55
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.

AlexioVay
  • 3,158
  • 2
  • 21
  • 41
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"

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
  }
Simon
  • 125
  • 9
0

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");
   }
}
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();      
}
akhil
  • 36
  • 4
-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");

    }
sudhAnsu63
  • 5,510
  • 4
  • 34
  • 50
-11

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.

Patrick Mevzek
  • 7,662
  • 15
  • 28
  • 43
Ben C. Forsberg
  • 787
  • 6
  • 2
  • The trick looks promising, but it doesn't work in FF 3.6.13. What I observe is that two headers with the same name are joined into one, with values separated with a comma -- and it doesn't work, as OP posted. I observed the headers in LiveHTTPHeaders and FireBug. – pwes Feb 10 '11 at 12:38
  • 1
    See, when I did it, I did "Header set ..." rather than "Header add ..." - seems to work for me. Firefox 3.6.16 – B T Apr 04 '11 at 23:50
  • This is not allowed and does not work in FF - https://bugzilla.mozilla.org/show_bug.cgi?id=671608 – Sairam Aug 27 '12 at 18:38
  • 2
    there's a variation on this which seems to work: http://stackoverflow.com/questions/9466496/how-to-configure-apache-to-handle-multiple-domains-with-access-control-allow-ori – Jack James Apr 28 '13 at 07:58
  • 113
    Just spent two hours trying to fix an issue related to CORS and it turns out that it was because of multiple Access-Control-Allow-Origin headers. I removed the multiple Access-Control-Allow-Origin headers and it started working. So this is not the right answer despite the number of votes. Use this method instead to support multiple domains: http://stackoverflow.com/a/1850482/123545 – ErJab May 25 '13 at 00:26
  • You will in fact get an error message in the log from the latest Chrome browser as of now, telling you that it's ignoring the Access-Control-Allow-Origin header because there are multiple values. – Danger Apr 24 '14 at 23:33
  • 1
    Nope. From https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS : "A returned resource may have one Access-Control-Allow-Origin header" .. and from http://www.w3.org/TR/cors/#access-control-allow-origin-response-header : "Rather than allowing a space-separated list of origins, it is either a single origin or the string "null"" (first I've heard of "null" here btw) – AlexChaffee Jun 10 '14 at 17:36
  • 21
    The [specs](http://www.w3.org/TR/cors/) clearly say that multiple values will cause the CORS algorithm to fails. So this isn't correct. – Lior Sep 08 '14 at 15:56
  • 9
    Also confirming this is incorrect. Using this to serve CSS to four domains, I receive an error in Chrome's log *"[...] header contains multiple values 'http://aaa.com, http://bbb.com', but only one is allowed"*. It does not matter if you define it as one header, or multiple, or using `add` vs. `set`. It just doesn't work. – Radley Sustaire Oct 08 '14 at 17:56