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.

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:
| Requirement | Description |
|---|---|
| Cloud Server | A VPS with a public IP (AWS EC2, DigitalOcean, Linode, etc.) |
| SSH Access | Ability to SSH into your cloud server |
| Domain (Optional) | A domain pointing to your cloud server for HTTPS |
| Local App | Your 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]
- User accesses your cloud server's public IP or domain
- Nginx receives the request and forwards it through an SSH tunnel
- The tunnel connects to your local machine
- 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 3000user@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
Step 4: Add HTTPS with Let's Encrypt (Recommended)
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
| Issue | Solution |
|---|---|
| Connection refused | Ensure SSH tunnel is active and GatewayPorts is enabled |
| 502 Bad Gateway | Check if your local app is running on the correct port |
| Timeout errors | Increase proxy_read_timeout in Nginx config |
| WebSocket issues | Ensure 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:
- Use HTTPS: Always encrypt traffic with SSL/TLS
- Limit Access: Use Nginx's
allowanddenydirectives to restrict IP access - Authentication: Add basic auth for sensitive applications
- Firewall Rules: Configure
ufworiptablesto limit open ports - 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:
- Create SSH Tunnel: Use
ssh -Rto forward remote port to localhost - Configure Nginx: Set up reverse proxy to forward traffic to the tunnel port
- Secure with HTTPS: Use Let's Encrypt for free SSL certificates
- 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! 🎉