Search code examples
javascriptgoogle-chromevue.jscloudflarevue-resource

Ajax response data to vue resource served via cloudflare not being parsed from json in Chrome


I have a site deployed to Heroku with which I use Cloudflare as a proxy.

For some front-end behaviour I'm using Vue with Vue-resource to perform some ajax requests. The responses are in the form of JSON with a Content-Type:application/json header.

Strangely when my scripts are erroring because the responses are not being parsed to a javascript array/object by vue-resource, despite this being the expected behaviour. However, this problem is only occuring when the site is accessed via the Cloudflare URL. If I access it from the Heroku url (e.g. foo-bar-1234.herokuapp.com) all works fine. It also is working fine on my local dev environment (Vagrant, Nginx).

The problem also only occurs in Chrome. It works fine in Safari and Firefox regardless of whether it's Cloudflare or not.

I believe the problem must lie with something that CloudFlare is doing to the response from the Heroku server that is stopping Vue Resource in Chrome from properly parsing the response.

For reference here is the response from the CloudFlare-served request:

cache-control:no-cache
cf-ray:2d2f1980573d3476-LHR
content-encoding:gzip
content-type:application/json
date:Mon, 15 Aug 2016 19:37:10 GMT
server:cloudflare-nginx
set-cookie:sam_session=[redacted]; expires=Mon, 15-Aug-2016 21:37:10 GMT; Max-Age=7200; path=/; HttpOnly
status:200
via:1.1 vegur
x-ratelimit-limit:60
x-ratelimit-remaining:59

vs the heroku-served one:

Cache-Control:no-cache
Connection:keep-alive
Content-Type:application/json
Date:Mon, 15 Aug 2016 19:40:10 GMT
Server:Apache
Set-Cookie:sam_session=[redacted]; expires=Mon, 15-Aug-2016 21:40:10 GMT; Max-Age=7200; path=/; HttpOnly
Transfer-Encoding:chunked
Via:1.1 vegur
X-Ratelimit-Limit:60
X-Ratelimit-Remaining:58

The headers seem pretty similar so I can't think how the responses differ in a way that would cause this...

Any debug suggestions appreciated.

Update: The compiled javascript can be seen here (large file): https://github.com/samarkanddesign/samarkand-web/blob/master/public/js/admin.js

It's a large file so better to look at the repo: https://github.com/samarkanddesign/samarkand-web/tree/master/resources/assets/js

Update 2: example of a vue-resource ajax request executed in both situations:

  1. CloudFlare enter image description here

  2. Direct from Heroku: enter image description here

We can see the in the heroku example that the response data is resolved as an array of objects whilst in the CloudFlare one it's a string.


Solution

  • I believe the problem lies with Vue-Resource and the fact that CloudFlare (SPDY protocol) uses lower-case response headers, whilst Heroku Apache uses capitalised.

    Vue Resource is incorrectly ignoring lowercase headers in its interceptors resulting in it missing parsing the response body.

    This is referenced in this bug issue: https://github.com/vuejs/vue-resource/issues/317

    And a quick way to patch it is via a custom interceptor:

    Vue.http.interceptors.unshift(function(request, next) {
        next(function(response) {
            if(typeof response.headers['content-type'] != 'undefined') {
                response.headers['Content-Type'] = response.headers['content-type'];
            }
        });
    });