You’ve built your Node.js app, and it’s running perfectly on your EC2 instance. But how do you let the world see it? Visiting http://your-server-ip:3000 isn’t professional, scalable, or secure.
This is where Nginx (pronounced "Engine-X") comes in. Think of it as a smart traffic director or a doorman for your server. It listens for public web traffic on the standard ports (80 for HTTP and 443 for HTTPS) and forwards it to your Node.js app running privately on its own port. This setup is called a reverse proxy.
In this guide, we'll walk through setting up Nginx and then securing your site with a free SSL certificate from Let's Encrypt using a tool called Certbot. By the end, you'll have a secure, production-ready setup at https://yourdomain.com.
An Ubuntu/Debian-based EC2 instance.
Your Node.js app is already on the server and running (e.g., using PM2 on port 3000).
You own a domain name and can edit its DNS records.
Part 1: Setting Up Nginx as a Reverse Proxy
Step 1: Install Nginx
First, let's update your server's package list and install Nginx.
sudo apt update
sudo apt install -y nginxNow, let's enable Nginx to start automatically whenever the server reboots and start it right away.
sudo systemctl enable nginx
sudo systemctl start nginxYou can check its status with sudo systemctl status nginx. You should see it’s active (running)
Step 2: Clear the Default Nginx Site
Nginx comes with a default "Welcome to Nginx" page enabled. We don't need this, as it can conflict with our own configuration. Let's disable it by removing its symbolic link.
sudo unlink /etc/nginx/sites-enabled/defaultStep 3: Create Your Own Nginx Configuration File
Now, we'll create a new configuration file for our application. We'll name our file kayno but you can name it after your project.
sudo nano /etc/nginx/sites-available/kaynoThis opens a text editor. Paste the following configuration inside.
server {
listen 80;
server_name _; # Use an underscore for now
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}
}Press Ctrl+X, then Y, then Enter to save and exit.
Step 4: What Does That Configuration Mean? (Line by Line)
Let's quickly break down that file in plain English:
server { ... }: Defines a set of rules for a specific website.
listen 80;: Nginx should listen for incoming traffic on port 80 (standard HTTP).
server_name _;: For now, this rule block will apply to any domain name or IP address. We'll change this later.
location / { ... }: These rules apply to all requests (e.g., yourdomain.com/, yourdomain.com/about, etc.).
proxy_pass http://127.0.0.1:3000;: This is the most important line. It tells Nginx to forward the request to the application running on localhost (the same machine) at port 3000.
proxy_set_header ...: These lines pass along important information (like the original domain name and the user's real IP address) to your Node.js app. This ensures your app behaves correctly and that your logs are accurate. The Upgrade and Connection headers are essential for supporting WebSockets.
Step 5: Enable Your New Site
We've created the configuration, but Nginx doesn't know about it yet. We need to create a symbolic link (like a shortcut) from sites-available to sites-enabled.
sudo ln -s /etc/nginx/sites-available/kayno /etc/nginx/sites-enabled/Step 6: Test and Reload Nginx
Before applying the changes, it's always smart to ask Nginx to check your configuration for any syntax errors.
sudo nginx -tIf you see syntax is ok and test is successful, you're good to go! Now, restart Nginx to apply the new configuration.
sudo systemctl restart nginxSuccess! At this point, if you visit your EC2 instance's public IP address in your browser, you should see your Node.js application instead of the default Nginx page.
Part 2: Connecting Your Domain and Adding HTTPS
Step 7: Point Your Domain to Your EC2 Instance
Now let's switch from using an IP address to a professional domain name.
Go to your domain registrar (GoDaddy, Namecheap, Google Domains, etc.).
Find the DNS management settings.
Add an A record:
Host/Name: Put your subdomain here (e.g., app) or @ if you're using the root domain.
Value/Points to: Your EC2 instance's Public IP address.
TTL: Leave the default.
It might take a few minutes (or longer) for DNS changes to propagate across the internet.
Step 8: Update Your Nginx Config for the Subdomain
Now, tell Nginx to only respond to requests for your specific domain.
sudo nano /etc/nginx/sites-available/kaynoChange the server_name line from _ to your actual domain/subdomain.
server {
listen 80;
server_name app.example.com; # 👈 Change this to your domain
location / {
# ... the rest of the file stays the same
}
}Save the file, then test and restart Nginx again.
sudo nginx -t
sudo systemctl restart nginxNow, http://app.example.com should load your app. We're almost there, but notice it's still "Not Secure." Let's fix that.
Step 9: Install Certbot
Certbot is a fantastic tool that automatically gets a free SSL certificate from Let's Encrypt and configures Nginx for you.
sudo apt update
sudo apt install -y certbot python3-certbot-nginxStep 10: Let Certbot Work Its Magic
This one command does all the heavy lifting. It reads your Nginx configuration, sees the server_name you defined, and requests a certificate for it.
sudo certbot --nginx -d app.example.comCertbot will ask you a few questions:
Your email address (for renewal notices).
To agree to the terms of service.
If you want to share your email (optional).
Crucially, it will ask if you want to redirect HTTP traffic to HTTPS. Choose option 2 (Redirect). This is highly recommended for security.
Step 11: Test Your Secure Site and Auto-Renewal
That's it! Open your browser and navigate to https://app.example.com. You should see your application loading with a padlock icon in the address bar, indicating a secure connection.
Certbot also sets up a cron job to automatically renew your certificate before it expires. You can test that the renewal process is working with a dry run:
sudo certbot renew --dry-runIf the dry run is successful, you are all set!
Conclusion
Congratulations! You have successfully configured a production grade web server. Your Node.js application is now running behind a powerful Nginx reverse proxy, accessible via a custom domain name, and fully secured with an auto renewing HTTPS certificate. This robust setup not only makes your application professional and secure but also opens the door to more advanced configurations like load balancing and caching in the future.