Solving HTTPS Challenges: Nginx, Let's Encrypt, & Cloudflare Deep Dive

Brief Summary

This post details setting up and troubleshooting HTTPS for a multi-domain Nginx server. It addresses redirect loops and 403 Forbidden errors encountered when integrating Let’s Encrypt SSL certificates with Cloudflare. Key areas include Nginx configuration patterns, certificate path corrections, static site serving, and proxying for services like n8n and an IPAM tool.

Initial Setup and Objective

The objective was to secure blankhandle.dev and its subdomains (n8n.blankhandle.dev, ipam.blankhandle.dev) with HTTPS. The setup involved:

Problems Encountered

Two primary issues arose post-SSL setup:

  1. “Too Many Redirects” Error: Occurred when accessing blankhandle.dev via HTTPS, indicating a redirect loop.
  2. 403 Forbidden Error: The static Hugo site returned a 403 error, despite correct file permissions. These issues suggested misconfigurations in Nginx and its interaction with Cloudflare.

Step-by-Step Solution

Step 1: Initial SSL Certificate Acquisition with Certbot

Certbot’s Nginx plugin was used to obtain and configure SSL certificates:

sudo certbot --nginx -d blankhandle.dev -d www.blankhandle.dev -d n8n.blankhandle.dev -d ipam.blankhandle.dev

While Certbot acquired certificates, its initial Nginx configurations often led to redirect loops, likely due to conflicting directives or insufficient awareness of proxy headers (e.g., X-Forwarded-Proto).

Step 2: Nginx Configuration Overhaul for Main Domain (blankhandle.dev)

To resolve the redirect loop, a revised Nginx configuration was applied to /etc/nginx/sites-available/blankhandle.dev.conf, separating HTTP and HTTPS handling and accounting for Cloudflare’s proxying:

# --- HTTP Block: Redirects all HTTP traffic to HTTPS ---
server {
    listen 80;
    listen [::]:80;
    server_name blankhandle.dev www.blankhandle.dev;

    # Redirect all HTTP requests to HTTPS
    return 301 https://$host$request_uri;
}

# --- HTTPS Block: Serves the Hugo website securely ---
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name blankhandle.dev www.blankhandle.dev;

    # Let's Encrypt SSL certificate paths (managed by Certbot)
    ssl_certificate /etc/letsencrypt/live/blankhandle.dev/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blankhandle.dev/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Root directory for the Hugo site's public folder
    root /path/to/your/hugo/site/public; # IMPORTANT: Replace with actual path (e.g., /home/username/my-progress-journal/public)
    index index.html;

    location / {
        # Handle X-Forwarded-Proto for Cloudflare integration
        # Prevents redirect loops if Cloudflare sends HTTP to port 443 (e.g., "Flexible" SSL)
        if ($http_x_forwarded_proto = "http") {
            return 301 https://$host$request_uri;
        }
        try_files $uri $uri/ =404; # Serve files or return 404
    }

    # Optional: Custom error pages
    error_page 404 /404.html;
    location = /404.html {
        root /usr/share/nginx/html; # Default Nginx error page location
        internal;
    }
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html; # Default Nginx error page location
        internal;
    }
}

Key Nginx Configuration Elements:

Step 3: Correcting Certificate Paths for Subdomains

Certbot-generated configurations for subdomains (e.g., n8n.blankhandle.dev, ipam.blankhandle.dev) initially referenced incorrect, subdomain-specific certificate paths. Certbot typically issues a single certificate covering all specified SANs, stored under the primary domain’s directory.

Updated ssl_certificate and ssl_certificate_key paths in n8n.blankhandle.dev.conf and ipam.blankhandle.dev.conf to use the primary domain’s certificate:

ssl_certificate /etc/letsencrypt/live/blankhandle.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blankhandle.dev/privkey.pem;

This ensured Nginx used the correct certificate files for all subdomains.

Step 4: Ensuring Proper Site Root for Hugo

The 403 Forbidden error on the static site occurred because Nginx was defaulting to /var/www/html/ instead of the Hugo site’s build output directory. The deploy-hugo.sh script places generated files into /home/username/my-progress-journal/public/. The root directive in the Nginx HTTPS server block (Step 2) was critical to fix this.

Tags: