什么是ngrok
ngrok 是一款内网穿透工具
特性/场景 | 描述 |
---|---|
核心功能 | 内网穿透,将本地服务暴露到公网 |
主要优势 | 简单易用、无需公网IP、无需配置路由器、提供HTTPS、自带请求监控工具 |
典型用户 | 软件开发人员、测试工程师、DevOps工程师 |
核心使用场景 | 1. 本地开发调试 2. Webhook测试(极其重要) 3. 演示预览 4. 移动端测试 5. 临时访问内网服务 |
注册帐号获取Token
打开ngrok控制台进行帐号注册,注册完毕之后会进入控制台,切换到Authtoken页面即可查看分配的token。
免费版的账户可以使用
- 1个随机域名
- 20000次https请求,每分钟4000次
- 1GB网络传输流量
启动隧道之后可以在控制台查看接入的端点
ngrok 支持多种方式的使用
命令行工具
可以通过多种方式来进行全局的命令行工具安装
$ brew install ngrok
$ npm install ngrok -g
安装之后设置token,就是注册时获取的authtoken
$ ngrok config add-authtoken $YOUR_AUTHTOKEN
然后就可以启动内网穿透了,比如我本地启动了5173端口
$ ngrok http http://localhost:5173
启动之后可以看到已经分配了一个地址并重定向到了本地5173端口
访问这个链接之后可以看到跟本地访问的效果一致
SDK 调用
ngrok也支持 SDK 的方式调用,将隧道的连接放进业务中控制。以NodeJS SDK 为例,我们可以使用以下API来进行处理
验证
await ngrok.authtoken(token);
将控制台中生成的token传入authtoken
方法用于验证身份,以便后续连接。
连接
const url = await ngrok.connect();
const url = await ngrok.connect(port);
创建隧道连接,可以指定端口,不指定时默认80
端口进行转发。
也可以传入一个配置来精细控制连接的方式:
- proto: ‘http’, // http|tcp|tls, 默认 http
- addr: 8080, // 转发的端口, 默认 80
- basic_auth: ‘user:pwd’, // 隧道鉴权方式
- subdomain: ‘son’, // 子域名
- authtoken: ‘12345’, // token
- region: ‘us’, // 连接服务器地域 (us, eu, au, ap, sa, jp, in), 默认 us
- configPath: ‘~/git/project/ngrok.yml’, // 配置文件目录
- binPath: path => path.replace(‘app.asar’, ‘app.asar.unpacked’), // 自定义bin目录
- onStatusChange: status => {}, // 连接状态变化回调
- onLogEvent: data => {}, // ngrok标准输出
断开连接
await ngrok.disconnect(url); // 停止指定地址
await ngrok.disconnect(); // 停止所有
await ngrok.kill(); // 杀掉ngrok进程
使用以上API在退出程序时断开连接,释放进程。
例如我们可以通过NodeJS SDK来实现一个Vite插件,在Vite DevServer启动时建立隧道连接。
import type { Plugin, ViteDevServer } from 'vite'
import { connect, authtoken, disconnect } from 'ngrok'
interface NgrokPluginOptions {
// ngrok 认证令牌,可以从环境变量 NGROK_AUTHTOKEN 中获取
authToken?: string
// 是否启用 ngrok,默认为 true
enabled?: boolean
// ngrok 配置选项
ngrokOptions?: {
// 自定义子域名
subdomain?: string
// 区域设置 (us, eu, ap, au, sa, jp, in)
region?: 'us' | 'eu' | 'ap' | 'au' | 'sa' | 'jp' | 'in'
// 基本认证
auth?: string
// 主机头重写
host_header?: string
// 绑定tls
bind_tls?: boolean | string
// 检查证书
inspect?: boolean
}
}
let ngrokUrl: string | null = null
export default function vitePluginNgrok(options: NgrokPluginOptions = {}): Plugin {
const {
authToken = process.env.NGROK_AUTHTOKEN,
enabled = true,
ngrokOptions = {}
} = options
return {
name: 'vite-plugin-ngrok',
apply: 'serve', // 只在开发模式下应用
configureServer(server: ViteDevServer) {
if (!enabled) {
return
}
// 在服务器启动后设置 ngrok
server.middlewares.use('/__ngrok_status', (_req, res) => {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({
url: ngrokUrl,
status: ngrokUrl ? 'connected' : 'disconnected'
}))
})
// 监听服务器启动事件
server.httpServer?.on('listening', async () => {
await setupNgrok(server, authToken, ngrokOptions)
})
// 服务器关闭时断开 ngrok
server.httpServer?.on('close', async () => {
if (ngrokUrl) {
try {
await disconnect()
console.log('\n🔌 ngrok 隧道已断开')
} catch (error) {
console.error('断开 ngrok 时出错:', error)
}
}
})
}
}
}
async function setupNgrok(
server: ViteDevServer,
authToken?: string,
ngrokOptions: NgrokPluginOptions['ngrokOptions'] = {}
) {
try {
// 从 httpServer 获取实际监听的地址信息
const address = server.httpServer?.address()
if (!address) {
console.error('❌ 无法获取服务器地址信息')
return
}
const port = typeof address === 'string' ? parseInt(address) : address.port
const host = typeof address === 'string' ? 'localhost' : address.address === '::1' ? 'localhost' : address.address
console.log(`\n🚀 正在启动 ngrok 隧道...`)
console.log(`📍 本地地址: http://${host}:${port}`)
// 如果提供了认证令牌,先设置认证
if (authToken) {
try {
await authtoken(authToken)
console.log('✅ ngrok 认证令牌已设置')
} catch (error) {
console.warn('⚠️ 设置 ngrok 认证令牌失败:', error)
}
}
// 连接到 ngrok
const url = await connect({
addr: port,
...ngrokOptions
})
ngrokUrl = url
console.log(`\n🌐 ngrok 隧道已建立!`)
console.log(`┌─────────────────────────────────────────────────┐`)
console.log(`│ 本地地址: http://${host}:${port}${' '.repeat(Math.max(0, 29 - host.length - port.toString().length))}│`)
console.log(`│ 公网地址: ${url}${' '.repeat(Math.max(0, 37 - url.length))}│`)
console.log(`└─────────────────────────────────────────────────┘`)
console.log(`\n💡 您可以通过公网地址访问您的应用`)
console.log(`📊 ngrok 状态: http://127.0.0.1:4040 (ngrok 检查界面)`)
console.log(`🔗 插件状态: http://${host}:${port}/__ngrok_status\n`)
} catch (error) {
console.error('\n❌ 启动 ngrok 隧道失败:')
console.error(error)
console.log('\n💡 提示:')
console.log(' 1. 确保已安装 ngrok: https://ngrok.com/download')
console.log(' 2. 设置认证令牌: ngrok authtoken <your_token>')
console.log(' 3. 或通过环境变量设置: NGROK_AUTHTOKEN=<your_token>')
console.log(' 4. 或在插件配置中传入 authToken 参数\n')
}
}
在启动开发服务器时可以看到日志中已经打印出连接地址
我们可以通过127.0.0.1:4040地址查看连接状态
总结
总而言之,ngrok 是一个功能强大且开发者友好的工具,它解决了“如何让外界安全、方便地访问本地服务”这一核心痛点,是现代软件开发流程中不可或缺的利器。