Access-Control-Allow-Origin wildcard subdomains, ports and protocols
Use @Noyo's solution instead of this one. It's simpler, clearer and likely a lot more performant under load.
ORIGINAL ANSWER LEFT HERE FOR HISTORICAL PURPOSES ONLY!!
I did some playing around with this issue and came up with this reusable .htaccess
(or httpd.conf
) solution that works with 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>
Just set the ACCESS_CONTROL_ROOT
variable at the top of the block to your root domain and it will echo the Origin:
request header value back to the client in the Access-Control-Allow-Origin:
response header value if it matches your domain.
Note also that you can use sub.mydomain.example
as the ACCESS_CONTROL_ROOT
and it will limit origins to sub.mydomain.example
and *.sub.mydomain.example
(i.e. it doesn't have to be the domain root). The elements that are allowed to vary (protocol, port) can be controlled by modifying the URI matching portion of the regex.
I'm answering this question, because the accepted answer can't do following
- regex grouping is a performance hit, which is not necessary.
- cannot match primary domain and it only works for sub domain.
For example: It won't send CORS headers for http://mywebsite.example
while works for 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"
To enable for your site, you just put your site in place of mywebsite.example
in the above Apache Configuration.
To allow Multiple sites:
SetEnvIf Origin "http(s)?://(.+\.)?(othersite\.com|mywebsite\.example)(:\d{1,5})?$" CORS=$0
Validating After deploying:
The following curl response should have the "Access-Control-Allow-Origin" header after the change.
curl -X GET -H "Origin: http://site1.example" --verbose http://site2.example/query
Based on DaveRandom's answer, I was also playing around and found a slightly simpler Apache solution that produces the same result (Access-Control-Allow-Origin
is set to the current specific protocol + domain + port dynamically) without using any rewrite rules:
SetEnvIf Origin ^(https?://.+\.mywebsite\.example(?::\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"
And that's it.
Those who want to enable CORS on the parent domain (e.g. mywebsite.example
) in addition to all its subdomains can simply replace the regular expression in the first line with this one:
^(https?://(?:.+\.)?mywebsite\.example(?::\d{1,5})?)$
.
Note: For spec compliance and correct caching behavior, ALWAYS add the Vary: Origin
response header for CORS-enabled resources, even for non-CORS requests and those from a disallowed origin (see example why).
The CORS spec is all-or-nothing. It only supports *
, null
or the exact protocol + domain + port: http://www.w3.org/TR/cors/#access-control-allow-origin-response-header
Your server will need to validate the origin header using the regex, and then you can echo the origin value in the Access-Control-Allow-Origin
response header.