expose-localhost
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseExpose Localhost
暴露 Localhost
Expose a local service to the public internet using ngrok. Optionally add OAuth, OWASP protection, or rate limiting via Traffic Policy.
使用 ngrok 将本地服务暴露到公共互联网。可选择通过 Traffic Policy 添加 OAuth、OWASP 防护或速率限制。
Prerequisites
前置条件
- ngrok CLI installed (command available)
ngrok - Auth token configured ()
ngrok config add-authtoken <TOKEN>
- 已安装 ngrok CLI(命令可用)
ngrok - 已配置 Auth token()
ngrok config add-authtoken <TOKEN>
Workflow
工作流
Step 1: Pre-flight & Configuration
第 1 步:预检与配置
Silently verify ngrok is ready:
bash
ngrok config checkIf auth token missing, tell user to run: (get token at https://dashboard.ngrok.com/get-started/your-authtoken)
ngrok config add-authtoken <TOKEN>Then ask all questions upfront before doing anything:
Before I expose your service, I need a few details:
1. **Port**: I see your app runs on port 3000. Is that correct?
2. **Domain**: Use your dev domain, or do you have a custom domain?
3. **Access control**: Open access, or require login (Google/GitHub/etc.)?
4. **Save config?**: One-time setup, or save for reuse?Do NOT mention cloud endpoints, reserved domains, or internal endpoints — those are advanced concepts the user shouldn't need to think about.
Detecting the port: Check scripts for , for , for port mappings.
package.json--port.envPORT=docker-compose.ymlIs it even HTTP? If the user says SSH, RDP, a database, or names a port like 22, 3389, 5432, or 3306, this is a raw TCP service — skip to Non-HTTP Services (TCP). The domain and access-control questions above do not apply there.
Domains: Most ngrok accounts have a free static dev domain (e.g., ). Running uses it automatically. Users can also provide a custom domain configured in the ngrok dashboard. Some accounts (especially new ones) may not have a dev domain yet — if ngrok fails with , tell the user: "You don't have a dev domain yet. Claim your free one at https://dashboard.ngrok.com/domains — then we can try again."
something.ngrok-free.devngrok http PORTERR_NGROK_15013If user requests OAuth, also ask: "Should only specific people be able to access it? I can restrict by email address or email domain."
After gathering answers, confirm and get a Y/n before proceeding.
静默验证 ngrok 是否已准备就绪:
bash
ngrok config check如果缺少 Auth token,请让用户运行:(可从 https://dashboard.ngrok.com/get-started/your-authtoken 获取 token)。
ngrok config add-authtoken <TOKEN>然后,在执行任何操作之前一次性询问所有问题:
Before I expose your service, I need a few details:
1. **Port**: I see your app runs on port 3000. Is that correct?
2. **Domain**: Use your dev domain, or do you have a custom domain?
3. **Access control**: Open access, or require login (Google/GitHub/etc.)?
4. **Save config?**: One-time setup, or save for reuse?不要提及云端点、保留域名或内部端点——这些是高级概念,用户不应需要考虑这些内容。
检测端口:检查 scripts 中是否有 、 中是否有 ,以及 中的端口映射。
package.json--port.envPORT=docker-compose.yml这是 HTTP 服务吗? 如果用户提到 SSH、RDP、数据库,或说出 、、、 这样的端口,则这是原始 TCP 服务——跳转到非 HTTP 服务(TCP)。域名和访问控制问题不适用于该场景。
22338954323306域名:大多数 ngrok 账号都有免费的静态开发域名(例如 )。运行 会自动使用它。用户也可以提供在 ngrok dashboard 中配置的自定义域名。部分账号(尤其是新账号)可能还没有开发域名——如果 ngrok 出现 ,告诉用户:“你还没有开发域名。请在 https://dashboard.ngrok.com/domains 领取免费域名,然后我们可以重试。”
something.ngrok-free.devngrok http PORTERR_NGROK_15013如果用户要求 OAuth,还要询问:“是否只允许特定人员访问?我可以按电子邮件地址或电子邮件域名进行限制。”
收集完答案后,确认并获得 Y/n 再继续。
Step 2: Start the Tunnel
第 2 步:启动隧道
No security (simplest)
不启用安全措施(最简单)
bash
ngrok http {PORT} &
sleep 3
curl -s http://localhost:4040/api/tunnels | grep -o '"public_url":"[^"]*"' | head -1With a specific domain, add .
--url https://{DOMAIN}bash
ngrok http {PORT} &
sleep 3
curl -s http://localhost:4040/api/tunnels | grep -o '"public_url":"[^"]*"' | head -1使用特定域名时,添加 。
--url https://{DOMAIN}With security (Traffic Policy)
使用安全措施(Traffic Policy)
Create the traffic policy file first, then start ngrok with it.
OAuth-only (default when user requests auth):
yaml
on_http_request:
- actions:
- type: oauth
config:
provider: googleReplace with the chosen provider (google, github, microsoft, gitlab, linkedin, twitch).
googleIf the user requests OAuth, default to OAuth-only. Do NOT add OWASP or rate limiting unless explicitly asked — OAuth already blocks unauthenticated access.
OAuth with email restriction — use a separate rule with a CEL expression to deny non-matching emails. Do NOT add an field to the OAuth action.
allowSingle email:
yaml
on_http_request:
- actions:
- type: oauth
config:
provider: google
- expressions:
- "actions.ngrok.oauth.identity.email != 'user@example.com'"
actions:
- type: deny
config:
status_code: 403Email domain:
yaml
on_http_request:
- actions:
- type: oauth
config:
provider: google
- expressions:
- "!actions.ngrok.oauth.identity.email.endsWith('@your-company.com')"
actions:
- type: deny
config:
status_code: 403Multiple emails — name the identity explicitly on the left of :
inyaml
- expressions:
- "!(actions.ngrok.oauth.identity.email in ['a@x.com', 'b@x.com'])"
actions:
- type: deny
config:
status_code: 403Shared secret (when the person won't log in with an IdP — an external client, a
reviewer without a company account):
yaml
on_http_request:
- actions:
- type: basic-auth
config:
credentials:
- "reviewer:{PASSWORD}"Hand them the URL and the password over a channel you already trust. If they have a
stable IP, is an alternative with nothing to share.
restrict-ipsOpen-access hardening (no auth, but wants protection):
yaml
on_http_request:
- actions:
- type: rate-limit
config:
name: default-rate-limit
algorithm: sliding_window
capacity: 200
rate: "60s"
bucket_key:
- conn.client_ip
- type: owasp-crs-request
config:
on_error: halt
on_http_response:
- actions:
- type: owasp-crs-response
config:
on_error: haltAfter writing the policy file, start ngrok:
bash
ngrok http {PORT} --traffic-policy-file .ngrok/traffic-policy.yml &
sleep 3
curl -s http://localhost:4040/api/tunnels | grep -o '"public_url":"[^"]*"' | head -1Add if using a specific domain.
--url https://{DOMAIN}Sharing a dev server? Vite, Next, and webpack reject requests whose Host header
isn't localhost, and their hot-reload websockets need the ngrok domain allowed. Set
this up before handing over the URL — see .
references/TROUBLESHOOTING.md先创建 Traffic Policy 文件,然后使用它启动 ngrok。
仅 OAuth(用户要求身份验证时的默认选项):
yaml
on_http_request:
- actions:
- type: oauth
config:
provider: google将 替换为选定的提供商(google、github、microsoft、gitlab、linkedin、twitch)。
google如果用户要求 OAuth,默认使用仅 OAuth 方案。不要添加 OWASP 或速率限制,除非用户明确要求——OAuth 已经可以阻止未经身份验证的访问。
OAuth 与电子邮件限制——使用带 CEL expression 的独立规则拒绝不匹配的电子邮件。不要在 OAuth 操作中添加 字段。
allow单个电子邮件:
yaml
on_http_request:
- actions:
- type: oauth
config:
provider: google
- expressions:
- "actions.ngrok.oauth.identity.email != 'user@example.com'"
actions:
- type: deny
config:
status_code: 403电子邮件域名:
yaml
on_http_request:
- actions:
- type: oauth
config:
provider: google
- expressions:
- "!actions.ngrok.oauth.identity.email.endsWith('@your-company.com')"
actions:
- type: deny
config:
status_code: 403多个电子邮件——在 左侧明确指定 identity:
inyaml
- expressions:
- "!(actions.ngrok.oauth.identity.email in ['a@x.com', 'b@x.com'])"
actions:
- type: deny
config:
status_code: 403共享密钥(当对方不会使用 IdP 登录时——例如外部客户端或没有公司账号的审阅者):
yaml
on_http_request:
- actions:
- type: basic-auth
config:
credentials:
- "reviewer:{PASSWORD}"通过你已经信任的渠道将 URL 和密码交给对方。如果对方使用固定 IP,也可以使用 ,无需共享任何凭据。
restrict-ips开放访问加固(无 Auth,但需要防护):
yaml
on_http_request:
- actions:
- type: rate-limit
config:
name: default-rate-limit
algorithm: sliding_window
capacity: 200
rate: "60s"
bucket_key:
- conn.client_ip
- type: owasp-crs-request
config:
on_error: halt
on_http_response:
- actions:
- type: owasp-crs-response
config:
on_error: halt写入策略文件后,启动 ngrok:
bash
ngrok http {PORT} --traffic-policy-file .ngrok/traffic-policy.yml &
sleep 3
curl -s http://localhost:4040/api/tunnels | grep -o '"public_url":"[^"]*"' | head -1使用特定域名时,添加 。
--url https://{DOMAIN}共享开发服务器? Vite、Next 和 webpack 会拒绝 Host 请求头不是 localhost 的请求,而且其热重载 WebSocket 需要允许使用 ngrok 域名。在交付 URL 前先完成此设置——参见 。
references/TROUBLESHOOTING.mdStep 3: Handle Errors
第 3 步:处理错误
If a traffic policy action fails due to plan limitations:
- Tell the user which specific action requires an upgrade
- Offer to remove that action from the policy and retry
- Do NOT suggest switching to cloud endpoints as a workaround
如果某个 Traffic Policy 操作因套餐限制而失败:
- 告诉用户具体哪个操作需要升级套餐
- 提议从策略中移除该操作并重试
- 不要建议以 Cloud Endpoints 作为变通方案
Step 4: Persistent Configuration (If Requested)
第 4 步:持久化配置(如有请求)
Save these files to the project:
- — the policy (if security was configured)
.ngrok/traffic-policy.yml - :
.ngrok/expose.sh
bash
#!/bin/bash
set -e
echo "Your service will be at: https://{DOMAIN}"
ngrok http {PORT} --url https://{DOMAIN} --traffic-policy-file .ngrok/traffic-policy.ymlOmit if no policy. Omit if no specific domain.
--traffic-policy-file--urlOptionally add to :
package.jsonjson
{ "scripts": { "tunnel": "bash .ngrok/expose.sh" } }将这些文件保存到项目中:
- ——策略(如果配置了安全措施)
.ngrok/traffic-policy.yml - :
.ngrok/expose.sh
bash
#!/bin/bash
set -e
echo "Your service will be at: https://{DOMAIN}"
ngrok http {PORT} --url https://{DOMAIN} --traffic-policy-file .ngrok/traffic-policy.yml如果没有策略,省略 。如果没有特定域名,省略 。
--traffic-policy-file--url也可以选择添加到 :
package.jsonjson
{ "scripts": { "tunnel": "bash .ngrok/expose.sh" } }Teardown
关闭隧道
bash
pkill ngrokbash
pkill ngrokNon-HTTP Services (TCP)
非 HTTP 服务(TCP)
For SSH, RDP, or a database, use instead of .
ngrok tcpngrok httpbash
ngrok tcp {PORT} &
sleep 3
curl -s http://localhost:4040/api/tunnels | grep -o '"public_url":"[^"]*"' | head -1ngrok returns an address like . The host and port are
used separately by the client:
tcp://1.tcp.ngrok.io:12345bash
undefined对于 SSH、RDP 或数据库,请使用 而不是 。
ngrok tcpngrok httpbash
ngrok tcp {PORT} &
sleep 3
curl -s http://localhost:4040/api/tunnels | grep -o '"public_url":"[^"]*"' | head -1ngrok 会返回类似 的地址。客户端需要分别使用其中的主机和端口:
tcp://1.tcp.ngrok.io:12345bash
undefinedlocal sshd on port 22, reached through the address above
local sshd on port 22, reached through the address above
ssh -p 12345 {USER}@1.tcp.ngrok.io
Common ports: SSH `22`, RDP `3389`, PostgreSQL `5432`, MySQL `3306`.
**The address changes on every restart.** For one that persists, reserve it first.
ngrok assigns the hostname and port — unlike domains, you cannot choose them:
```bash
ngrok api reserved-addrs create --description "SSH for {NAME}" --region us
ngrok tcp {PORT} --url tcp://{ADDR}Reserved TCP addresses need a paid plan and keep billing until deleted.
Access control is different here. OAuth and OWASP are HTTP-only and will not
work. TCP policies run in the phase, where is the
usual control:
on_tcp_connectrestrict-ipsyaml
on_tcp_connect:
- actions:
- type: restrict-ips
config:
enforce: true
allow:
- 203.0.113.0/24Attach it the same way:
ngrok tcp {PORT} --traffic-policy-file .ngrok/traffic-policy.ymlssh -p 12345 {USER}@1.tcp.ngrok.io
常用端口:SSH `22`、RDP `3389`、PostgreSQL `5432`、MySQL `3306`。
**每次重启时地址都会变化。** 如果需要持久地址,请先保留该地址。ngrok 会分配主机名和端口——与域名不同,你不能自行选择:
```bash
ngrok api reserved-addrs create --description "SSH for {NAME}" --region us
ngrok tcp {PORT} --url tcp://{ADDR}保留的 TCP 地址需要付费套餐,并会持续计费,直至删除。
这里的访问控制方式不同。 OAuth 和 OWASP 仅适用于 HTTP,无法用于 TCP。TCP 策略在 阶段运行,通常使用 作为控制手段:
on_tcp_connectrestrict-ipsyaml
on_tcp_connect:
- actions:
- type: restrict-ips
config:
enforce: true
allow:
- 203.0.113.0/24以相同方式附加:
ngrok tcp {PORT} --traffic-policy-file .ngrok/traffic-policy.ymlCloud Endpoints (Advanced)
Cloud Endpoints(高级)
Only use if the user explicitly needs a URL that persists after the agent stops (e.g., webhooks, long-lived integrations).
Requires the ngrok MCP server or an API key. If the user manages ngrok as code, add the endpoint there instead. If not, and the MCP server is connected, use it and do not ask for an API key: and replace steps 1 and 2, and replaces step 4. Otherwise the user sets an API key with (from https://dashboard.ngrok.com/api-keys). See for the order to pick in.
gw_reserve_domaingw_create_cloud_endpointdelete_resourcengrok config add-api-key <KEY>ngrok-surfaces- Reserve domain:
ngrok api reserved-domains create --domain "{DOMAIN}" - Create cloud endpoint with a traffic policy that includes as the last action:
forward-internal
bash
ngrok api endpoints create --url "https://{DOMAIN}" --bindings public --traffic-policy "$(cat .ngrok/traffic-policy.yml)"The traffic policy must end with:
yaml
- type: forward-internal
config:
url: https://{NAME}.internal- Start the internal agent:
ngrok http {PORT} --url https://{NAME}.internal - Teardown:
ngrok api endpoints delete {ENDPOINT_ID}
仅当用户明确需要一个在 Agent 停止后仍然有效的 URL 时,才使用此功能(例如 webhook、长期运行的集成)。
这需要 ngrok MCP server 或 API key。如果用户以代码方式管理 ngrok,应改为在其中添加 endpoint。如果未以代码方式管理,但已连接 MCP server,则使用它且不要索要 API key: 和 替代第 1、2 步, 替代第 4 步。否则,用户应使用 设置 API key(可从 https://dashboard.ngrok.com/api-keys 获取)。有关选择顺序,请参阅 。
gw_reserve_domaingw_create_cloud_endpointdelete_resourcengrok config add-api-key <KEY>ngrok-surfaces- 保留域名:
ngrok api reserved-domains create --domain "{DOMAIN}" - 创建 Cloud Endpoint,并让 Traffic Policy 将 作为最后一个操作:
forward-internal
bash
ngrok api endpoints create --url "https://{DOMAIN}" --bindings public --traffic-policy "$(cat .ngrok/traffic-policy.yml)"Traffic Policy 必须以以下内容结尾:
yaml
- type: forward-internal
config:
url: https://{NAME}.internal- 启动内部 Agent:
ngrok http {PORT} --url https://{NAME}.internal - 拆除 endpoint:
ngrok api endpoints delete {ENDPOINT_ID}
Related Skills
相关 Skills
This skill covers giving a service a public URL. Neighbouring jobs have their own
skills — hand off rather than reimplementing them here:
- Auth, rate limits, IP rules, or a maintenance page in general —
. (Sharing a preview with one named person is handled here, in Step 2.)
secure-endpoint - It should receive webhooks — verifies provider signatures at the edge and can keep the receiver off the public internet.
receive-webhooks - It is an MCP server — handles per-provider credentials.
run-mcp-gateway - One endpoint per sandbox, tenant, or device, created by a controlplane —
.
provision-sandbox-access - Something is broken (, a 400 through the URL but not on localhost) —
ERR_NGROK_*.troubleshoot-ngrok - Setup or auth problems — .
ngrok-setup
For the underlying model — endpoint types, HTTP vs TCP, and the full Traffic Policy
action set — see . For applying config on a surface other than the
CLI (SDKs, REST API, Terraform, Kubernetes), see .
ngrok-enginengrok-surfaces此 skill 涵盖为服务提供公共 URL。相邻任务有各自的 skill——应转交给它们,而不是在这里重新实现:
- 一般需要 Auth、速率限制、IP 规则或维护页面——。(在本 skill 的第 2 步中,也处理向指定人员共享预览。)
secure-endpoint - 需要接收 webhook——会在边缘验证提供商签名,并让接收器无需暴露在公共互联网上。
receive-webhooks - 它是 MCP server——处理各提供商的凭据。
run-mcp-gateway - 每个 sandbox、tenant 或 device 使用一个 endpoint,并由 controlplane 创建——。
provision-sandbox-access - 出现故障(、通过 URL 访问返回 400 但在 localhost 上正常)——
ERR_NGROK_*。troubleshoot-ngrok - 安装或 Auth 问题——。
ngrok-setup
对于底层模型——endpoint 类型、HTTP 与 TCP,以及完整的 Traffic Policy 操作集——请参阅 。如需在 CLI 以外的 surface(SDK、REST API、Terraform、Kubernetes)上应用配置,请参阅 。
ngrok-enginengrok-surfaces