How to Test Your Localhost on Cloud Using Nginx

A practical guide to exposing your local development server to the cloud using Nginx as a reverse proxy, enabling remote testing and collaboration.
How to Test Your Localhost on Cloud Using Nginx

Introduction

Testing your local development environment from a cloud server can be incredibly useful. Whether you need to share your work with a remote team member, test webhooks from external services, or simply verify your application works in a production-like environment, exposing your localhost to the cloud is a common requirement.

In this guide, we'll walk through how to use Nginx as a reverse proxy to securely expose your local development server to a cloud instance.

Why Test Localhost on Cloud?

Before diving in, let's understand the use cases:

  • Webhook Testing: Services like Stripe, GitHub, or Slack require a public URL to send webhook events
  • Remote Collaboration: Share your development progress with team members without deploying
  • Cross-Device Testing: Test your application on different devices connected to the internet
  • Demo Purposes: Quickly showcase your work to clients without a full deployment

Prerequisites

Before starting, ensure you have:

RequirementDescription
Cloud ServerA VPS with a public IP (AWS EC2, DigitalOcean, Linode, etc.)
SSH AccessAbility to SSH into your cloud server
Domain (Optional)A domain pointing to your cloud server for HTTPS
Local AppYour application running on localhost (e.g., localhost:3000)

Architecture Overview

Here's how the setup works:

[Internet] → [Cloud Server:80/443] → [Nginx Reverse Proxy] → [SSH Tunnel] → [Your Localhost:3000]
  1. User accesses your cloud server's public IP or domain
  2. Nginx receives the request and forwards it through an SSH tunnel
  3. The tunnel connects to your local machine
  4. Your local server responds, and the response travels back

Step 1: Set Up SSH Reverse Tunnel

First, create an SSH reverse tunnel from your local machine to the cloud server. This tunnel will forward traffic from the cloud server to your localhost.

# On your local machine
ssh -R 8080:localhost:3000 user@your-cloud-server-ip

Explanation:

  • -R 8080:localhost:3000 - Binds port 8080 on the remote server to your local port 3000
  • user@your-cloud-server-ip - Your cloud server credentials

Make the Tunnel Persistent

For a more stable connection, use autossh:

# Install autossh
sudo apt install autossh  # Ubuntu/Debian
brew install autossh      # macOS

# Create persistent tunnel
autossh -M 0 -f -N -R 8080:localhost:3000 user@your-cloud-server-ip

Step 2: Configure Your Cloud Server

Enable GatewayPorts (Required)

Edit the SSH daemon configuration on your cloud server:

sudo nano /etc/ssh/sshd_config

Add or modify this line:

GatewayPorts yes

Restart SSH service:

sudo systemctl restart sshd

Step 3: Install and Configure Nginx

Install Nginx

# Ubuntu/Debian
sudo apt update
sudo apt install nginx

# CentOS/RHEL
sudo yum install nginx

# Start Nginx
sudo systemctl start nginx
sudo systemctl enable nginx

Configure Nginx as Reverse Proxy

Create a new Nginx configuration file:

sudo nano /etc/nginx/sites-available/localhost-proxy

Add the following configuration:

server {
    listen 80;
    server_name your-domain.com;  # Or use your server's public IP

    location / {
        proxy_pass http://127.0.0.1:8080;
        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_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 86400;
    }
}

Enable the Configuration

# Create symbolic link
sudo ln -s /etc/nginx/sites-available/localhost-proxy /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx

Secure your connection with a free SSL certificate:

# Install Certbot
sudo apt install certbot python3-certbot-nginx

# Obtain certificate
sudo certbot --nginx -d your-domain.com

# Auto-renewal is configured automatically

Your Nginx config will be automatically updated to:

server {
    listen 443 ssl;
    server_name your-domain.com;

    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

server {
    listen 80;
    server_name your-domain.com;
    return 301 https://$server_name$request_uri;
}

Complete Example: Testing a Next.js App

Let's walk through a complete example of exposing a Next.js development server.

On Your Local Machine

# Start your Next.js app
cd my-nextjs-app
npm run dev
# App running at http://localhost:3000

# In another terminal, create the SSH tunnel
ssh -R 8080:localhost:3000 ubuntu@203.0.113.50

On Your Cloud Server (203.0.113.50)

# Nginx config at /etc/nginx/sites-available/nextjs-dev
server {
    listen 80;
    server_name dev.myapp.com;

    location / {
        proxy_pass http://127.0.0.1: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;
    }

    # Next.js HMR (Hot Module Replacement) support
    location /_next/webpack-hmr {
        proxy_pass http://127.0.0.1:8080/_next/webpack-hmr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Result

Your local Next.js app is now accessible at http://dev.myapp.com!

Troubleshooting Common Issues

IssueSolution
Connection refusedEnsure SSH tunnel is active and GatewayPorts is enabled
502 Bad GatewayCheck if your local app is running on the correct port
Timeout errorsIncrease proxy_read_timeout in Nginx config
WebSocket issuesEnsure Upgrade and Connection headers are properly set

Debug Commands

# Check if tunnel port is listening
sudo netstat -tlnp | grep 8080

# Test local proxy connection
curl http://127.0.0.1:8080

# Check Nginx error logs
sudo tail -f /var/log/nginx/error.log

# Check Nginx access logs
sudo tail -f /var/log/nginx/access.log

Security Considerations

When exposing your localhost to the internet, keep these security practices in mind:

  1. Use HTTPS: Always encrypt traffic with SSL/TLS
  2. Limit Access: Use Nginx's allow and deny directives to restrict IP access
  3. Authentication: Add basic auth for sensitive applications
  4. Firewall Rules: Configure ufw or iptables to limit open ports
  5. Temporary Exposure: Only keep the tunnel active when needed

Example: Adding Basic Authentication

# Create password file
sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd devuser

Update Nginx config:

location / {
    auth_basic "Development Server";
    auth_basic_user_file /etc/nginx/.htpasswd;

    proxy_pass http://127.0.0.1:8080;
    # ... rest of proxy settings
}

Alternative: Using a Single Script

Create a convenient script to automate the process:

#!/bin/bash
# expose-localhost.sh

LOCAL_PORT=${1:-3000}
REMOTE_PORT=${2:-8080}
SERVER="user@your-cloud-server-ip"

echo "🚀 Exposing localhost:$LOCAL_PORT to cloud server port $REMOTE_PORT"
echo "📡 Access your app at: http://your-domain.com"
echo "Press Ctrl+C to stop"

autossh -M 0 -N -R $REMOTE_PORT:localhost:$LOCAL_PORT $SERVER

Usage:

chmod +x expose-localhost.sh
./expose-localhost.sh 3000 8080

Summary

Exposing your localhost to the cloud using Nginx and SSH tunnels is a powerful technique for development and testing. Here's a quick recap:

  1. Create SSH Tunnel: Use ssh -R to forward remote port to localhost
  2. Configure Nginx: Set up reverse proxy to forward traffic to the tunnel port
  3. Secure with HTTPS: Use Let's Encrypt for free SSL certificates
  4. Add Authentication: Protect your development server from unauthorized access

This setup gives you the flexibility to test webhooks, share your work remotely, and validate your application in a production-like environment—all without deploying your code.

Happy coding! 🎉