I recently saw a post about using ngrok for testing webhooks using a local development server. They charge $5 a month for reserved domains, and other features. This can be done with the power of ssh, nginx and Let's Encrypt. The only catch is, you need your own server.

Benefits of this approach

  • You get control over your data, it's not going through a 3rd party
  • Many developers already have access to a web server, so it won't cost anything
  • You can setup unlimited domains with this approach, making it better and cheaper than ngrok
  • You can setup other things on the server and learn more about linux

Setup

I'll be using DigitalOcean to create a cheap $5 server. If you already have a linux server running somewhere, preferably Ubuntu, you can follow along.

Let's go into the DigitalOcean dashboard and create a new $5/mo droplet. If you already have a server with open ssh installed, you can skip this and get all the benefits of ngrok for free. I'll be using an Ubuntu 19.04 droplet with ssh key authentication. Setting up ssh keys is out of scope for this guide. Use password authentication if you must, although ssh keys are the most secure, and can be automatically installed to a server created with DigitalOcean.

Install some tools

Now that we created a server, let's ssh into it:

ssh root@165.22.63.11

You should definitely disable root logins, password authentication, and other security measures, but we will not worry about that in this guide.

sudo apt-get update
sudo apt-get install nginx -y
sudo apt install certbot -y
sudo apt-get install -y python-certbot-nginx

Setup Nginx

Let's create an nginx configuration for our site. I'll use vim:

vim /etc/nginx/sites-enabled/server.conf

The contents will be:

upstream tunnel {
  server 127.0.0.1:8888;
}

server {
  server_name tutorial.zach.codes;
  
  location / {
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    
    proxy_pass http://tunnel;
  }
}

This configuration tells nginx we have a tunnel running on port 8888. Our local computer will send traffic through this port using ssh after we finish setup. Next, we need to change tutorial.zach.codes to be a domain name that you own, and is pointed to our server. Using DigitalOcean, you can go to the Networking section and add DNS records for any domains hosted with them. This will vary based on your hosting provider:

After this record is created, and you changed server_name tutorial.zach.codes; to your domain name, save the file.

Setup HTTPS

Next up, we can run one easy command to get https working on our server:

sudo certbot --nginx -d tutorial.zach.codes

Once again replacing the domain with your own. It'll ask for your email so that it can notify you of any issues or renewals. After it completes, you will now have the correct configuration in the nginx file we first created to serve our tunnel over https.

The really neat part is that certbot automatically adds a cron job to renew this certificate with the command we just ran. So there is no work to do with renewing anything.

We're done!

Now we can try out our freshly created, self-hosted solution to forwarding our traffic over the internet. I have a development server running on port 3000. The command below will forward it to our server:

ssh -R 8888:localhost:3000 root@tutorial.zach.codes

Once again, replacing the domain with your own.

This command tells your server that any requests to 8888 on it should go to port 3000 on our local machine. The command is a little hard to memorize, so I've added this function to my ~/.zshrc file:

forward () {
  ssh -R 8888:localhost:$1 root@tutorial.zach.codes
}

Anytime I want to expose something publicly, I can type forward 3000 in my terminal. Incredibly simple, and since I already pay for a small server that hosts this website, I can do everything ngrok can do for free.

I hope you enjoyed this article. This stuff hasn't always been so easy to do on your own. Recent Let's Encrypt updates have made this process really simple.

Once again, I recommend setting up a firewall like csf, disabling root logins, and blocking all ports except 80 and 443 if you plan to keep this server up and running.