You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
How to configure HTTPS with Lets Encrypt, Nginx reverse proxy, Express and Node
Have a Node app ready for production.
Create an app.js file in your project directory:
constexpress=require('express');constpath=require('path');constapp=express();// Allow dotfiles - this is required for verification by Lets Encrypt's certbotapp.use(express.static(path.join(__dirname,'build'),{dotfiles: 'allow'}));app.get('*',function(req,res){res.sendFile(path.join(__dirname,'build','index.html'));});app.listen(3000);
Configure Nginx at /etc/nginx/sites-available/default
# Default server configuration
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
# Virtual Host/SSL/Reverse proxy configuration for example.com
server {
# Listen on both HTTP and HTTPS - between Nginx and Express the traffic is HTTP but this is not a major
# security concern as both services are on the same box
listen 80;
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
include snippets/ssl-example.com.conf;
include snippets/ssl-params.conf;
server_name example.com www.example.com;
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_cache_bypass $http_upgrade;
}
# Allow location for Acme challenge - you also might need to allow 'dotfiles' in Express (see next section)
location ~ /.well-known {
allow all;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
}
}
Restart Nginx and start your Express server (I recommend PM2 to manage the process):
sudo apt-get update && sudo apt-get upgrade -y
sudo apt-get install nginx -y
sudo systemctl status nginx # To check the status of nginx
sudo systemctl start nginx # To start nginx
sudo systemctl enable nginx
sudo rm /etc/nginx/sites-available/default
sudo vim /etc/nginx/sites-available/default
Then, copy and paste this into the file you opened in vim, and replace "<your IP address>" with either a domain you own if you have one, or just your public IP address (we'll talk about getting a domain below)
mkdir myapp
cd myapp
npm install express
vim app.js
and copy and paste this into the file you opened with vim:
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send("Hello World!");
});
app.listen(8080, 'localhost');
Now run:
node app.js
and your app is running! You won't see any output, but you'll notice you won't exit from the program until you do ctrl+C and stop the server. You should be able to see the words "Hello World" when you visit your server's public IP.
Now, to link your public IP to an actual domain, you will need to go to a domain provider, like namecheap.com or domain.com and buy one. They can cost under $10 a year, and you only ever need to buy one to actually get infinite subdomains (like, if you buy alex.com, you can make websites like meet.alex.com or whos.alex.com).
Assuming you used namecheap, you should now follow this guide to add an A name record to your domain. A name records basically direct a request to your domain to go to an IP address you specific. Set an A name record from "@" (which represents when people visit your website with no subdomain, so like, "alex.com"), that goes to your public IP address. Set the TTL to be as short as possible, so it updates more frequently. Make sure you use your PUBLIC ip address for your AWS instance, not your private one, as web domains can only access your public one.
Now, go back to your nginx config with:
sudo vim /etc/nginx/sites-available/default
and change it so you include your URL in the "server_names" argument list if you didn't already:
server {
listen 80;
server_name <your IP address> <your domain>; # REMEMBER TO REPLACE WITH YOUR DOMAIN. DO NOT INCLUDE http:// or https://
location / {
proxy_pass http://localhost:8080;
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;
}
}
Now run the steps you should run every time you want to deploy new nginx config changes:
sudo nginx -t
sudo systemctl reload nginx
and if you visit your website at your domain, you should be able to see "Hello World" instead of a 404 error. If it doesn't work initially, try using "ctrl+shift+R" (basically a hard refresh to avoid reviewing a cached version of your website) to reload the page.
Now that you have a basic HTTP website, let's add HTTPS.
Run the certifying process! (This will allow Let's Encrypt to know that you control this server). Replace "my domain url" with your domain url.
sudo certbot --nginx -d <my domain url>
If the process asks if you'd like to redict HTTP traffic to HTTPS, I would generally say yes, you should, just so your users don't accidentally use HTTP.
If that succeeded, now when people visit your website, they will have encryption! Awesome!! You're a top web developer now. Go make something cool!
I prefer to use cmder on Windows. It provides most comment linux commends out of the box.
Create a node.js project
# install express-generator
npm install -g express-generator
# create new express project# use hogan.js and sass
express --view=hjs --css=sass node-app
# install packagecd node-app
yarn
# start server
yarn start
Testing the server by browsing http://localhost:3000 using Chrome
Or curl http://localhost:3000 from terminal
Create Vagrant virtual box
# create a folder to save vagrant vm profile
mkdir vagrant
# install Vagrant box# https://app.vagrantup.com/boxes/
vagrant box add ubuntu/xenial64
vagrant init ubuntu/xenial64
# start vm
vagrant up
vagrant status
# login vm
vagrant ssh
Edit the generated Vagrantfile, change language mode to Ruby
# add a user called "luke"
sudo adduser luke
# switch to user
sudo su - luke
whoami
# create SSH connectioncd~
mkdir .ssh
# 700 owner can read, write and execute
chmod 700 .ssh
# do NOT change the file name, `authorized_keys`
touch .ssh/authorized_keys
# owner can read and write
chmod 600 .ssh/authorized_keys
Copy the public key to .ssh/authorized_keys file
From local machine, on Windows
cat C:\Users\<username>\.ssh\id_rsa.pub
On Mac
cat ~/.ssh/id_rsa.pub
From vm
# paste your public key using vim
vim ~/.ssh/authorized_keys
Login as new user
ssh luke@<ip_address>
Create a compressed file to upload our project
# tar compress, zip, file <output_filename>.tar.gz files...
tar czf test-app.tar.gz app.js bin package.json public routes views
module.exports={apps: [{name: 'node-demo',script: './bin/www',watch: false,// pm2 kills and restarts your application without indication!instances: 'max',exec_mode: 'cluster',env: {PORT: 3001,NODE_ENV: 'production'},env_dev: {DEBUG: 'node-demo:server',PORT: 3001,NODE_ENV: 'development'}}]}
Start pm2
pm2 start pm2.config.js
# Check current running processes
pm2 list
Run pm2 as a service
# generate startup script# provide user and home path
pm2 startup systemd -u luke --hp /home/luke
Returns
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u luke --hp /home/luke
Save your pm2 configuration!
pm2 save
Test pm2 by rebooting the server
sudo reboot
Automated deployment
# create deploy script
touch deploy.sh
# give it execute right
chmod +x deploy.sh
# run it
./deploy.sh
In deploy.sh file
#!/bin/bashecho"Creating zip file and copying to remove..."
tar czf app.tar.gz bin public routes views app.js package.json pm2.config.js yarn.lock
scp app.tar.gz [email protected]:~
rm app.tar.gz
echo"Login to remove..."# the command afterward will be executed on remote
ssh [email protected]<< 'ENDSSH'pm2 stop node-demopm2 delete node-demoecho "pm2 is stopped"rm -rf appmkdir apptar xf app.tar.gz -C apprm app.tar.gzcd appyarnecho "Starting pm2..."pm2 start pm2.config.jspm2 saveENDSSH
Setup nginx as a Reverse Proxy (listening on Port 80)
Only root can access Port 80, so the deploy user doesn't have access.
What can nginx do?
HTTPS
Listening on Port 80, reverse proxy to node.js server
Serve static files
Load balancing
nginx setup
Login remote as root
sudo apt-get update && sudo apt-get install nginx -y
cd /etc/nginx/sites-available
sudo vim default
In /etc/nginx/nginx.conf
The # of cores your CPU is running
worker_processes: auto;
Check the # of cores
cat /proc/cpuinfo
Add proxy configuration into server block in /etc/nginx/sites-available/default file
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
proxy_pass http://localhost:3001;
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;
}
}
To map another url, e.g. http://dev.mynodeapp.com/app2