396

我正在尝试为所有子域、端口和协议启用 CORS。

例如,我希望能够运行从http://sub.mywebsite.com:8080/https://www.mywebsite.com/的 XHR 请求*

通常,我想启用来自源匹配的请求(并且仅限于):

//*.mywebsite.com:*/*

4

12 回答 12

312

CORS 规范是全有或全无。它只支持*null或者确切的协议+域+端口:http ://www.w3.org/TR/cors/#access-control-allow-origin-response-header

您的服务器将需要使用正则表达式验证原始标头,然后您可以在Access-Control-Allow-Origin响应标头中回显原始值。

于 2012-12-22T20:14:31.117 回答
250

基于 DaveRandom 的回答,我也在玩,发现了一个稍微简单的 Apache 解决方案,它产生相同的结果(Access-Control-Allow-Origin动态设置为当前特定协议 + 域 + 端口),而不使用任何重写规则:

SetEnvIf Origin ^(https?://.+\.mywebsite\.com(?::\d{1,5})?)$   CORS_ALLOW_ORIGIN=$1
Header append Access-Control-Allow-Origin  %{CORS_ALLOW_ORIGIN}e   env=CORS_ALLOW_ORIGIN
Header merge  Vary "Origin"

就是这样。

那些想要在父域(例如 mywebsite.com)以及其所有子域上启用 CORS 的人可以简单地将第一行中的正则表达式替换为这个:

^(https?://(?:.+\.)?mywebsite\.com(?::\d{1,5})?)$.

注意:为了规范合规性和正确的缓存行为,始终Vary: Origin为启用 CORS 的资源添加响应标头,即使对于非 CORS 请求和来自不允许来源的请求(参见示例原因)。

于 2015-01-16T18:09:55.180 回答
62

编辑:使用@Noyo 的解决方案而不是这个。它更简单、更清晰,并且在负载下可能性能更高。

原始答案仅用于历史目的!


我对这个问题做了一些尝试,并提出了这个可与 Apache 一起使用的可重用 .htaccess(或 httpd.conf)解决方案:

<IfModule mod_rewrite.c>
<IfModule mod_headers.c>
    # Define the root domain that is allowed
    SetEnvIf Origin .+ ACCESS_CONTROL_ROOT=yourdomain.com

    # Check that the Origin: matches the defined root domain and capture it in
    # an environment var if it does
    RewriteEngine On
    RewriteCond %{ENV:ACCESS_CONTROL_ROOT} !=""
    RewriteCond %{ENV:ACCESS_CONTROL_ORIGIN} =""
    RewriteCond %{ENV:ACCESS_CONTROL_ROOT}&%{HTTP:Origin} ^([^&]+)&(https?://(?:.+?\.)?\1(?::\d{1,5})?)$
    RewriteRule .* - [E=ACCESS_CONTROL_ORIGIN:%2]

    # Set the response header to the captured value if there was a match
    Header set Access-Control-Allow-Origin %{ACCESS_CONTROL_ORIGIN}e env=ACCESS_CONTROL_ORIGIN
</IfModule>
</IfModule>

只需将ACCESS_CONTROL_ROOT块顶部的变量设置为您的根域,如果它与您的域匹配,它将Origin:在响应标头值中将请求标头值回显给客户端。Access-Control-Allow-Origin:

另请注意,您可以使用sub.mydomain.comasACCESS_CONTROL_ROOT并将来源限制为sub.mydomain.comand *.sub.mydomain.com(即它不必是域根)。可以通过修改正则表达式的 URI 匹配部分来控制允许变化的元素(协议、端口)。

于 2013-09-23T12:09:52.657 回答
39

我正在回答这个问题,因为接受的答案不能跟随

  1. 正则表达式分组会影响性能,这不是必需的。
  2. 无法匹配域,它仅适用于子域。

例如:它不会为http://mywebsite.com发送 CORS 标头,而适用于http://somedomain.mywebsite.com/

SetEnvIf Origin "http(s)?://(.+\.)?mywebsite\.com(:\d{1,5})?$" CORS=$0

Header set Access-Control-Allow-Origin "%{CORS}e" env=CORS
Header merge  Vary "Origin"

要为您的站点启用,您只需在上述 Apache 配置中将您的站点替换为“mywebsite.com”。

允许多个站点:

SetEnvIf Origin "http(s)?://(.+\.)?(othersite\.com|mywebsite\.com)(:\d{1,5})?$" CORS=$0

部署后验证:

以下 curl 响应在更改后应具有“Access-Control-Allow-Origin”标头。

curl -X GET -H "Origin: http://examplesite1.com" --verbose http://examplesite2.com/query
于 2016-09-23T19:54:47.330 回答
15

我需要一个仅限 PHP 的解决方案,以防万一有人也需要它。它接受一个允许的输入字符串,如“*.example.com”,如果输入匹配,则返回请求标头服务器名称。

function getCORSHeaderOrigin($allowed, $input)
{
    if ($allowed == '*') {
        return '*';
    }

    $allowed = preg_quote($allowed, '/');

    if (($wildcardPos = strpos($allowed, '*')) !== false) {
        $allowed = str_replace('*', '(.*)', $allowed);
    }

    $regexp = '/^' . $allowed . '$/';

    if (!preg_match($regexp, $input, $matches)) {
        return 'none';
    }

    return $input;
}

以下是 phpunit 数据提供者的测试用例:

//    <description>                            <allowed>          <input>                   <expected>
array('Allow Subdomain',                       'www.example.com', 'www.example.com',        'www.example.com'),
array('Disallow wrong Subdomain',              'www.example.com', 'ws.example.com',         'none'),
array('Allow All',                             '*',               'ws.example.com',         '*'),
array('Allow Subdomain Wildcard',              '*.example.com',   'ws.example.com',         'ws.example.com'),
array('Disallow Wrong Subdomain no Wildcard',  '*.example.com',   'example.com',            'none'),
array('Allow Double Subdomain for Wildcard',   '*.example.com',   'a.b.example.com',        'a.b.example.com'),
array('Don\'t fall for incorrect position',    '*.example.com',   'a.example.com.evil.com', 'none'),
array('Allow Subdomain in the middle',         'a.*.example.com', 'a.bc.example.com',       'a.bc.example.com'),
array('Disallow wrong Subdomain',              'a.*.example.com', 'b.bc.example.com',       'none'),
array('Correctly handle dots in allowed',      'example.com',     'exampleXcom',            'none'),
于 2015-05-22T10:42:38.147 回答
3

当从“cookie 域”( www.domain.tld )读取字体时,我们在静态“无 cookie”域上的 Font Awesome 遇到了类似的问题,这篇文章是我们的英雄。请参阅此处:如何解决“缺少跨域资源共享 (CORS) 响应标头”网络字体问题?

对于 copy/paste-r 类型(并提供一些道具),我将所有贡献拼凑在一起,并将其添加到站点根目录的 .htaccess 文件的顶部:

<IfModule mod_headers.c>
 <IfModule mod_rewrite.c>
    SetEnvIf Origin "http(s)?://(.+\.)?(othersite\.com|mywebsite\.com)(:\d{1,5})?$" CORS=$0
    Header set Access-Control-Allow-Origin "%{CORS}e" env=CORS
    Header merge  Vary "Origin"
 </IfModule>
</IfModule>

超级安全,超级优雅。喜欢它:您不必为资源窃贼/热链接器类型打开服务器带宽。

于 2017-11-18T18:30:41.420 回答
3

在 .htaccess 中设置Access-Control-Allow-Origin时,只有以下工作:

SetEnvIf Origin "http(s)?://(.+\.)?domain\.com(:\d{1,5})?$" CRS=$0
Header always set Access-Control-Allow-Origin "%{CRS}e" env=CRS

我尝试了其他几个建议的关键字Header append, Header set,没有一个按照 SO 上的许多答案中的建议起作用,但我不知道这些关键字是否已过时或对nginx无效。

这是我的完整解决方案:

SetEnvIf Origin "http(s)?://(.+\.)?domain\.com(:\d{1,5})?$" CRS=$0
Header always set Access-Control-Allow-Origin "%{CRS}e" env=CRS
Header merge Vary "Origin"

Header always set Access-Control-Allow-Methods "GET, POST"
Header always set Access-Control-Allow-Headers: *

# Cached for a day
Header always set Access-Control-Max-Age: 86400

RewriteEngine On

# Respond with 200OK for OPTIONS
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
于 2018-10-27T13:33:03.347 回答
3

对于Spring Boot,我发现RegexCorsConfiguration它扩展了官方CorsConfigurationhttps ://github.com/looorent/spring-security-jwt/blob/master/src/main/java/be/looorent/security/jwt/RegexCorsConfiguration.java

于 2018-12-04T10:54:11.343 回答
1

看起来最初的答案是针对 Apache 2.4 之前的。它对我不起作用。这是我必须更改以使其在 2.4 中工作的内容。这适用于yourcompany.com的任何深度的子域。

SetEnvIf Host ^((?:.+\.)*yourcompany\.com?)$    CORS_ALLOW_ORIGIN=$1
Header append Access-Control-Allow-Origin  %{REQUEST_SCHEME}e://%{CORS_ALLOW_ORIGIN}e    env=CORS_ALLOW_ORIGIN
Header merge  Vary "Origin"
于 2018-04-24T20:00:57.870 回答
0

我不得不稍微修改一下Lars 的答案,因为一个孤儿\最终出现在正则表达式中,只比较实际主机(不注意协议或端口),并且我想支持localhost除了我的生产域之外的域。因此,我将$allowed参数更改为数组。

function getCORSHeaderOrigin($allowed, $input)
{
    if ($allowed == '*') {
        return '*';
    }

    if (!is_array($allowed)) {
        $allowed = array($allowed);
    }

    foreach ($allowed as &$value) {
        $value = preg_quote($value, '/');

        if (($wildcardPos = strpos($value, '\*')) !== false) {
            $value = str_replace('\*', '(.*)', $value);
        }
    }

    $regexp = '/^(' . implode('|', $allowed) . ')$/';

    $inputHost = parse_url($input, PHP_URL_HOST);

    if ($inputHost === null || !preg_match($regexp, $inputHost, $matches)) {
        return 'none';
    }

    return $input;
}

用法如下:

if (isset($_SERVER['HTTP_ORIGIN'])) {
    header("Access-Control-Allow-Origin: " . getCORSHeaderOrigin(array("*.myproduction.com", "localhost"), $_SERVER['HTTP_ORIGIN']));
}
于 2018-11-27T16:19:06.180 回答
0

对我来说,我想要一个多域选项,这是我使用的解决方案,使用pythonand flask,

VALID_DOMAINS = 'https://subdomain1.example.com', 'https://subdomain2.example.com'


def handle_request(request):
    origin = request.headers.get('Origin')

    if request.method == 'OPTIONS':
        if origin not in :
            return ''

        headers = {
            'Access-Control-Allow-Origin': origin,
            'Access-Control-Allow-Methods': 'GET',
            'Access-Control-Allow-Headers': 'Content-Type',
            'Access-Control-Max-Age': '3600',
        }
        return '', 204, headers

    return (
        main_function_with_logic(request),
        200,
        {'Access-Control-Allow-Origin': origin,
         ...}
    )

您显然可以将 VALID_DOMAINS 扩展到您想要的任意长度,以及您想要的任何内容(非 https、不同的端口等),然后在请求中检查它。

我更喜欢这个解决方案而不是通配符解决方案,所以这是我在我运行的服务器上的选择。

于 2021-09-28T16:29:24.067 回答
-4

在我的情况下使用角度

在我的 HTTP 拦截器中,我设置

with Credentials: true.

在请求的头部

于 2020-04-05T13:48:41.113 回答