Ever needed to share your local WordPress development site with a client, test webhooks, or make email images load properly? Tunneling services let you expose your local site to the internet with a public URL. Here’s how to do it for any local setup.
Why You Need This
- Email testing: Images and links in emails sent from local sites won’t work without a public URL
- Webhook testing: Payment gateways, APIs, and third-party services need to reach your site
- Client previews: Share work-in-progress without deploying
- Mobile testing: Test your site on real devices
- Third-party integrations: OAuth callbacks, n8n, make, Zapier, etc.
Understanding Your Local Setup
Before tunneling, identify your setup:
Standard Local Domains (Valet/Herd/Local)
http://mysite.test
orhttps://mysite.test
(port 80/443)http://mysite.local
orhttps://mysite.local
(port 80/443)
Custom Ports
http://localhost:7070
http://localhost:8080
Docker/Custom Setups
- May use non-standard ports or domains
Method 1: ngrok (Most Popular & Reliable)
Installation
Mac (Homebrew):
bash
brew install ngrok
Windows/Linux: Download from ngrok.com and add to PATH
Setup (one-time):
bash
ngrok config add-authtoken YOUR_AUTH_TOKEN
For HTTP Sites (port 80)
Simple localhost:
bash
ngrok http 80
Laravel Valet/Herd sites (mysite.test):
bash
ngrok http 80 --host-header=mysite.test
Custom port:
bash
ngrok http 7070
For HTTPS Sites (port 443)
Laravel Valet/Herd with SSL (mysite.test):
bash
ngrok http https://mysite.test
Or if that doesn’t work:
bash
ngrok http 443 --host-header=mysite.test
With self-signed certificates:
bash
ngrok http https://mysite.test --host-header=mysite.test
Reading ngrok Output
When you run ngrok, you’ll see:
Forwarding https://abc123.ngrok-free.app -> http://localhost:80
Use the https://abc123.ngrok-free.app
URL as your public site URL.
Method 2: Expose (Simpler Alternative)
Installation
bash
npm install -g @beyondcode/expose
Or download from beyondco.de/expose
Usage
For Valet/Herd sites:
bash
expose share http://mysite.test:80 --subdomain=mysite
For HTTPS sites:
bash
expose share https://mysite.test:443
Custom ports:
bash
expose share http://localhost:7070
Expose handles virtual hosts better than ngrok and has a cleaner interface.
Method 3: LocalWP Live Link (For Local by Flywheel Users)
If you’re using Local by Flywheel:
- Open your site in Local
- Click the “Live Link” button in the top right
- Enable the live link
- Copy the generated URL
This is the easiest method if you’re already using Local!
Method 4: Cloudflare Tunnel (Free & Permanent)
For more permanent solutions:
Installation
bash
brew install cloudflare/cloudflare/cloudflared
Setup
bash
cloudflared tunnel login cloudflared tunnel create mysite
Run Tunnel
For Valet/Herd:
bash
cloudflared tunnel --url https://mysite.test
For custom ports:
bash
cloudflared tunnel --url http://localhost:7070
Updating WordPress Site URLs
Once your tunnel is running, WordPress needs to know about the new URL.
Method 1: wp-config.php (Temporary – Recommended)
Add to your wp-config.php
(before “That’s all, stop editing!”):
php
define('WP_HOME', 'https://abc123.ngrok-free.app'); define('WP_SITEURL', 'https://abc123.ngrok-free.app');
Pros: Easy to revert, doesn’t touch database
Cons: Need to update every time tunnel URL changes
Method 2: WP-CLI (If you need database update)
bash
wp search-replace 'https://mysite.test' 'https://abc123.ngrok-free.app'
Remember to revert when done:
bash
wp search-replace 'https://abc123.ngrok-free.app' 'https://mysite.test'
Method 3: Plugin (Easiest for Non-technical Users)
Install Relative URL plugin to make all URLs relative, or use WP Migrate DB for easy URL switching.
Common Setup Examples
Laravel Valet (Mac)
bash
# HTTP site ngrok http 80 --host-header=mysite.test # HTTPS site (secured with valet secure) ngrok http https://mysite.test
Laravel Herd (Mac/Windows)
bash
# HTTP site ngrok http 80 --host-header=mysite.test # HTTPS site ngrok http https://mysite.test
XAMPP/MAMP
bash
# Default XAMPP/MAMP ngrok http 80 # Custom port ngrok http 8888
Docker (DDEV, Lando, etc.)
bash
# Check your docker port first docker ps # Then tunnel that port ngrok http 8080 --host-header=mysite.test
Local by Flywheel
Just use the built-in Live Link feature! Or:
bash
ngrok http 80 --host-header=mysite.local
Troubleshooting
“Getting localhost homepage instead of my site”
You forgot the host header:
bash
ngrok http 80 --host-header=mysite.test
“SSL certificate errors”
Your local site uses HTTPS:
bash
ngrok http https://mysite.test
“Site loads but images/CSS broken”
Update WordPress URLs using one of the methods above.
“ngrok session expired”
Free ngrok URLs expire when you close the tunnel. Either:
- Restart ngrok (get new URL, update WordPress)
- Upgrade to ngrok paid plan for persistent URLs
- Use Cloudflare Tunnel for free permanent URLs
“Multiple sites on same port”
Always use --host-header
:
bash
ngrok http 80 --host-header=specific-site.test
Security Considerations
⚠️ Important: These tunnels expose your local site to the internet!
- Never expose production databases
- Never leave tunnels running unattended
- Don’t share tunnel URLs publicly if they contain sensitive data
- Use ngrok’s authentication features for added security:
bash
ngrok http 80 --basic-auth="username:password"
Quick Reference Cheat Sheet
bash
# HTTP on port 80 ngrok http 80 # HTTP with virtual host ngrok http 80 --host-header=mysite.test # HTTPS site ngrok http https://mysite.test # Custom port ngrok http 7070 # With basic auth ngrok http 80 --basic-auth="user:pass" # Expose alternative expose share https://mysite.test:443
Conclusion
Tunneling your local WordPress site is essential for modern development workflows. Whether you’re using Laravel Valet, Herd, Local by Flywheel, or a custom setup, ngrok and Expose make it simple to get a public URL in seconds.
My recommendation: Start with ngrok for its reliability and great documentation. If you need something permanent, try Cloudflare Tunnel. And if you’re using Local by Flywheel, just use the built-in Live Link!
What’s your preferred local development setup? Let me know in the comments!