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, source0.0.0.0/0and::/0 - HTTPS: TCP, port
443, source0.0.0.0/0and::/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:
Arecord- Name:
@ - Content:
203.0.113.10(replace with your EC2 public IP)
- Name:
Arecord- Name:
www - Content:
203.0.113.10(replace with your EC2 public IP)
- Name:
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.
- Log in to the Cloudflare dashboard and select your account + domain.
- Go to
DNS->Records. - Find the
Arecord for@and toggle Proxy status to Off (grey cloud / DNS only). - Find the
Arecord forwwwand 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.10Install Nginx:
sudo dnf install -y nginxStart and enable the service:
sudo systemctl start nginx
sudo systemctl enable nginxPhase 3: Create a simple HTTP site (for validation)
Create a directory to serve content:
sudo mkdir -p /var/www/siteCreate 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>
EOFCreate 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;
}
}
EOFValidate and reload Nginx:
sudo nginx -t
sudo systemctl reload nginxAt 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-nginxRequest certificates:
sudo certbot --nginx -d example.com -d www.example.comDuring the interactive prompts, Certbot can optionally configure HTTP-to-HTTPS redirects for you.
Verify that Nginx config is still valid:
sudo nginx -tPhase 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/siteunder/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/includelines).
# --- 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 nginxPhase 6: Re-enable Cloudflare proxy and set Full (Strict)
- In Cloudflare
DNS, switch your records back to Proxied (orange cloud). - 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.timerIf it is disabled:
sudo systemctl enable certbot-renew.timer
sudo systemctl start certbot-renew.timerVerify it is scheduled:
sudo systemctl list-timers | grep certbotDry-run a renewal:
sudo certbot renew --dry-runIf 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.

