Fixing stuff and maybe other things.

Getting CORS to work with Apache

Ok, if you’re reading this, I’m assuming you know what CORS means, so I won’t tell you that it stands for Cross Origin Resource Sharing. Or maybe I just told you.

Anyway, you want to enable it on your Apache server. Maybe, like me, you’re building an API-based web app. So you need some JavaScript to pull data from a remote server. (Or even, like in my case, a different subdomain on the same physical server.) It’s easy in Node.js, so it shouldn’t be hard in Apache.

So you google “apache enable cors”. The first result is from Wow, how relevant! Sounds so legit! And it says all you have to do is throw this somewhere:

Header set Access-Control-Allow-Origin "*"

So you put it in your httpd.conf file or .htaccess and boom done.


Except then you try it. And Firebug is all like: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at This can be fixed by moving the resource to the same domain or enabling CORS.

And Chrome says: XMLHttpRequest cannot load https://howdare.youthink.thiswouldbe.easy. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘’ is therefore not allowed access.

But… you did it right. You did just what it said you should do. You even googled it a few more times and everyone says the same thing—Just that one line of code and you’re supposed to be done! You’re supposed to be kicking back with some nachos now! Chrome even says that the header is there, for crying out loud!

Screenshot from Chrome


Yeah, no.

Disappointed face
Turns out there’s a friggin metric crapton more to it than that. I won’t go into all the details here, but there’s a lot. What I will do is give you a list of quick and dirty things to try.

Enable mod_headers

There’s a module that allows Apache to add things to the request/response headers. You’ll need that. Near the top-ish of your httpd.conf file, look for…

#LoadModule headers_module modules/

(Mine was on line 115 in my Apache 2.4 setup.)

If yours has that hash/number/octothorpe/# sign at the beginning, remove it. As with any change to httpd.conf, you’ll need to restart Apache for this to take effect.


First, just putting that line in my .htaccess wasn’t working. I could see the header in Chrome (image above) but it was apparently being ignored for who-the-crap-knows-why. Adding “always” before “set” seemed to correct this. I also moved it from my .htaccess to httpd.conf, just above my virtual hosts section.

Header always set Access-Control-Allow-Origin "*"

You’ll also see different versions of this out there in the wild, some where it says to use header set, and others where it says header add. Both will work, but set is safer in this case because add can add multiple headers, which according to the CORS documentation is not allowed.

Depending on your situation, that might do it for you. But probably not. So…

Other headers

Try adding these three:

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, Accept-Encoding"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"

The 2nd one determines what headers your requesting server (the one trying to make the remote call) is allowed to send. You likely don’t need all of those, but I left in a bunch for the sake of example.

The 3rd one is super important. It determines what kind of RESTful calls your app is allowed to make. Again, you probably don’t need all of them. In fact, if you’re only doing GET requests, that’s the only one you need. But if you want to POST, then you need OPTIONS, too. More on that below.

GET works, but POST doesn’t: welcome to “preflights”

I won’t go into details here, but I will say that POSTs are different than GETs beyond the obvious ways. One way being that with POST, browsers do what’s called a “preflight” check. Basically, it sends a request before your actual POST, checking to see if it’s allowed to do what it’s trying to.

This was very confusing at first, because my GET calls were working fine, but when I tried to do a POST call, Chrome: 1. showed it as an OPTIONS request; and 2. returned 404 (or 400 or 403 or 500). The heck?

What’s happening is likely that your server is trying to respond to that OPTIONS preflight as a normal page fetch. It’s literally trying to serve up a page. In my case, the URL in question was a PHP script that was expected POST data and wasn’t getting it, so it barfed up a 403 error.

Making PHP return 200 OK for preflights

I had to make a script called blank.php, which contained nothing but some ranty comments. Then, over in .htaccess, I added this:

RewriteEngine On                  
RewriteRule ^(.*)$ blank.php [QSA,L]

What’s happening here is, whenever an OPTIONS request comes in (line 2), it redirects any-and-all of them to blank.php (line 3). Blank.php is, well, blank, so it returns an HTTP status of 200 OK. The browser then takes this as a successful attempt to discover what its OPTIONS are, learns from the other headers (Allow-Origin, Allow-Headers, Allow-Methods) that it’s allowed to do a POST, and then finally sends the real, actual, honest POST that you’ve been trying to get it to do this entire freakin’ time.

Hope that helped

Depending on your environment/needs, that might not be the end for you. If so, you have my sympathy. Either way, I hope this at least gets you a few steps closer.

Posted under: Fixing Stuff, Web Design


#1 by max on 05.20.15 at 12:27 pm

i lol’d

#2 by Travis on 06.25.15 at 12:15 pm

Have I told you lately that I love you?

#3 by Dan on 06.28.15 at 5:22 pm

Oh dude, I’ve been 2 days trying to work this out, it was the pre-flight shit bothering.
Thanks so much.
BTW. Also gotta load the rewrite module in apache for this to work

#4 by Gabriel on 08.20.15 at 9:27 am

Im facing a similar issue with my WP installation but every time i run my page after inserting the CORS in the htaccess file it gets overwritten back to default.
Any clue?

#5 by Toastmaster General on 08.20.15 at 10:12 am

The first thing I’d try is to make sure you’re not putting your changes inside the WordPress zone of your htaccess. (Between “# BEGIN WordPress” and “# END WordPress”). If that doesn’t work, you could try making it read-only except for when you need to change it.

#6 by Monica on 09.02.15 at 7:40 pm

Hi there.. I a fighting with this problem. I followed completely your post and I still do not get it working.. 🙁 Any help is welcome guys.. that is crazy with CORS.

#7 by fredy on 09.10.15 at 9:44 pm

Great blog. It resolved my issues (i have no experience in apache admin).
i have: load balancer -> reverse proxy -> wordpress
Might help others: the entries in .htaccess should be submitted on the host that gets the javascript call (if i’m not wrong, it worked for me, meaning on the reverse proxy site)

#8 by Shawn on 09.24.15 at 6:41 pm

added to my sites-enabled with “always” fixed right up!

#9 by Vincent on 09.30.15 at 8:44 am

Thanks a lot !! saved me tons of time !

#10 by Paul on 10.07.15 at 3:57 pm

Great post, CORS is a pain and this was really helpful. Thanks!!

#11 by Matthias on 01.16.16 at 12:39 pm

This information was a big help. Thanks.

#12 by Ops on 02.22.16 at 1:53 pm

Thanks, this was helpful

#13 by Matthew on 05.21.16 at 1:46 am

Oh man, finding this after struggling for two days with this nonsense, I just had to laugh. LOL

#14 by Adi on 06.12.16 at 1:51 pm

Thanks a lot!!!!!!!!!!!!!
you are too good

#15 by Ken Riley on 06.15.16 at 11:33 am

Thank you!!!!! ALWAYS

#16 by Hadrien on 06.16.16 at 7:24 am

Hi guys,
Sorry we are in the middle of a Hackaton, doing a Ionic app with a back in PHP. good idea, right ?
we succeeded the get method but we struggle with the post. where shall we put the blank.php file ?


#17 by Raja on 06.21.16 at 7:37 am

Thank a lot. Instead of creating blank.php, we can do this :

RewriteRule ^(.*)$ $1 [R=200,L,E=HTTP_ORIGIN:%{HTTP:ORIGIN}]]

#18 by MNicemen on 06.23.16 at 10:10 am

Thx a lot for this great post.
I still have issues (I mean, javascript error returning 404) using CORS to a HTTP Apache server that protect its cgi-bin with AuthConfig (Basic type) mechanisms.

Any hints? I’m stuck on that 🙁

Thx in advance !!!

#19 by Ben0bi on 08.03.16 at 10:39 am

After discovering “the whole web” I found your page. THANKS man, this saved my life.

Also, from another post I have a little other rewrite rule, which is shorter than that from Raja – I don’t know if it needs the E-variable. It works fine for me:

RewriteRule ^(.*)$ $1 [R=200,L]

And for your entertainment: I needed that to load remote images (same server but other port) into a pixi.js-WebGL-screen, because I “outsourced” all uploaded media, so that several websites at different server-locations could use them.

Leave a Comment