i am dain
Navigate back to the homepage

Getting Cross-origin resource sharing with complex jQuery AJAX requests

Daniel Demmel
January 18th, 2013 · 2 min read

I’m working on an HTML5 desktop application which needs to load some HTML text from a server, but not through an existing API. So I had to find a solution which can be set up without too much backend work and make it possible for non-technical people to easily update the content. I’m assuming that the easiest setup will be using an existing CMS system with a few custom server settings to enable cross-domain communication. It’s also a security sensitive application, so the content will need to be protected with basic authorisation at least.

Well, let me get things straight: CORS which is more than the simplest GET request is not easy!

While trying to set up and end to end proof of concept to make sure this will work and to be able to advise with setup I ran into several blockers, so I’m going to go through them step by step.

Setting up the jQuery AJAX request

For simple use cases all you need to do to is fire off a simple GET request:

2 type: "GET",
3 url: "https://www.html5rocks.com/en/tutorials/file/xhr2/",
4 success: function(response) {
5 var article = $(response)
6 .find("article")
7 .html();
8 $("body").html(article);
9 }

If the server you’re hitting is sending back the header Access-Control-Allow-Origin with the value * you’re all set.

However what I ended up with is far more complicated, as doing anything with custom headers (which included authorisation) you have to first make a so called preflight request. This is an OPTIONS request which only asks the server if it supports all the headers needed for the ‘real’ request which follows.

So let’s first see the full AJAX request:

2 type: "GET",
3 url: "https://www.mydomain.com/myFancyContent.php",
4 headers: {
5 MyCustomHeader: "important information"
6 },
7 xhrFields: {
8 withCredentials: true
9 },
10 username: "myuser",
11 password: "mypassword",
12 success: function() {
13 console.log("success");
14 },
15 error: function() {
16 console.log("failure");
17 }

JQuery has some helpers for authorisation, so the easiest way to get it working is setting xhrFields.withCredentials to true and passing username and password as separate parameters, this way we don’t have to worry about the intricacies of creating the auth hash ourselves.

Another convenience property is headers which you can use to inject any custom headers you might need, without having to manually do it with beforeSend.

Apache config

Another big stumbling block for me was to get the settings for Apache right.

The very first thing you need to do is making sure mod_headers is enabled, which can be as simple as:

1sudo a2enmod headers
2sudo /etc/init.d/apache2 force-reload

Once that’s done, you can either edit your main httpd.conf or apache.conf files, or just create an .htaccess file in the folder you want to enable CORS in.

These are the settings I ended up with after a lot of experimentation:

1SetEnvIf Origin ^(.*)$ ORIGIN_DOMAIN=$0
2<Files "*">
3 Header add Access-Control-Allow-Origin %{ORIGIN_DOMAIN}e
4 Header add Access-Control-Allow-Methods "GET, OPTIONS"
5 Header add Access-Control-Allow-Headers "Authorization, X-Requested-With, Content-Type, Origin, Accept"
6 Header add Access-Control-Allow-Credentials "true"

The first line makes sure that Origin is echoed back, because as it turns out once you have some custom headers a simple * in Access-Control-Allow-Origin won’t do any more. You can do some URL filtering here if you want, but this setup makes sure that even file:/// works which is great for development.

In Access-Control-Allow-Methods we make sure that OPTIONS will work, and with Access-Control-Allow-Headers and Access-Control-Allow-Credentials we set everything up for a basic authorisation handshake.

Annoyingly none of these worked on the simple HTML file I uploaded, until as a desperate idea after a few hours of futile attempts I tried renaming it to PHP extension which made Apache suddenly apply all of them.

If that wasn’t enough, the unregistered development HTTPS security certificate also blocked my attempts to get things working. If you open any URL on the server you’re using and accept the certificate this problem will go away, otherwise the options are to install / import the certificate on a system level or to start the browser with some flags like --allow-running-insecure-content for Chrome.

Hope this will help someone else facing the same problems!


Hero image: http://en.kremlin.ru/events/president/news/52898/photos/45602

More articles from dain

Setting up rmate with Sublime Text for remote file editing over SSH

Instead of terminal editors with steep learning curve, this guide helps you to tunnel Sublime Text in to your server for remote editing. With sudo!

September 2nd, 2012 · 1 min read

Safely access your home NAS anywhere with an OpenVPN server on GCP

How to run a low cost OpenVPN server on GCP to access your NAS anywhere even if your home internet is on a shared IP.

August 8th, 2021 · 3 min read
© 2012–2021 dain
Link to $https://twitter.com/daaainLink to $https://github.com/daaainLink to $https://www.facebook.com/danieldemmelLink to $https://www.linkedin.com/in/danieldemmel/