2 min read

getJSON AJAX mixed protocol error on HTTPS with nginx? Try this...

getJSON AJAX mixed protocol error on HTTPS with nginx? Try this...
Man shouting, pulling hair

The page at ‘https://domain.com/service1/’ was loaded over HTTPS, but is submitting data to an insecure location at ‘http://domain.com/service1/’: this content should also be submitted over HTTPS.

Does this look familiar? A few weeks ago a bug was discovered on my latest side project, GroupVite: since enabling SSL on our server the nice straight-to-S3 AJAX image uploader I’d crafted had gone wonky. Investigation on the production server resulted in the above error in my developer console.

Here is the JavaScript code in which the error originated:

var get_signed_request = function(file){
    $.getJSON('/service1', function(results) {
        upload_file(file, results.signed_request, results.url);
    }).fail(function() {
        alert('Something went wrong, can you please try that image again?');
    });
};

What the hell is going on there? In nginx I was redirecting properly, pointing all normal http requests to https. I discounted nginx as the problem point based on that (and on the strength of my general queasiness around unix sysops work), figuring there was something weird going on at browser level, or even an SSL cert problem.

Many console.log(window.location.protocol) calls later revealed that my JavaScript was fully aware of the protocol the page it was on was running at (HTTPS). Explicitly putting the full URL in to the $.getJSON function call had no effect. My SSL cert passed all the checks I could think of.

By this stage I was pulling my hair out. With no integration server set up yet for GroupVite, I had to merge dev into master and push it up to GitLab, then pull master down on my server and restart the Gunicorn service using Supervisor every single damn time I tried something new in the code.

Reluctantly, after much googling, I had to concede that the problem was somewhere in my nginx config for GroupVite. Cue the sigh that could be heard around the world.

Here was my config the way it stood, with the redirect for normal port 80 HTTP and the reverse proxy set up for the Gunicorn service:

server {
        listen 80;

        server_name groupvite.io www.groupvite.io;

        return 301 https://$host$request_uri;
}

server {
        listen 443;
        ssl on;
        ssl_certificate /var/www/groupvite/SSL.crt;
        ssl_certificate_key /var/www/groupvite/server.key;

        server_name groupvite.io www.groupvite.io;

        location / {
                proxy_pass http://localhost:8001;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
        }
        location /static {
                alias  /var/www/groupvite/static/;
        }
}

Once I dug in there, I quickly realised that the problem had to be somewhere in the reverse proxy section.

With painstaking investigation and experimenting line by line, I finally hit on the crucial thing left out of my reverse proxy configuration (in the location / {...} server rule):

location / {
        ...
        proxy_set_header X-Forwarded-Proto $scheme;
        ...
}

Resulting in the current config of:

server {
        listen 80;

        server_name groupvite.io www.groupvite.io;

        return 301 https://$host$request_uri;
}

server {
        listen 443;
        ssl on;
        ssl_certificate /var/www/groupvite/SSL.crt;
        ssl_certificate_key /var/www/groupvite/server.key;

        server_name groupvite.io www.groupvite.io;

        location / {
                proxy_pass http://localhost:8001;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                add_header Front-End-Https on;
                proxy_redirect off;
        }
        location /static {
                alias  /var/www/groupvite/static/;
        }
}

(I added in a few other nice lines that turned up over the course of my digging).

So there you have it. If you’re operating an SSL-ified site on nginx and having some trouble with AJAX requests on HTTPS-enabled pages, give the above config tweaks a go. Hopefully they’ll do the trick for you as they did me.