Nginx Reverse proxy not working with Gunicorn over LetsEncrypt SSL

I’ve EC2 Ubuntu 18.04 Server with Python Flask framework running on Gunicorn application server with Nginx reverse proxy listening on port 80 & 443. I’ve added LetsEncrypt for the domain using certbot.

The website works fine without ssl. With LetsEncrypt ssl configuration on Nginx the server fails to load the page. My previous supervisor and Nginx configuration without ssl support is as follows and Nginx works with gunicorn with no issues.

server {
        listen 80;
        server_name example.com www.example.com;

        location /static {
                alias /home/ubuntu/myapp-backend/myapp/static;
        }

        location / {
                proxy_pass http://localhost:8000;
                include /etc/nginx/proxy_params;
                proxy_redirect off;
        }
}
[program:myapp]
directory=/home/ubuntu/myapp-backend
command=/home/ubuntu/myapp-backend/venv/bin/gunicorn -w 5 run:app
user=ubuntu
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stderr_logfile=/var/log/myapp/myapp.err.log
stdout_logfile=/var/log/myapp/myapp.out.log

When I change the Nginx configuration to include LetsEncrypt support while listening on port 80 & 443, the website doesn’t show up… It shows indefinite 301 redirects requests.

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://www.example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    return 301 https://www.example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name www.example.com;

    ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location /static {
            alias /home/ubuntu/myapp-backend/myapp/static;
    }

    location / {
            proxy_pass http://localhost:8000;
            include /etc/nginx/proxy_params;
            proxy_redirect off;
    }
}

When I load the site example.com , it redirects to https:\\www.example.com . However the website homepage doesn’t load or show any response from server/Nginx. When I login to the server and run curl -v localhost:8000 , the the gunicorn works fine.

curl -v localhost:8000
* Rebuilt URL to: localhost:8000/
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 500 INTERNAL SERVER ERROR
< Server: gunicorn/19.9.0
< Date: Sat, 28 Sep 2019 14:14:47 GMT
< Connection: close
< Content-Type: text/html; charset=utf-8
< Content-Length: 27911
<
<!doctype html>
...

There was earlier Stackoverflow question on gunicorn over ssl with ssl parameters which can be added to supervisor config. I believe the error must be with on Nginx configuration on port 443 or Gunicorn supervisor config info. If you could have a look, I would appreciate it.

Okay, but what does it show for:

curl -ikL https://www.example.com

Obscuring your domain is a bit of a problem because your description of the symptoms doesn't match how the nginx config is written. Makes it hard to help you.

Thanks @_az for the reply. Basically its simple flask app with gunicorn server and Nginx reverse proxy with all the installation done using apt-get install command.

I’ve kept all the default settings in Nginx. Only changes added are ‘/etc/nginx/sites-enabled/myapp’ and /etc/supervisor/conf.d/myapp.conf as described above.

Here is the curl response you’ve requested. It currently shows Cloudflare Error 521 Web server is down message. In `Cloudflare A Name DNS entry for ‘@’ & ‘www’ points to EC2 public ip address.

curl -ikL https://www.mysite.com
HTTP/2 521
date: Sun, 29 Sep 2019 03:01:01 GMT
content-type: text/html; charset=UTF-8
set-cookie: __cfduid=d292a37eec7eba82e4329ec8c1ea247151569726061; expires=Mon, 28-Sep-20 03:01:01 GMT; path=/; domain=.mysite.com; HttpOnly
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
set-cookie: cf_ob_info=521:51dab5ca2d27245a:IAD; path=/; expires=Sun, 29-Sep-19 03:01:31 GMT
set-cookie: cf_use_ob=443; path=/; expires=Sun, 29-Sep-19 03:01:31 GMT
expires: Thu, 01 Jan 1970 00:00:01 GMT
cache-control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
pragma: no-cache
server: cloudflare
cf-ray: 51dab5ca2d27245a-IAD

<!DOCTYPE html>
...

According to Cloudflare, 521 is connection refused.

So from here you can try figure out why Cloudflare can’t connect to your server, or you can try disable the Cloudflare proxy (the “orange cloud” next to your A records) to try make this simpler to debug.

Hi again @_az,

Thank you for your quick solution. I disabled the Cloudflare proxy (orange cloud). The website is working like a charm. Now it’s directly serving from Gunicorn via Nginx.

I tried to enabled Cloudflare proxy again and run the curl command. It results in forever 301 redirect loop.

 curl -ikL mysite.com
HTTP/1.1 301 Moved Permanently
Date: Sun, 29 Sep 2019 04:55:53 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: __cfduid=d5a2d673c0876362539d66e9b1dd996b01569732952; expires=Mon, 2 8-Sep-20 04:55:52 GMT; path=/; domain=.mysite.com; HttpOnly
Location: https://www.mysite.com/
Server: cloudflare
CF-RAY: 51db5e0c1d27c1cf-IAD

HTTP/2 301
date: Sun, 29 Sep 2019 04:55:53 GMT
content-type: text/html
set-cookie: __cfduid=da17d09cb93f98eca943e5aee2e6d34a51569732953; expires=Mon, 2 8-Sep-20 04:55:53 GMT; path=/; domain=.mysite.com; HttpOnly
location: https://www.mysite.com/
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi /beacon/expect-ct"
server: cloudflare
cf-ray: 51db5e0cfe465703-IAD

HTTP/2 301
date: Sun, 29 Sep 2019 04:55:53 GMT
content-type: text/html
set-cookie: __cfduid=da17d09cb93f98eca943e5aee2e6d34a51569732953; expires=Mon, 2 8-Sep-20 04:55:53 GMT; path=/; domain=.mysite.com; HttpOnly
location: https://www.mysite.com/
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi /beacon/expect-ct"
server: cloudflare
cf-ray: 51db5e0d3e675703-IAD
...

The only DNS settings I’ve is two 'A Records for ‘@’ and ‘www’ value pointing at the server public IP address. I’ve enabled development mode in Cloudflare Cache settings. Still, enabling proxy in Cloudflare results in 301 loop.

I’ve googled for the issue. I’ve found the Cloudflare support page for (Troubleshooting redirect loop.)[https://support.cloudflare.com/hc/en-us/articles/115000219871-Troubleshooting-redirect-loop-errors] It seems to be some sort of config/technical issue with Cloudflare.

I’ve changed the SSL/TLS settings in Cloudflare from default flexSSL to Full SSL. Now website is working fine with Cloudflare proxy on and Cloudflare SSL on FullSSL.

Thanks again. Cheers!

If you have a valid Let's Encrypt certificate on your origin, use "Full (strict)" instead of "Full".

"Full" uses HTTPS but it isn't secure because it doesn't validate the certificate. "Full (strict)" does.

Hi @mnordhoff, thanks for the advice. I will sure use “Full (strict)” mode. I really wish if there was some blog/article type resources for this type of information for developers.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.