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

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

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

/ / * .mywebsite.example: * / *


当前回答

我不得不稍微修改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']));
    }

其他回答

我需要一个只有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'),

当在.htaccess中设置Access-Control-Allow-Origin时,只有以下几种有效:

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

我尝试了其他几个建议的关键字头追加,头集,没有工作建议在Stack Overflow上的许多答案,虽然我不知道这些关键字是否过时或对nginx无效。

以下是我的完整解决方案:

SetEnvIf Origin "http(s)?://(.+\.)?domain\.example(:\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]

当从“cookie域”(www.domain.example)读取字体时,我们在静态的“无cookie”域上也遇到了类似的问题,这篇文章是我们的英雄。看这里:我如何修复“丢失跨源资源共享(CORS)响应头”webfont问题?

对于复制/粘贴-r类型(并给一些道具),我从所有的贡献中拼凑起来,并将其添加到站点根目录的。htaccess文件的顶部:

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

超级安全,超级优雅。喜欢它:你不必开放你的服务器带宽给资源小偷/热链接者类型。

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

原来的答案只用于历史目的!!


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

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

    # 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变量设置为您的根域,它将在Access-Control-Allow-Origin: response头值中将Origin: request头值回显给客户端,如果它与您的域匹配。

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

我在回答这个问题,因为公认的答案做不到跟随

正则表达式分组会影响性能,但这是不必要的。 不能匹配主域,只适用于子域。

例如:它不会为http://mywebsite.example发送CORS头,而为http://somedomain.mywebsite.example/工作

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"

要启用你的网站,你只是把你的网站在mywebsite的地方。在上面的Apache配置的例子。

允许多个站点:

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

部署后验证:

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

curl -X GET -H "Origin: http://site1.example" --verbose http://site2.example/query