Access-Control-Allow-Origin Несколько Доменов Происхождения?

есть ли способ разрешить несколько кросс-доменов с помощью Access-Control-Allow-Origin заголовок?

Я в курсе *, но он слишком открыт. Я действительно хочу разрешить только пару доменов.

в качестве примера, что-то вроде этого:

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

Я пробовал выше код, но не работает в Firefox.

можно ли указать несколько доменов, или я застрял с одним?

26 ответов


звучит как рекомендуемый способ сделать это, чтобы ваш сервер прочитал заголовок Origin от клиента, сравните это со списком доменов, которые вы хотели бы разрешить, и если он соответствует, Эхо значение Origin заголовок возвращается клиенту как Access-Control-Allow-Origin заголовок в ответе.

С .htaccess вы можете сделать это так:

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

другое решение, которое я использую в 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");
}

это сработало для меня:

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

во .htaccess, это будет работать наверняка.


у меня была такая же проблема с WOFF-шрифтов, неограниченное количество поддоменов, имел доступ. Чтобы разрешить субдомены, я добавил что-то подобное в свой httpd.conf:

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

для нескольких доменов Вы можете просто изменить регулярное выражение в SetEnvIf.


вот как повторить заголовок Origin, если он соответствует вашему домену с Nginx, это полезно, если вы хотите обслуживать несколько поддоменов шрифта:

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

вот что я сделал для приложения PHP, которое запрашивается 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);
}

если запрашивающий источник разрешен моим сервером, верните $http_origin себя как значение Access-Control-Allow-Origin заголовок вместо возврата * символ.


есть один недостаток, который вы должны знать: как только вы исходные файлы на CDN (или любой другой сервер, который не позволяет создавать сценарии) или если ваши файлы кэшируются на прокси-сервере, изменение ответа на основе заголовка запроса "Origin" не будет работать.


для нескольких доменов, в .htaccess:

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

для IIS 7.5+ с установленным модулем перезаписи URL 2.0 см. это так ответ


для пользователей Nginx разрешить CORS для нескольких доменов. Мне нравится пример @marshall, хотя его anwers соответствует только одному домену. Чтобы соответствовать списку домена и поддомена, это регулярное выражение упрощает работу со шрифтами:

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

Это будет только эхо заголовков" Access-Control-Allow-Origin", которые соответствуют заданному списку доменов.


вот решение для Java web app, основанное на ответе от yesthatguy.

Я использую Джерси REST 1.x

настройки сети.xml, чтобы быть в курсе Джерси 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;
}

}

как уже упоминалось выше, Access-Control-Allow-Origin должен быть уникальным и Vary должно быть установлено в Origin Если вы находитесь за CDN (сеть доставки контента).

соответствующая часть моей конфигурации 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';
}

может быть, я и ошибаюсь, но насколько я вижу Access-Control-Allow-Origin есть "origin-list" в качестве параметра.

By определение an 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

и из этого я утверждаю, что разные истоки допускаются и должны быть пробел.


Я изо всех сил пытался настроить это для домена под управлением HTTPS, поэтому я решил поделиться решением. Я использовал следующую директиву в моем httpd.conf:

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

изменить example.com к Вашему доменному имени. Добавьте это внутрь <VirtualHost x.x.x.x:xx> в своем httpd.conf. Обратите внимание, что если ваш VirtualHost имеет суффикс порта (например,:80) тогда эта директива не будет применяться к HTTPS, поэтому вам также нужно будет перейти к / etc/apache2/sites-доступно / по умолчанию-ssl и добавьте ту же директиву в этот файл, внутри .

как только файлы конфигурации будут обновлены, вам нужно будет выполнить следующие команды в терминале:

a2enmod headers
sudo service apache2 reload

Если у вас возникли проблемы со шрифтами, использовать:

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

HTTP_ORIGIN используется не всеми браузерами. насколько безопасен HTTP_ORIGIN? для меня он появляется пустым в FF.
У меня есть сайты, которые я разрешаю доступ к моему сайту, отправляют идентификатор сайта, Затем я проверяю свою БД для записи с этим идентификатором и получаю значение столбца SITE_URL (www.yoursite.com).

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

даже если отправить действительный идентификатор сайта, запрос должен быть из домена, указанного в моей БД, связанного с этим идентификатором сайта.


вот расширенный вариант для apache, который включает в себя некоторые из последних и запланированных определений шрифтов:

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

и еще один ответ на 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

чтобы облегчить доступ к нескольким доменам для службы ASMX, я создал эту функцию в глобальном.файл 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();
    }
}

Это позволяет обрабатывать CORS OPTIONS глагол тоже.


ответ поддержки Google на обслуживание объявлений по SSL и грамматика в самом RFC казалось бы, указывает, что вы можете разделять URL-адреса пробелом. Не уверен, насколько хорошо это поддерживается в разных браузерах.


если вы попробуете так много примеров кода, как я, чтобы заставить его работать с помощью CORS, стоит упомянуть, что вы должны сначала очистить кэш, чтобы попробовать, если он действительно работает, подобно проблемам, когда старые изображения все еще присутствуют, даже если он удален на сервере (потому что он все еще сохраняется в кэше).

CTRL + SHIFT + DEL в Google Chrome, чтобы удалить кэш.

это помогло мне использовать этот код после попытки многих pure .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=
            Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
        </IfModule>
    </FilesMatch>

также обратите внимание, что широко распространено, что многие решения говорят, что вы должны ввести Header set ... но это Header add .... Надеюсь, это поможет кому-то с такими же проблемами в течение нескольких часов, как я.


для довольно простой копирования / вставки для приложений .NET я написал это, чтобы включить CORS из глобального.файл asax. Этот кодекс следует рекомендациям, содержащимся в принятом в настоящее время ответе, и отражает любое происхождение, указанное в запросе в ответе. Это эффективно достигает ' * ' без его использования. Причина этого заключается в том, что он включает несколько других функций CORS, включая возможность отправки AJAX XMLHttpRequest с атрибутом 'withCredentials', установленным в 'правда'.

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" , "*");
    }
}

более гибкий подход заключается в использовании выражений Apache 2.4. Вы можете сопоставлять Домены, пути и почти каждую другую переменную запроса. Хотя ответ * для всех единственными запросчиками, получающими этот ответ, являются те, которые в любом случае отвечают требованиям.

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

пример кода PHP для сопоставления поддоменов.

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

ответ, похоже, заключается в том, чтобы использовать заголовок более одного раза. То есть, вместо отправки

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 в качестве первого аргумента.


мы также можем установить это в Global.файл asax для Asp.net применение.

protected void Application_BeginRequest(object sender, EventArgs e)
    {

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

    }