A modern, open-source alternative to localtunnel. Bundles client & server to host your own tunnel infrastructure.
Expose local services to the internet, or embed tunneling in your own tools.
Share your local server with teammates, test webhooks, or demo work without deploying.
Embed pipenet in your own tools to provide tunneling capabilities. mcp-proxy uses pipenet to connect local MCP servers with remote AI clients.
Run your own tunnel server for full control over security, domains, and availability.
One package. Two modes. Use the public server or deploy your own.
# Expose local port npx pipenet client --port 3000 # Custom subdomain npx pipenet client --port 3000 \ --subdomain myapp # Your own server npx pipenet client --port 3000 \ --host https://tunnel.example.com
# Start server npx pipenet server --port 3000 # Custom domain npx pipenet server --port 3000 \ --domain tunnel.example.com # Cloud-ready npx pipenet server --port 3000 \ --tunnel-port 3001
Built for modern deployment environments.
| Feature | pipenet | localtunnel |
|---|---|---|
| Cloud deployment | single-port | random ports |
| Multiple domains | ✓ | — |
| TypeScript | ✓ | — |
| ES Modules | ✓ | — |
| Maintenance | Active | Limited |
| WebSocket | ✓ | ✓ |
Tunnels any HTTP-based traffic to your local server.
| Protocol | Notes |
|---|---|
| HTTP / HTTPS | Standard request/response |
| WebSocket | Full duplex via HTTP upgrade |
| SSE | Long-lived HTTP connections |
| HTTP Streaming | Chunked transfer encoding |
Programmatic usage for testing, automation, and integration.
import { pipenet } from 'pipenet'; const tunnel = await pipenet({ port: 3000 }); console.log(tunnel.url); // https://abc123.pipenet.dev tunnel.on('request', (info) => console.log(info.method, info.path)); tunnel.on('close', () => console.log('closed'));
| port | number Local port to expose |
| host | string Tunnel server URL |
| subdomain | string Request specific subdomain |
| localHost | string Proxy to this hostname instead of localhost |
| localHttps | boolean Tunnel to local HTTPS server |
| allowInvalidCert | boolean Skip cert validation |
| request | Fired on each proxied request with method and path |
| error | Fired when an error occurs |
| close | Fired when tunnel closes |
Deploy your own tunnel infrastructure with lifecycle hooks.
import { createServer } from 'pipenet/server'; const server = createServer({ domains: ['tunnel.example.com'], secure: true, tunnelPort: 3001, // Lifecycle hooks onTunnelCreated: (tunnel) => { console.log(`Tunnel created: ${tunnel.id} at ${tunnel.url}`); }, onTunnelClosed: (tunnel) => { console.log(`Tunnel closed: ${tunnel.id}`); }, onRequest: (req) => { console.log(`${req.method} ${req.path} via ${req.tunnelId}`); }, }); await server.tunnelServer.listen(3001); server.listen(3000);
| domains | string[] Custom domain(s) for tunnel server |
| secure | boolean Require HTTPS |
| landing | string Redirect URL for root requests |
| maxTcpSockets | number Max sockets per client (default: 10) |
| tunnelPort | number Shared port for cloud deployments |
| onTunnelCreated | Called when a new tunnel is created |
| onTunnelClosed | Called when a tunnel is closed |
| onRequest | Called on each proxied request |
| GET /api/status | Server status and tunnel count |
| GET /api/tunnels/:id/status | Status of specific tunnel |
| GET /:id | Request new tunnel with ID |