How to set up SSL on EC2 using Nginx and Let’s Encrypt (Certbot)

August 6th, 20256 mins read

How to set up SSL on EC2 using Nginx and Let’s Encrypt (Certbot)

I recently had to explore a bunch of AWS services — S3 buckets, EC2 instances, ElastiCache (Valkey), security groups, and everything in between. And while the learning curve can feel steep at first, it’s surprisingly fun once things start to click.

One of the last things I tackled was setting up SSL on an EC2 instance using Nginx and Let’s Encrypt. I had expected it to be complicated, but it turned out to be way easier than I thought. So I figured I’d write it down — both for anyone trying to do the same, and for future me when I forget the steps again.

In this guide, I explain exactly how I set up free HTTPS on my EC2-hosted project using Certbot and Nginx. If you already have a domain pointing to your server, this will take less than 10 minutes.

Prerequisites

Before we get into installing SSL, make sure you have the following ready:

  • An EC2 instance. For this tutorial, I used an Ubuntu 20.04 server, but any modern Ubuntu version should work.
  • A registered domain. Make sure the domain’s A record points to your EC2 instance's public IPv4 address. You can set this up via your domain registrar.
  • Nginx installed on your EC2. This acts as the web server that Certbot hooks into to handle SSL. You can install it with the commands below:
sudo apt update
sudo apt install nginx -y

Make sure it’s running:

sudo systemctl status nginx

And test it by visiting your server IP in the browser:

http://<your-ec2-public-ip>

You should see the default Nginx welcome page.

  • Finally, to be safe, test that your domain is already routing to EC2:
ping yourdomain.com

or:

curl -I http://yourdomain.com

Install Certbot and set up SSL with Let’s Encrypt

Now that your EC2 instance is live, Nginx is installed, and your domain is pointing correctly, it’s time to install Certbot. But first, what is Certbot?

Certbot is a free, open-source tool that makes it super easy to get HTTPS working on your server. It:

  • Connects with Let’s Encrypt to request a certificate,
  • Configures Nginx automatically,
  • Sets up automatic renewal, so you never have to worry about expiry.

Let’s now install it, alongside the Nginx plugin. First, make sure your package list is up to date:

sudo apt update

Install Certbot and the Nginx plugin:

sudo apt install certbot python3-certbot-nginx -y

Make sure you have python3-certbot-nginx installed. This is crucial for Certbot to work with Nginx.

Now run Certbot and let it handle everything:

sudo certbot --nginx

You’ll be prompted to go through the following steps:

  1. Email Address: For urgent notices like certificate expiration.
  2. Agree to Terms: Say yes (Let’s Encrypt’s ToS).
  3. Subscribe to EFF Email List: Optional — say no if you don’t want updates.
  4. Select Domain(s): Certbot will scan your Nginx config and list available domains. Select the one(s) you want SSL for (use spacebar to select).
  5. Redirect or Not?:
    • Choose the option that redirects all HTTP to HTTPS.
    • This will automatically update your Nginx config to redirect all traffic securely.

If Everything Goes Well… you’ll see something like:

Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/yourdomain.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/yourdomain.com/privkey.pem

And now, if you open your site in the browser using https://yourdomain.com. You should see no “not secure” warning.

To confirm everything is working:

curl -I https://yourdomain.com

You should see HTTP/1.1 200 OK or 301 Moved Permanently:

HTTP/1.1 301 Moved Permanently
Location: https://yourdomain.com/
...

You can also use SSL testing tools like SSL Server Test

Review and Understand Your Nginx Configuration After SSL Setup

After Certbot completes the setup, it automatically edits your Nginx server block to enable HTTPS. But it’s helpful to understand what exactly it changes, especially if you want to tweak or troubleshoot later.

For most Ubuntu setups, the default site config is located at:

/etc/nginx/sites-available/default

Or if you set up a custom domain block, you might find it at:

/etc/nginx/sites-available/yourdomain.com

You can view the config using:

sudo nano /etc/nginx/sites-available/default

After a successful run, you’ll see two server blocks:

  1. Certbot updates it to redirect all HTTP traffic to HTTPS:
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

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

This ensures any visit to http://yourdomain.com is securely redirected to https://.

  1. This is the block where Nginx serves your site with the SSL certificate:
server {
    listen 443 ssl;
    server_name yourdomain.com www.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://localhost:3000;  # or whatever your app runs on
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

From the code above, these are some stuffs you may want to understand:

  • ssl_certificate and ssl_certificate_key point to files managed by Certbot.
  • include /etc/letsencrypt/options-ssl-nginx.conf: This adds strong SSL settings maintained by Certbot.
  • ssl_dhparam: Adds additional security using Diffie-Hellman parameters.
  • The proxy_pass block is commonly used for Node.js, Express, or any backend app running behind Nginx.

If your app runs on a different port (like 4000 or 8080), you may want to update the proxy_pass line accordingly.

If you edit the file manually, always check for syntax issues before restarting:

sudo nginx \-t

Then reload:

sudo systemctl reload nginx

Set Up and Verify Automatic SSL Renewal

Let’s Encrypt certificates are only valid for 90 days, but Certbot is designed to auto-renew them before they expire. That said, it’s smart to understand how the renewal works and test it to avoid downtime.

When you installed Certbot using apt, it also installed a systemd timer (or cron job on older systems) that:

  • Runs twice daily
  • Checks if any cert is near expiry
  • Auto-renews if needed
  • Reloads Nginx if the certificate was renewed

You can simulate a dry run to confirm that auto-renewal works:

sudo certbot renew \--dry-run

If successful, you’ll see something like:

Congratulations, all renewals succeeded. The following certs are not due for renewal yet:
 /etc/letsencrypt/live/yourdomain.com/fullchain.pem expires on 2025-10-01

This dry run is safe and doesn't actually update your certificate — it’s just a test.

To confirm the auto-renewal is scheduled, run:

systemctl list-timers | grep certbot

You should see output like:

NEXT LEFT LAST PASSED UNIT ACTIVATES
Tue 2025-08-06 04:00:00 UTC 8h left Tue 2025-08-05 04:00:00 UTC 16h ago certbot.timer certbot.service

This shows Certbot’s timer is active and running. If for any reason auto-renewal fails or is disabled, you can manually renew your cert with:

sudo certbot renew

And then reload Nginx:

sudo systemctl reload nginx

Let’s Encrypt uses the email you provided during Certbot setup to notify you if:

  • A renewal attempt failed
  • A certificate is about to expire

Make sure you're using a real, monitored email address when prompted by Certbot.