Setting CORS (cross-origin resource sharing) on Apache with correct response headers allowing everything through
July 30, 2014Once in a while you need to make a cross-domain request from Javascript, this is something the browser very much dislikes. I’m no expert on CORS, and I feel that all the documentation on it is pretty bad. But I thought, “Anybody can google”, and so I did.
I started off with just adding the Access-Control-Allow-Origin header in my Apache configuration, thinking that it’ll solve my problems. I also decided to set it on wildcard, allowing anything to request resources.
# In my virtualhost config Header set Access-Control-Allow-Origin "*"
Restart server, reload page, and I was greeted with the normal cross-domain error.
# Chrome developer tools OPTIONS http://my.example.dev/setting/1316 405 (Method Not Allowed) # Firefox developer tools Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://my.example.dev/setting/1316. This can be fixed by moving the resource to the same domain or enabling CORS.
I kept adding config flags until it looked something like this
Header set Access-Control-Allow-Origin "*" Header set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT" Header set Access-Control-Max-Age "1000" Header set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token"
I still got the same error. At this point I started to look more into the error, “OPTIONS http://my.example.dev/setting/1316 405 (Method Not Allowed)” or “OPTIONS Method Not Allowed”, I looked under the network tab and noticed that the request method was “OPTIONS”, so I started digging and as if I understood correctly, this is a “pre-flight” request, basically asking the server for the CORS headers, but without data.
The API I was communicating with was not configured to handle OPTIONS request, nor was it supposed to. I decided I didn’t want to hack on the API code instead I wanted to try and circumvent this through Apache.
I added a small rewrite to the virtualhost configuration, basically responding with a 200 SUCCESS on every OPTIONS request.
Header set Access-Control-Allow-Origin "*" Header set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT" Header set Access-Control-Max-Age "1000" Header set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token" # Added a rewrite to respond with a 200 SUCCESS on every OPTIONS request RewriteEngine On RewriteCond %{REQUEST_METHOD} OPTIONS RewriteRule ^(.*)$ $1 [R=200,L]
Progress! It still didn’t work, but I got a new error;
# Chrome developer tools XMLHttpRequest cannot load http://my.example.dev/setting/1316. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://mydevsite.dev' is therefore not allowed access. # Firefox developer tools Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://my.example.dev/setting/1316. This can be fixed by moving the resource to the same domain or enabling CORS.
So by looking at the errors, it looks like the “Access-Control-Allow-Origin” response header is missing, but I’ve already added it to my config with a wildcard domain.
The problem occurs because I don’t cleanly respond with the RewriteRule, I actually redirect the request to a 200 SUCCESS which means earlier response flags were removed. All I had to do was set the “Always” flag on my “Header set” rule.
So for my final try, which was successful, my configuration looked like this;
# Always set these headers. Header always set Access-Control-Allow-Origin "*" Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT" Header always set Access-Control-Max-Age "1000" Header always set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token" # Added a rewrite to respond with a 200 SUCCESS on every OPTIONS request. RewriteEngine On RewriteCond %{REQUEST_METHOD} OPTIONS RewriteRule ^(.*)$ $1 [R=200,L]
And voilà, everything is playing nicely. Hopefully this can help another unfortunate soul trying to navigate through the pitfalls of CORS.
Please note that I haven’t used this config on a production server, just on local development servers, allowing wildcard domains for CORS will create security issues on your site!
Comments on this subject