One of the most exciting moments in a development project is sharing your work with others. If you’re building an app with Socket.IO, whether for real-time messaging, notifications, or interactive applications, you’ll eventually want to make your local setup accessible to the public. This allows collaborators, testers, or clients to interact with your app without needing to deploy it to a server. In this post, I’ll walk you through how I make my local Socket.IO app available to the internet using ngrok.
Thank me by sharing on Twitter 🙏
Ngrok is a simple yet powerful tool for creating a secure tunnel from the public internet to your local machine. It handles the heavy lifting of exposing your development environment without complicated network or server setups. In this tutorial, I’ll guide you through the process and share some key tips along the way.
Setting Up ngrok for Socket.IO
To get started, ensure you’ve installed ngrok on your machine. You can download it from ngrok’s official site and follow their quick installation instructions. With ngrok ready, you’re just a few commands away from making your Socket.IO server publicly accessible.
Start your Socket.IO server locally. For this example, let’s assume your server runs on http://localhost:3000
.
- Open your terminal and run:
ngrok http 3000
Ngrok will generate a public URL, such ashttps://1234-5678-90ab.ngrok.io
. This URL will forward traffic from the internet to your local server running on port 3000. - Take note of the public URL because you’ll use it in the client code to connect to your Socket.IO server.
- Before your client can connect to the server, an important step is visiting the ngrok URL in your browser. You will need to enter the password (if ngrok is configured with authentication) or simply access the URL to allow ngrok to establish the connection. Without this step, the browser cannot make requests to the Socket.IO server through the ngrok tunnel.
Updating the Client to Use the Public URL
Now that you have a public URL from ngrok, you’ll need to update your Socket.IO client to connect to it. The client should use the ngrok URL instead of localhost
.
Brother Genuine Standard Yield Toner Cartridge, TN730, Replacement Black Toner, Page Yield Up To 1,200 Pages, Amazon Dash Replenishment Cartridge,1 Pack
$47.98 (as of January 9, 2025 10:16 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)HP 64 Black/Tri-color Ink Cartridges (2-pack) | Works with HP ENVY Inspire 7950e; ENVY Photo 6200, 7100, 7800; Tango Series | Eligible for Instant Ink | X4D92AN
$46.89 (as of January 9, 2025 10:16 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Amazon Basics Micro SDXC Memory Card with Full Size Adapter, A2, U3, Read Speed up to 100 MB/s, 128 GB, Black
$10.77 (as of January 9, 2025 10:16 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Here’s an example of how I modify the client-side code using TypeScript:
import { io } from 'socket.io-client';
const socket = io('https://1234-5678-90ab.ngrok.io'); // Replace with your ngrok URL
socket.on('connect', () => {
console.log('Connected to the Socket.IO server');
});
socket.on('message', (msg: string) => {
console.log('Message from server:', msg);
});
// Sending a message to the server
socket.emit('message', 'Hello from the client!');
This change allows your app to connect to the publicly accessible Socket.IO server, making it usable by anyone with the ngrok URL.
Ensuring Smooth Communication: Configuring CORS
When exposing your app to the internet, handling cross-origin requests becomes critical. By default, browsers block connections to different domains unless explicitly allowed. To ensure the client can communicate with the server through the ngrok URL, configure CORS on the server side.
Here’s how I configure CORS in a TypeScript-based Socket.IO server:
import { Server } from 'socket.io';
import http from 'http';
const server = http.createServer();
const io = new Server(server, {
cors: {
origin: '*', // Allow all origins (adjust for production as needed)
methods: ['GET', 'POST'],
},
});
io.on('connection', (socket) => {
console.log('A client connected:', socket.id);
socket.on('message', (msg: string) => {
console.log('Received message:', msg);
socket.emit('response', 'Message received!');
});
socket.on('disconnect', () => {
console.log('Client disconnected:', socket.id);
});
});
server.listen(3000, () => {
console.log('Socket.IO server running on port 3000');
});
This configuration allows the client to connect from any domain. For security, replace '*'
with the specific origin (e.g., https://1234-5678-90ab.ngrok.io
) if you know where the requests are coming from.
Handling Common Challenges
Initial Browser Authentication
Ngrok sessions often require initial authentication or a direct visit to the public URL to enable full access. If your setup includes password protection, ngrok will prompt for a password when you visit the URL. Additionally, even without a password, some browsers or environments may not allow WebSocket requests to function until the URL is accessed directly at least once. Always ensure this step is completed before testing your Socket.IO client.
Persistent URLs
One limitation of ngrok’s free tier is that the generated URL changes every time you start a new session. This can be inconvenient if you need a consistent URL for testing or sharing with others. To address this, you can upgrade to ngrok’s paid plan, which offers reserved domains.
Securing Your Tunnel
Ngrok automatically provides HTTPS for the public URL, ensuring secure communication. However, if your app deals with sensitive data, verify that your server and client use secure protocols correctly.
Performance Considerations
Since ngrok tunnels route traffic through an intermediary server, it’s essential to test for latency or bottlenecks, especially with real-time apps. Ngrok performs well for most use cases, but keep this in mind for intensive workloads.
Testing Your Setup
After configuring the server and client, test the connection:
- Start the Socket.IO server.
- Run the ngrok command to create a tunnel.
- Visit the ngrok URL in your browser and complete any required authentication.
- Launch your client application and verify it connects to the server using the ngrok URL.
On successful connection, you’ll see logs in your server indicating that a client has connected. Send messages back and forth to confirm everything works as expected.
Wrapping Things Up
Making a local Socket.IO server accessible to the public can sound daunting, but tools like ngrok make it surprisingly straightforward. By combining ngrok with simple updates to your client code and some basic CORS configuration, you can showcase your app to collaborators or testers with ease.
One crucial step that’s often overlooked is visiting the ngrok URL before attempting to connect from the client. This allows the tunnel to authenticate and enable the browser to make WebSocket requests properly. With this in place, your real-time app is ready for testing and sharing over the internet.