DEV Community

jobayer ahmed
jobayer ahmed

Posted on

Turn your local machine into a web server with Cloudflare Tunnel

πŸ“‹ Table of Contents

What is Cloudflare Tunnel?

Cloudflare Tunnel (formerly Argo Tunnel) creates a secure, encrypted tunnel between your local machine and Cloudflare's global network. It allows you to expose your localhost to the internet without:

  • Opening ports on your router
  • Exposing your home IP address
  • Complex firewall configurations
  • Port forwarding setup

How it works:

[Your Local App] β†’ [Cloudflared Client] β†’ [Cloudflare Edge] β†’ [Users Worldwide]
Enter fullscreen mode Exit fullscreen mode

All connections are outbound-only from your machine, making it inherently more secure than traditional hosting methods.

Why Use Cloudflare Tunnel?

πŸ”’ Security Benefits

  • βœ… No exposed home IP address
  • βœ… Automatic SSL/TLS encryption
  • βœ… Built-in DDoS protection
  • βœ… No inbound firewall ports needed
  • βœ… Zero-trust network access

πŸš€ Practical Advantages

  • βœ… Share local work instantly with clients/teams
  • βœ… Host personal projects without traditional hosting costs
  • βœ… Test webhooks requiring public URLs
  • βœ… Deploy from behind restrictive firewalls/NAT
  • βœ… Access from anywhere without VPN

πŸ’° Cost Effective

  • βœ… Free for personal use
  • βœ… No VPS or hosting fees required
  • βœ… Enterprise-grade infrastructure at no cost

Prerequisites

Before you begin, ensure you have:

  • [ ] A Cloudflare account (Sign up free)
  • [ ] A domain name added to Cloudflare
  • [ ] A local application running (any port)
  • [ ] Basic command line knowledge
  • [ ] Administrator/sudo access on your machine

Installation

macOS

Using Homebrew:

brew install cloudflare/cloudflare/cloudflared
Enter fullscreen mode Exit fullscreen mode

Linux

Debian/Ubuntu:

wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
Enter fullscreen mode Exit fullscreen mode

RHEL/CentOS:

wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
sudo rpm -i cloudflared-linux-x86_64.rpm
Enter fullscreen mode Exit fullscreen mode

Arch Linux:

yay -S cloudflared
Enter fullscreen mode Exit fullscreen mode

Windows

  1. Download the latest .exe from GitHub Releases
  2. Place it in a directory in your PATH (e.g., C:\Windows\System32)

Verify Installation

cloudflared --version
Enter fullscreen mode Exit fullscreen mode

Expected output: cloudflared version 20XX.X.X

Quick Start

Step 1: Authenticate

cloudflared tunnel login
Enter fullscreen mode Exit fullscreen mode

This opens your browser to authorize cloudflared with your Cloudflare account. Select your domain when prompted.

Step 2: Create a Tunnel

cloudflared tunnel create my-tunnel
Enter fullscreen mode Exit fullscreen mode

Output:

Tunnel credentials written to /Users/username/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json
Created tunnel my-tunnel with id a7b3c4d5-e6f7-8901-2345-6789abcdef01
Enter fullscreen mode Exit fullscreen mode

πŸ“ Save the tunnel ID - you'll need it for configuration.

Example paths by OS:

  • macOS: /Users/john/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json
  • Linux: /home/john/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json
  • Windows: C:\Users\John\.cloudflared\a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

Step 3: Create Configuration File

Create ~/.cloudflared/config.yml:

tunnel: my-tunnel
credentials-file: /home/username/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

ingress:
  - hostname: app.yourdomain.com
    service: http://localhost:3000
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Real-world example:

tunnel: my-blog-tunnel
credentials-file: /home/john/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

ingress:
  - hostname: blog.jobayer.me
    service: http://localhost:4000
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Replace:

  • my-tunnel β†’ your tunnel name (e.g., my-blog-tunnel)
  • a7b3c4d5-e6f7-8901-2345-6789abcdef01.json β†’ your actual tunnel credentials file
  • app.yourdomain.com β†’ your desired subdomain (e.g., blog.jobayer.me)
  • localhost:3000 β†’ your local app address (e.g., localhost:4000 for Hugo)

Configuration file locations:

  • macOS/Linux: ~/.cloudflared/config.yml
  • Windows: C:\Users\YourUsername\.cloudflared\config.yml

Step 4: Configure DNS

cloudflared tunnel route dns my-tunnel app.yourdomain.com
Enter fullscreen mode Exit fullscreen mode

This creates a CNAME record pointing to your tunnel.

Real-world example:

cloudflared tunnel route dns my-blog-tunnel blog.jobayer.me
Enter fullscreen mode Exit fullscreen mode

Output:

Created CNAME route for blog.jobayer.me
Route created successfully
Enter fullscreen mode Exit fullscreen mode

What this does:

  • Creates a CNAME record in your Cloudflare DNS
  • Points blog.jobayer.me to your tunnel endpoint (e.g., a7b3c4d5-e6f7-8901-2345-6789abcdef01.cfargotunnel.com)
  • Makes your tunnel accessible via the friendly domain name

Verify in Cloudflare Dashboard:

  1. Go to your domain in Cloudflare Dashboard
  2. Click "DNS" in the sidebar
  3. You should see a CNAME record like:
    • Name: blog
    • Target: a7b3c4d5-e6f7-8901-2345-6789abcdef01.cfargotunnel.com

Step 5: Start the Tunnel

cloudflared tunnel run my-tunnel
Enter fullscreen mode Exit fullscreen mode

Output:

2024-12-05T10:30:15Z INF Starting tunnel tunnelID=a7b3c4d5-e6f7-8901-2345-6789abcdef01
2024-12-05T10:30:16Z INF Connection registered connIndex=0 location=IAD
2024-12-05T10:30:17Z INF Connection registered connIndex=1 location=ATL
2024-12-05T10:30:18Z INF Connection registered connIndex=2 location=ORD
2024-12-05T10:30:19Z INF Connection registered connIndex=3 location=DFW
Enter fullscreen mode Exit fullscreen mode

What this means:

  • βœ… Tunnel is running successfully
  • βœ… Connected to 4 Cloudflare edge locations (IAD, ATL, ORD, DFW)
  • βœ… Your local app is now accessible globally

πŸŽ‰ Your app is now live at https://blog.jobayer.me!

Testing your tunnel:

# From another terminal or device:
curl https://blog.jobayer.me

# Or open in browser:
# https://blog.jobayer.me
Enter fullscreen mode Exit fullscreen mode

Real-world example:
If you're running a Next.js dev server on port 3000 locally, anyone can now access it at https://blog.jobayer.me with automatic HTTPS!

Keep it running:

  • Keep the terminal window open, or
  • Press Ctrl+C to stop, or
  • Set it up as a system service (see Advanced Usage section)

Configuration

Basic Configuration Structure

tunnel: <tunnel-name>
credentials-file: <path-to-credentials>

ingress:
  - hostname: <your-hostname>
    service: <local-service>
  - service: http_status:404  # Catch-all rule (required)
Enter fullscreen mode Exit fullscreen mode

Multiple Services

Route different subdomains to different local services:

tunnel: my-tunnel
credentials-file: /home/user/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

ingress:
  # Frontend application
  - hostname: app.yourdomain.com
    service: http://localhost:3000

  # Backend API
  - hostname: api.yourdomain.com
    service: http://localhost:8080

  # Blog
  - hostname: blog.yourdomain.com
    service: http://localhost:4000

  # Admin panel
  - hostname: admin.yourdomain.com
    service: http://localhost:5000

  # Catch-all (required)
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Real-world example - Full Stack Application:

tunnel: fullstack-dev
credentials-file: /home/john/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

ingress:
  # React frontend (running on Vite dev server)
  - hostname: app.jobayer.me
    service: http://localhost:5173

  # Node.js/Express API
  - hostname: api.jobayer.me
    service: http://localhost:3000

  # PostgreSQL admin (pgAdmin)
  - hostname: db.jobayer.me
    service: http://localhost:5050

  # Storybook component library
  - hostname: storybook.jobayer.me
    service: http://localhost:6006

  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

DNS Setup:
After creating this config, run these commands:

cloudflared tunnel route dns fullstack-dev app.jobayer.me
cloudflared tunnel route dns fullstack-dev api.jobayer.me
cloudflared tunnel route dns fullstack-dev db.jobayer.me
cloudflared tunnel route dns fullstack-dev storybook.jobayer.me
Enter fullscreen mode Exit fullscreen mode

Result:

  • https://app.jobayer.me β†’ React app on localhost:5173
  • https://api.jobayer.me β†’ Express API on localhost:3000
  • https://db.jobayer.me β†’ pgAdmin on localhost:5050
  • https://storybook.jobayer.me β†’ Storybook on localhost:6006

Path-Based Routing

Route different URL paths to different services:

ingress:
  # API routes
  - hostname: yourdomain.com
    path: /api/*
    service: http://localhost:8080

  # Admin routes
  - hostname: yourdomain.com
    path: /admin/*
    service: http://localhost:5000

  # Default routes
  - hostname: yourdomain.com
    service: http://localhost:3000

  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Real-world example - Microservices on same domain:

tunnel: microservices
credentials-file: /home/john/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

ingress:
  # Authentication service
  - hostname: jobayer.me
    path: /auth/*
    service: http://localhost:4000

  # Payment processing
  - hostname: jobayer.me
    path: /payments/*
    service: http://localhost:4001

  # User management
  - hostname: jobayer.me
    path: /users/*
    service: http://localhost:4002

  # Analytics dashboard
  - hostname: jobayer.me
    path: /analytics/*
    service: http://localhost:4003

  # Main frontend application (catch-all for other paths)
  - hostname: jobayer.me
    service: http://localhost:3000

  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

URL routing result:

  • https://jobayer.me/ β†’ Frontend (localhost:3000)
  • https://jobayer.me/auth/login β†’ Auth service (localhost:4000)
  • https://jobayer.me/payments/checkout β†’ Payment service (localhost:4001)
  • https://jobayer.me/users/profile β†’ User service (localhost:4002)
  • https://jobayer.me/analytics/dashboard β†’ Analytics (localhost:4003)
  • https://jobayer.me/about β†’ Frontend (localhost:3000)

Custom Headers

Add custom headers to origin requests:

ingress:
  - hostname: app.yourdomain.com
    service: http://localhost:3000
    originRequest:
      httpHostHeader: localhost
      customHeaders:
        X-Custom-Header: "value"
Enter fullscreen mode Exit fullscreen mode

Advanced Usage

Running as a System Service

Keep your tunnel running persistently, even after reboots.

Linux (systemd)

sudo cloudflared service install
sudo systemctl start cloudflared
sudo systemctl enable cloudflared
Enter fullscreen mode Exit fullscreen mode

Check status:

sudo systemctl status cloudflared
Enter fullscreen mode Exit fullscreen mode

View logs:

sudo journalctl -u cloudflared -f
Enter fullscreen mode Exit fullscreen mode

macOS (launchd)

sudo cloudflared service install
sudo launchctl load /Library/LaunchDaemons/com.cloudflare.cloudflared.plist
Enter fullscreen mode Exit fullscreen mode

Windows (Service)

Run as Administrator:

cloudflared service install
sc start cloudflared
Enter fullscreen mode Exit fullscreen mode

Load Balancing

Distribute traffic across multiple instances:

ingress:
  - hostname: app.yourdomain.com
    service: http://localhost:3000
    originRequest:
      connectTimeout: 10s
  - hostname: app.yourdomain.com
    service: http://localhost:3001
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Logging Configuration

Enable detailed logging:

tunnel: my-tunnel
credentials-file: /path/to/credentials.json
loglevel: debug
logfile: /var/log/cloudflared.log

ingress:
  - hostname: app.yourdomain.com
    service: http://localhost:3000
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Log levels: debug, info, warn, error, fatal

Use Cases

1. Personal Blog/Portfolio

Scenario: Host a Hugo, Jekyll, or Next.js site from your laptop.

ingress:
  - hostname: blog.yourdomain.com
    service: http://localhost:1313  # Hugo default port
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Real-world example - Hugo Blog:

tunnel: my-blog
credentials-file: /home/sarah/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

ingress:
  - hostname: blog.jobayer.me
    service: http://localhost:1313
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Setup steps:

# 1. Start your Hugo dev server
hugo server -D

# 2. In another terminal, create tunnel route
cloudflared tunnel route dns my-blog blog.jobayer.me

# 3. Run the tunnel
cloudflared tunnel run my-blog
Enter fullscreen mode Exit fullscreen mode

Result: Your Hugo blog running on http://localhost:1313 is now accessible at https://blog.jobayer.me

Other static site generators:

# Jekyll (port 4000)
- hostname: blog.yourdomain.com
  service: http://localhost:4000

# Next.js (port 3000)
- hostname: portfolio.yourdomain.com
  service: http://localhost:3000

# Gatsby (port 8000)
- hostname: site.yourdomain.com
  service: http://localhost:8000
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • βœ… Zero hosting costs
  • βœ… Instant content updates
  • βœ… Full control over your content
  • βœ… No deployment pipeline needed

2. Development & Staging

Scenario: Share work-in-progress with clients or team members.

ingress:
  - hostname: dev.yourdomain.com
    service: http://localhost:3000
  - hostname: staging.yourdomain.com
    service: http://localhost:4000
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Real-world example - Agency workflow:

tunnel: client-demos
credentials-file: /home/alex/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

ingress:
  # Client A's project
  - hostname: clienta-dev.myagency.com
    service: http://localhost:3001

  # Client B's project
  - hostname: clientb-dev.myagency.com
    service: http://localhost:3002

  # Client C's project
  - hostname: clientc-dev.myagency.com
    service: http://localhost:3003

  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Setup multiple client demos:

# Route all domains
cloudflared tunnel route dns client-demos clienta-dev.myagency.com
cloudflared tunnel route dns client-demos clientb-dev.myagency.com
cloudflared tunnel route dns client-demos clientc-dev.myagency.com

# Start all dev servers
cd ~/projects/clienta && npm run dev &  # port 3001
cd ~/projects/clientb && npm run dev &  # port 3002
cd ~/projects/clientc && npm run dev &  # port 3003

# Run tunnel
cloudflared tunnel run client-demos
Enter fullscreen mode Exit fullscreen mode

Share with clients:

Hi Client A,

Here's the latest version of your website:
https://clienta-dev.myagency.com

Let me know your feedback!
Enter fullscreen mode Exit fullscreen mode

Update in real-time:
When you save changes in your code editor, clients see updates immediately after page refresh!

Benefits:

  • βœ… No deployment to staging servers needed
  • βœ… Real-time collaboration
  • βœ… Instant feedback loop
  • βœ… Multiple projects running simultaneously
  • βœ… Professional presentation with custom domains

3. Webhook Testing

Scenario: Test Stripe, GitHub, or other webhook integrations.

ingress:
  - hostname: webhooks.yourdomain.com
    service: http://localhost:8080
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Real-world example - Stripe payment testing:

tunnel: stripe-webhooks
credentials-file: /home/mike/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

ingress:
  - hostname: webhooks.mikestore.com
    service: http://localhost:4000
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Express.js webhook handler:

// server.js
const express = require('express');
const app = express();

app.post('/webhooks/stripe', express.raw({type: 'application/json'}), (req, res) => {
  const sig = req.headers['stripe-signature'];
  const event = req.body;

  console.log('Received Stripe webhook:', event.type);

  switch (event.type) {
    case 'payment_intent.succeeded':
      console.log('Payment succeeded:', event.data.object);
      break;
    case 'payment_intent.payment_failed':
      console.log('Payment failed:', event.data.object);
      break;
  }

  res.json({received: true});
});

app.listen(4000);
Enter fullscreen mode Exit fullscreen mode

Setup steps:

# 1. Create DNS route
cloudflared tunnel route dns stripe-webhooks webhooks.mikestore.com

# 2. Start your server
node server.js

# 3. Run tunnel
cloudflared tunnel run stripe-webhooks

# 4. Configure in Stripe Dashboard
# Webhook URL: https://webhooks.mikestore.com/webhooks/stripe
Enter fullscreen mode Exit fullscreen mode

Test it:

# Create a test payment in Stripe Dashboard
# Watch your terminal for webhook logs:

Received Stripe webhook: payment_intent.succeeded
Payment succeeded: { id: 'pi_123abc', amount: 1000, ... }
Enter fullscreen mode Exit fullscreen mode

Other webhook examples:

ingress:
  # GitHub webhooks
  - hostname: github.yourdomain.com
    path: /webhook
    service: http://localhost:3000

  # Twilio webhooks
  - hostname: twilio.yourdomain.com
    service: http://localhost:4000

  # PayPal IPN
  - hostname: paypal.yourdomain.com
    service: http://localhost:5000

  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • βœ… Public URL for webhook callbacks
  • βœ… Test real payments without deploying
  • βœ… Debug webhook payloads locally
  • βœ… Instant iteration on webhook logic

4. Home Automation

Scenario: Access Home Assistant, Plex, or other home services remotely.

ingress:
  - hostname: home.yourdomain.com
    service: http://localhost:8123  # Home Assistant
  - hostname: media.yourdomain.com
    service: http://localhost:32400  # Plex
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Real-world example - Complete smart home setup:

tunnel: smart-home
credentials-file: /home/chris/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

ingress:
  # Home Assistant
  - hostname: home.jobayer.me
    service: http://localhost:8123

  # Plex Media Server
  - hostname: plex.jobayer.me
    service: http://localhost:32400

  # Pi-hole (ad blocking)
  - hostname: pihole.jobayer.me
    service: http://localhost:80

  # Synology NAS
  - hostname: nas.jobayer.me
    service: http://localhost:5000

  # Security cameras (Frigate)
  - hostname: cameras.jobayer.me
    service: http://localhost:5001

  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Setup DNS routes:

cloudflared tunnel route dns smart-home home.jobayer.me
cloudflared tunnel route dns smart-home plex.jobayer.me
cloudflared tunnel route dns smart-home pihole.jobayer.me
cloudflared tunnel route dns smart-home nas.jobayer.me
cloudflared tunnel route dns smart-home cameras.jobayer.me
Enter fullscreen mode Exit fullscreen mode

Run as system service (Raspberry Pi):

sudo cloudflared service install
sudo systemctl start cloudflared
sudo systemctl enable cloudflared
Enter fullscreen mode Exit fullscreen mode

Access from anywhere:

  • Control lights: https://home.jobayer.me
  • Watch movies: https://plex.jobayer.me
  • Check cameras: https://cameras.jobayer.me
  • Manage files: https://nas.jobayer.me

Mobile app integration:
Configure Home Assistant mobile app to use https://home.jobayer.me - now you can control your home from anywhere!

Example - Control lights from work:

1. Open https://home.jobayer.me on your phone
2. Click "Living Room Lights"
3. Turn off (you forgot to turn them off!)
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • βœ… Secure remote access without VPN
  • βœ… No port forwarding security risks
  • βœ… Automatic HTTPS encryption
  • βœ… Access all your home services
  • βœ… Works on any device, anywhere

5. API Development

Scenario: Expose local API for mobile app development.

ingress:
  - hostname: api.yourdomain.com
    service: http://localhost:8080
    originRequest:
      connectTimeout: 30s
  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Real-world example - Mobile app backend:

tunnel: mobile-api
credentials-file: /home/dev/.cloudflared/a7b3c4d5-e6f7-8901-2345-6789abcdef01.json

ingress:
  # REST API
  - hostname: api.jobayer.me
    service: http://localhost:3000
    originRequest:
      connectTimeout: 30s

  # GraphQL API
  - hostname: graphql.jobayer.me
    service: http://localhost:4000

  # API documentation (Swagger)
  - hostname: docs.jobayer.me
    service: http://localhost:8080

  - service: http_status:404
Enter fullscreen mode Exit fullscreen mode

Example Node.js/Express API:

// server.js
const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors());
app.use(express.json());

// API endpoints
app.get('/api/users', (req, res) => {
  res.json([
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Smith' }
  ]);
});

app.post('/api/users', (req, res) => {
  console.log('Creating user:', req.body);
  res.json({ id: 3, ...req.body });
});

app.get('/api/health', (req, res) => {
  res.json({ status: 'ok', timestamp: Date.now() });
});

app.listen(3000, () => {
  console.log('API running on port 3000');
});
Enter fullscreen mode Exit fullscreen mode

Setup:

# 1. Start API server
node server.js

# 2. Create DNS route
cloudflared tunnel route dns mobile-api api.jobayer.me

# 3. Run tunnel
cloudflared tunnel run mobile-api
Enter fullscreen mode Exit fullscreen mode

Mobile app configuration (React Native):

// config.js
const API_BASE_URL = 'https://api.jobayer.me';

export const fetchUsers = async () => {
  const response = await fetch(`${API_BASE_URL}/api/users`);
  return response.json();
};

export const createUser = async (userData) => {
  const response = await fetch(`${API_BASE_URL}/api/users`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(userData)
  });
  return response.json();
};
Enter fullscreen mode Exit fullscreen mode

Test from mobile device:

# Your phone can now access:
https://api.jobayer.me/api/users
https://api.jobayer.me/api/health
Enter fullscreen mode Exit fullscreen mode

Development workflow:

  1. Make changes to API code
  2. Save file (nodemon auto-restarts)
  3. Test immediately on mobile device
  4. See changes in real-time!

Example - Testing on physical iPhone:

// In React Native app
fetch('https://api.jobayer.me/api/users')
  .then(res => res.json())
  .then(users => console.log('Users:', users));

// Output in debugger:
// Users: [{id: 1, name: 'John Doe'}, {id: 2, name: 'Jane Smith'}]
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • βœ… Test API with real mobile devices
  • βœ… Share API with frontend developers
  • βœ… Test in production-like environment
  • βœ… No need to deploy for every change
  • βœ… Automatic HTTPS for secure testing

Troubleshooting

Tunnel Won't Start

Problem: Error starting tunnel

Solutions:

  1. Check config syntax:
   cloudflared tunnel info my-tunnel
Enter fullscreen mode Exit fullscreen mode
  1. Verify credentials file:
   ls ~/.cloudflared/
Enter fullscreen mode Exit fullscreen mode
  1. Test local service:
   curl http://localhost:3000
Enter fullscreen mode Exit fullscreen mode
  1. Check for port conflicts:
   lsof -i :3000  # macOS/Linux
   netstat -ano | findstr :3000  # Windows
Enter fullscreen mode Exit fullscreen mode

Connection Timeouts

Problem: Tunnel connects but requests timeout

Solutions:

  1. Verify service is running:
   netstat -tuln | grep 3000
Enter fullscreen mode Exit fullscreen mode
  1. Check firewall settings:
   sudo ufw status  # Linux
Enter fullscreen mode Exit fullscreen mode
  1. Test with curl:
   curl -v http://localhost:3000
Enter fullscreen mode Exit fullscreen mode
  1. Increase timeout:
   originRequest:
     connectTimeout: 60s
Enter fullscreen mode Exit fullscreen mode

DNS Not Resolving

Problem: Domain doesn't resolve to tunnel

Solutions:

  1. Check DNS records:

    • Go to Cloudflare Dashboard β†’ DNS
    • Verify CNAME record exists
  2. Wait for propagation:

   dig app.yourdomain.com
   nslookup app.yourdomain.com
Enter fullscreen mode Exit fullscreen mode
  1. Force HTTPS:

    • Access via https:// not http://
  2. Clear DNS cache:

   # macOS
   sudo dscacheutil -flushcache

   # Linux
   sudo systemd-resolve --flush-caches

   # Windows
   ipconfig /flushdns
Enter fullscreen mode Exit fullscreen mode

Certificate Errors

Problem: SSL/TLS certificate errors

Solutions:

  1. Wait for certificate provisioning (can take 5-10 minutes)

  2. Verify Cloudflare SSL mode:

    • Cloudflare Dashboard β†’ SSL/TLS β†’ Overview
    • Set to "Full" or "Full (strict)"
  3. For self-signed certs:

   originRequest:
     noTLSVerify: true
Enter fullscreen mode Exit fullscreen mode

High Latency

Problem: Slow response times

Solutions:

  1. Check Cloudflare region:
   cloudflared tunnel info my-tunnel
Enter fullscreen mode Exit fullscreen mode
  1. Monitor local resources:
   top  # or htop
Enter fullscreen mode Exit fullscreen mode
  1. Enable compression:
   originRequest:
     disableChunkedEncoding: false
Enter fullscreen mode Exit fullscreen mode
  1. Optimize application performance

Tunnel Keeps Disconnecting

Problem: Tunnel drops connection frequently

Solutions:

  1. Check logs:
   cloudflared tunnel run my-tunnel --loglevel debug
Enter fullscreen mode Exit fullscreen mode
  1. Verify network stability

  2. Increase grace period:

   grace-period: 30s
Enter fullscreen mode Exit fullscreen mode
  1. Check for updates:
   cloudflared update
Enter fullscreen mode Exit fullscreen mode

Best Practices

Security

βœ… Use strong authentication

originRequest:
  access:
    required: true
Enter fullscreen mode Exit fullscreen mode

βœ… Enable audit logging

logfile: /var/log/cloudflared.log
loglevel: info
Enter fullscreen mode Exit fullscreen mode

βœ… Regularly update cloudflared

cloudflared update
Enter fullscreen mode Exit fullscreen mode

βœ… Implement rate limiting in your application

βœ… Use environment variables for sensitive data

Performance

⚑ Enable HTTP/2

originRequest:
  http2Origin: true
Enter fullscreen mode Exit fullscreen mode

⚑ Optimize application before exposing

⚑ Use caching where appropriate

⚑ Monitor resource usage

⚑ Consider CDN settings in Cloudflare Dashboard

Reliability

πŸ”„ Run as system service for automatic restart

πŸ”„ Monitor uptime with health checks

πŸ”„ Keep backups of configuration files

πŸ”„ Document your setup for team members

πŸ”„ Test failover scenarios

Organization

πŸ“ Use descriptive tunnel names

cloudflared tunnel create prod-web-server
Enter fullscreen mode Exit fullscreen mode

πŸ“ Maintain separate configs for different environments

πŸ“ Version control your config files

πŸ“ Document DNS records and their purposes

πŸ“ Use consistent naming conventions

Managing Tunnels

List All Tunnels

cloudflared tunnel list
Enter fullscreen mode Exit fullscreen mode

View Tunnel Details

cloudflared tunnel info my-tunnel
Enter fullscreen mode Exit fullscreen mode

Delete a Tunnel

# Stop the tunnel first
cloudflared tunnel cleanup my-tunnel

# Delete the tunnel
cloudflared tunnel delete my-tunnel

# Remove DNS record
# (manually in Cloudflare Dashboard)
Enter fullscreen mode Exit fullscreen mode

Rotate Credentials

cloudflared tunnel token my-tunnel
Enter fullscreen mode Exit fullscreen mode

Update Tunnel Configuration

  1. Edit ~/.cloudflared/config.yml
  2. Restart the tunnel:
   sudo systemctl restart cloudflared
Enter fullscreen mode Exit fullscreen mode

Additional Resources

Originally published at https://jobayer.me/blog/turn-your-local-machine-into-a-web-server-with-cloudflare-tunnel/

Top comments (0)