How to debug CORS requests with Firebug

CORS FilterFirebug, the indispensable Firefox add-on for serious developers, is great for debugging web apps that make use of the new CORS mechanism for cross-domain requests. The acronym CORS stands for Cross-Origin Resource Sharing and was developed by a group of web activists to address the issue of making browser cross-domain requests in a standards-compliant way. The goal is to make CORS an official W3C standard. As of 2010 most browsers such as FF, IE, Chrome and Safari support it.

My first encounter with CORS was during the development of Json2Ldap, a smart piece of software designed to turn one or more LDAP directories into a web friendly JSON-RPC service. Json2Ldap turned out to be an excellent case for CORS as the web service had to be capable of serving cross-domain requests. Later I span off the server-side CORS code into a project on its own, called CORS Filter. It provides a generic servlet filter which can be retrofitted to any existing Java web app to make it cross-domain XHR capable.

Firebug has an excellent network activity monitor which can be used to reveal the CORS-specific conversation between browser and server during a cross-domain XHR. This is great for understanding how the protocol works, particularly the behaviour of simple/actual vs. pre-flight CORS requests.

To observe a CORS request in action you need a relatively recent version of Firebug (e.g. 1.6). Click on the “Net” tab and then on the “XHR” tab, then launch your cross-domain request.

How to load the Firebug Network Monitor

For a simple/actual CORS request Firebug will then display the following headers:

Simple/actual CORS request

Note the Origin header which the browser inserted to identify the domain from which the HTML page originated. The server checks the Origin value and responds accordingly, by either allowing or denying the request.

Simple CORS response

If the server allowed the origin it replies with an Access-Control-Allow-Origin header that contains a verbatim copy of the Origin header value or * (meaning any origin). The server may also include optional CORS response headers, such as Access-Control-Allow-Credentials (to tell the browser that it accepts cookies) or Access-Control-Expose-Headers (to tell the browser which custom headers are safe to expose to the requesting script).

CORS also has the so-called preflight request which browsers can send using HTTP OPTIONS prior to the actual CORS request to query the CORS settings of a particular HTTP server, such as the supported HTTP methods, custom headers, etc.

Tip: How to simulate a cross-domain request if you have just a single HTTP server to play with: Create a host alias of your web server and reload the requesting page using the alias URL.

Finally, today I learned that the Jetty 7 web server has a CORS filter implementation similar to the one I developed few months ago. On first inspection the only notable difference is that it doesn’t support request tagging and the Access-Control-Expose-Headers response header.

  • Good write up – easy to understand. What happens when authorization is denied ? How does Firebug react ?

  • Good question 🙂

    Here it’s important to understand that the CORS spec doesn’t postulate how the HTTP server should respond if the origin is not in the allow list. This is what the spec says “[then] the request is outside the scope of this specification.”

    In my own filter implementation I simply terminate any further processing of the request and return a HTTP status 403 (Forbidden). And Firebug marks the request as failed quite nicely in bright red colour 🙂

  • Pingback: Java Semantic & Linked Open Data webapps – Part 4 « Ultan O'Carroll ( … uoccou … )()

  • Firebug totally does not pick up CORS requests for me ;(

    Did you do anything special to your firebug to get it to pick up the requests? I’m on 1.6.2…

  • Hi Johnatan,

    No, I didn’t do anything special. I’m also on 1.6.2.

    You might have the Net tab disabled for some reason however. Click on the small arrow on the Net tab and make sure “Enabled” is selected.