4

Strange problem here. I use FullCalendar to initiate an ajax request to an endpoint on my server. Endpoint is:

https://my_website/events/?start=2019-03-31&end=2019-05-12&_=1555698739056

Note that it is explicitly https. However, when I initiate a request (that is, when Fullcalendar initiates a request), I get a 301 and a redirect to a non-https endpoint:

http://my_website/events?start=2019-03-31&end=2019-05-12&_=1555698739056

which fails because the page is loaded over https.

enter image description here

The endpoint works fine - when i load it into the browser I get the expected json output (via https). There are other ajax requests happening on this page that work correctly, and I successfully do the exact same thing with Fullcalendar elsewhere on this site (to another endpoint). It's just this one scenario that is behaving unexpectedly.

Probably noteworthy is this sits in a docker container behind nginx reverse proxy / load balancer; site config is pretty simple:

upstream docker {
    server localhost:8701;
    server localhost:8702;
  }

server {
    server_name my_website;
    location / {
      proxy_pass http://docker;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
      # proxy_set_header                HTTP_Country-Code $geoip_country_code;
        proxy_pass_request_headers      on;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/my_website/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/my_website/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($host = my_website) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    server_name my_website;
    return 404; # managed by Certbot

}

And nginx log of the request is like this:

134.124.11.91 - - [19/Apr/2019:13:49:49 -0500] "GET /events/?start=2019-04-28&end=2019-06-09&_=1555699678658 HTTP/1.1" 301 0 "https://my_website" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"

Does anyone see something I'm missing that would be causing this strange 301 redirect to a non-https endpoint?

WillardSolutions
  • 2,316
  • 4
  • 28
  • 38
  • There's nothing in your nginx config that could cause this redirect. Probably it's your endpoint initiating redirect because request is proxied via HTTP. To confirm, I recommend adding $upstream_status variable to log_format. If it will be equal to 301, then I am right. It probably can be solved by using https in proxy_pass or changing endpoint code. – SuddenHead Apr 22 '19 at 10:30

2 Answers2

5

Problem

This would appear to happen because you're not using canonical URLs, whereas your backend is enforcing such URLs through these 301 redirects, whereas it is not actually aware of the canonical address scheme.


Solution

  • Best solution would be to fix your front-end code to always use canonical URLs. E.g., in the examples you provide, there's a difference in whether or not a trailing slash is present in your API endpoint.

  • You should probably configure your back-end to be properly aware that it's being accessed through https, e.g., add something like the following next to all your other proto_set_header directives in your nginx that terminates https and passes the traffic to the backend:

    proto_set_header    X-Forwarded-Proto   $scheme;
    

Other thoughts

  • Another solution would be to configure http://nginx.org/r/proxy_redirect to properly recognise the local Location headers returned by your backend, and convert them on-the-fly as needed; however, the prior two options are probably a better approach in your situation.
cnst
  • 25,870
  • 6
  • 90
  • 122
  • Thanks, but 1) I don't see where there's a difference between trailing slashes and non-trailing slashes in my question; what endpoint doesn't have a trailing slash? 2) explicitly settting the x-forwared-proto header did not help; and 3) I've played with proxy_redirect a bit, but to no avail. Any specific advice on how to use it? – WillardSolutions Apr 23 '19 at 02:23
  • 1
    (1), Your question has `https://my_website/events/?` and then `http://my_website/events?` — the original request has a trailing slash, the redirect doesn't. (3), the first parameter for proxy_redirect would probably be `http://my_website/`, the second, `https://my_website/`; of course, this depends on your original question being correct, as the first part has to be an exact match to what's produced by your backend in its `Location` replies. – cnst Apr 23 '19 at 06:22
1

I am not entirely sure, because your screenshots are a bit conflicting, but here goes:

In your inspector screenshot we see one request to https (which is cancelled) and one to http (which is blocked due to mixed protocols). The hint is in the cancelled one, being cancelled means there was no redirect there, but that the browser decided it no longer needed the request. There are several previous questions that had similar issues, see here and here two examples.

One reason why a request gets canceled is if the button/input/link you use to change the date of your FullCalendar is not just doing an ajax request but also doing a second http request (because of a wrapping form, href, etc). You have not included your html and JavaScript of your FullCalendar implementation, so I do not know this for sure, but check that you do not have a form surrounding the input element, or if you wrote your own event handler do the following.

function(e){
  e.preventdefault(); 

  .... // your date switching code here 

  return false;
}

If you use a link with an onclick attribute, make sure you add ...(yourcode);return false; at the end.

Important: If my theory is right, this means the line from your log doesn't actually correspond to the same thing we see in the inspector and is in fact a redirect from HTTP to HTTPS, not the other way around. This would be hard to see because nginx does not include the request protocol in the log by default.

Tom
  • 233
  • 1
  • 6