End-to-end setup: Nginx on Amazon Linux 2023, Cloudflare DNS/proxying, and Let's Encrypt certificates via Certbot for Cloudflare Full (Strict).

This guide walks through the end-to-end process of deploying a secure Nginx web server on Amazon Linux 2023, using Cloudflare for DNS/proxying, and Let’s Encrypt via Certbot so you can run Cloudflare SSL/TLS in Full (Strict) mode.

Prerequisites

  • An AWS EC2 instance running Amazon Linux 2023
  • SSH access to your instance (typically ec2-user)
  • A domain managed by Cloudflare
  • A public IPv4 address on the instance (or an Elastic IP)
  • AWS CLI installed locally (optional, but helpful for verification)

Phase 1: AWS and DNS configuration

1) Configure AWS security group inbound rules

Your instance must allow inbound traffic for Nginx and for Certbot validation:

  • HTTP: TCP, port 80, source 0.0.0.0/0 and ::/0
  • HTTPS: TCP, port 443, source 0.0.0.0/0 and ::/0
  • SSH: TCP, port 22 (ideally restricted to your IP)

2) Configure Cloudflare DNS

In Cloudflare DNS settings, point your domain to the instance’s public IP:

  • A record
    • Name: @
    • Content: 203.0.113.10 (replace with your EC2 public IP)
  • A record
    • Name: www
    • Content: 203.0.113.10 (replace with your EC2 public IP)

Important for Certbot: set both records to DNS only (grey cloud) for now.

Temporarily disable proxying (alternative to a global pause)

Instead of pausing Cloudflare globally, you can disable the proxy on just the DNS records Certbot needs.

  1. Log in to the Cloudflare dashboard and select your account + domain.
  2. Go to DNS -> Records.
  3. Find the A record for @ and toggle Proxy status to Off (grey cloud / DNS only).
  4. Find the A record for www and toggle Proxy status to Off (grey cloud / DNS only).

After the certificate is issued and Nginx is serving HTTPS correctly, you can switch both records back to Proxied (orange cloud).

When Cloudflare proxying is enabled, Let’s Encrypt validation can fail (common symptoms: timeouts or Cloudflare 52x errors). You can re-enable proxying after you have a valid certificate installed.

Phase 2: Install and start Nginx

SSH into the instance:

ssh ec2-user@203.0.113.10

Install Nginx:

sudo dnf install -y nginx

Start and enable the service:

sudo systemctl start nginx
sudo systemctl enable nginx

Phase 3: Create a simple HTTP site (for validation)

Create a directory to serve content:

sudo mkdir -p /var/www/site

Create a basic test page:

sudo tee /var/www/site/index.html >/dev/null <<'EOF'
<!doctype html>
<html>
  <head><meta charset="utf-8" /><title>It works</title></head>
  <body><h1>Nginx is running</h1></body>
</html>
EOF

Create an Nginx server block:

sudo tee /etc/nginx/conf.d/site.conf >/dev/null <<'EOF'
server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    root /var/www/site;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}
EOF

Validate and reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

At this point, visiting http://example.com/ should show your test page.

Phase 4: Install Certbot and issue Let’s Encrypt certificates

Install Certbot (Nginx plugin):

sudo dnf install -y certbot python3-certbot-nginx

Request certificates:

sudo certbot --nginx -d example.com -d www.example.com

During the interactive prompts, Certbot can optionally configure HTTP-to-HTTPS redirects for you.

Verify that Nginx config is still valid:

sudo nginx -t

Phase 5: Configure Nginx server blocks (HTTP redirect + /blog/ alias)

After Certbot finishes, it will have added a working HTTPS server block and the required ssl_certificate directives.

If you want:

  • All HTTP to redirect to HTTPS
  • The HTTPS root (/) to redirect to /blog/
  • Content served from /var/www/site under /blog/

edit your Nginx config (for example /etc/nginx/conf.d/site.conf) so it has this shape.

Important:

  • Keep the Certbot-managed SSL directives that Certbot added (the ssl_certificate / ssl_certificate_key / include lines).
# --- HTTP Server Block (Redirects all traffic to HTTPS) ---
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

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

# --- HTTPS Server Block (Content + /blog alias) ---
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;

    server_name example.com www.example.com;

    # >>>>> Certbot-managed SSL directives will be here <<<<<

    ssl_protocols TLSv1.2 TLSv1.3;

    location = / {
        return 301 https://$host/blog/;
    }

    location ^~ /blog/ {
        alias /var/www/site/;
        index index.html;
        try_files $uri $uri/ =404;
    }

    root /usr/share/nginx/html;

    error_page 404 /404.html;
    location = /404.html {
        root /var/www/site;
    }

    access_log /var/log/nginx/site.access.log;
    error_log  /var/log/nginx/site.error.log warn;
}

Validate and reload:

sudo nginx -t
sudo systemctl reload nginx

Phase 6: Re-enable Cloudflare proxy and set Full (Strict)

  1. In Cloudflare DNS, switch your records back to Proxied (orange cloud).
  2. In Cloudflare SSL/TLS, set encryption mode to Full (Strict).

Phase 7: Automatic renewal

Certbot installs a systemd timer on many distros. Check it:

sudo systemctl is-enabled certbot-renew.timer

If it is disabled:

sudo systemctl enable certbot-renew.timer
sudo systemctl start certbot-renew.timer

Verify it is scheduled:

sudo systemctl list-timers | grep certbot

Dry-run a renewal:

sudo certbot renew --dry-run

If the dry run succeeds, you are set: your origin has a real Let’s Encrypt certificate and Cloudflare can safely run Full (Strict) end-to-end.

FAQ

Why does Certbot fail when Cloudflare proxying is enabled?
When Cloudflare is Proxied (orange cloud), HTTP/HTTPS traffic can be terminated or filtered by Cloudflare before it reaches your origin, which can break Let's Encrypt validation (especially HTTP-01). Temporarily set the DNS records to DNS only (grey cloud), issue the certificate, verify Nginx serves HTTPS correctly, then re-enable proxying.
What Cloudflare SSL mode should I use with a Let's Encrypt certificate on the origin?
Use Full (Strict). Full (Strict) requires a valid certificate on the origin server, which a Let's Encrypt certificate satisfies. Avoid Flexible because it encrypts only between the browser and Cloudflare, not between Cloudflare and your server.
How do I verify Certbot renewal is working on Amazon Linux 2023?
Confirm the systemd timer is enabled (certbot-renew.timer), then run a dry run (certbot renew --dry-run). If the dry run succeeds, automatic renewal should work as scheduled.

Welcome to The infinite monkey theorem

Somewhere a monkey just typed Shakespeare in TypeScript. Be the first to read the masterpieces (and the hilarious misfires) landing on the blog.

Subscribe to The infinite monkey theorem

We fling fresh posts—no banana peels attached—straight to your inbox.