Can I set an Access-Control-Allow-Origin header to all pages on a domain and its subdomains?

TRiG picture TRiG · May 27, 2014 · Viewed 16.7k times · Source

I am trying to use a webfont which I am legally permitted to use, but not to distribute. I am hosting the font files on a separate domain used for static content. The two domains are unrelated (one is not a subdomain of the other). Let’s say that the site using the webfont is example.com and the site hosting it is example.net.

I tried this in the .htaccess file on example.net

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

However, this allows the font to work only on the homepage of example.com. I tried again:

    Header set Access-Control-Allow-Origin "example.com/*"

Now the font works on example.com everywhere except the homepage, which is (of course) not what I wanted.

I can’t find any documentation for this header. What I really want is to allow all pages on example.com and www.example.com (or, for good measure, *.example.com). Is there a simple way to do this? I’m guessing that the header takes some kind of regex.

Looking for documentation I found,

  • a lot of stuff about how this header interacts with ajax,
  • a lot of brief notes saying that it’s necessary for webfonts (at least in Firefox).

I did not find any documentation about the syntax of the header itself, or how to specify variants of a domain.

Based on an answer to a related question, I tried this:

<FilesMatch "\.(ttf|otf|eot|woff|svg)$">
    <IfModule mod_headers.c>
        SetEnvIf Origin "http(s)?://(www\.)?(example.com)$" AccessControlAllowOrigin=$0$1
        Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
        Header set Access-Control-Allow-Credentials true
    </IfModule>
</FilesMatch>

My understanding was that this would set a separate header for each request, allowing each requesting page individually to use the font. Checking with Firebug, though, it looks like the header is always http://example.com, both on the homepage and elsewhere. Nonetheless, this works, but leaves me confused. A related question shows that a similar setup was not working for someone else. His question suggests that for him, it actually was sending a different header for each requesting page, and that HTTP 304 Not Modified responses were therefore breaking. His solution was to add an always directive to the .htaccess, but for me that resulted in HTTP 500 errors.

As it is now, it’s working, and I think will continue to work when example.com switches to HTTPS (as it will shortly). However, I can’t help but feel that it’s overly complicated. It’s setting the same header each time, but is using complex pattern-matching to do so. Also, while I haven’t yet any problems with HTTP 304 Not Modified responses (in fact, I’ve not yet seen any such responses: the browser simply doesn’t request the font files at all until I clear the cache), I worry that I might see them in future.

Answer

mb21 picture mb21 · Jul 1, 2014

As stated by the CORS spec, you can have only one domain in the Access-Control-Allow-Origin header (or * or null).

So yes, you need to set the header differently depending on what domain is requesting the site. That's why the apache config snippet you posted tries to match on the Origin header of the request with this regex:

http(s)?://(www\.)?(example.com)$

The $ matches end of string. So this regex will match requests from http://www.example.com, http://example.com and their https equivalents, but not example.com/bla. This should be fine since the Origin header of the request, as well as the Access-Control-Allow-Origin header of the response should contain only the host and not the subpages.

So when you're on the page http://example.com/about-us, the browser will send something like the following request to get the font from http://cdn.net/myfont.otf:

GET /myfont.otf HTTP/1.1
Host: http://cdn.net
Origin: http://example.com

There the server will patternmatch on the Origin header and return with:

Access-Control-Allow-Origin: http://example.com