All your traffic.
One gateway.
ngrok is an all-in-one cloud networking platform that secures, transforms, and routes your traffic to services running anywhere.
Trusted by teams across the globe
Try ngrok by sharing a local app.
Right now.
$ docker pull ngrok/ngrokFrom localhost to live in prod.
Instead of a hodgepodge of nginx, NLBs, VPNs, model routers, and Cloudflare, solve every networking problem with one gateway.
Put your local app on a public URL
Deliver and secure APIs
Route, secure, and transform traffic to any AI model
Control devices in the field
Connect to APIs and DBs in customer networks
You may also need to…
Handle webhooks for production services
Assign secure URLs to previews and CI tests
SSH and RDP into devices in the field
Access remote K8s clusters from dev
Connect your local MCP server to ChatGPT and Claude
Replay HTTP requests to debug webhooks faster
Okay, but how does it work?
Connect anything with Endpoints, Traffic Policy, and Secure Tunnels.
Route, transform, and authenticate your traffic.
No more cryptic nginx configs or Lua plugins. Everything is a URL with traffic rules you attach. Compose them together and offload processing to ngrok’s cloud gateway.
Endpoints & Traffic Policy
Take action at any phase in the request lifecycle.
Traffic Policy is an expressive CEL-based rules system. When a request hits different phases of its lifecycle, ngrok executes each rule sequentially.
1on_http_request:2 # send requests with the /api path prefix to the api service3 - expressions:4 # conditions are CEL expressions, see https://cel.dev5 - req.url.path.startsWith('/api')6 actions:7 - type: forward-internal8 config:9 url: https://api.internal10 11 # route dynamically based on a header using CEL interpolation12 - actions:13 - type: forward-internal14 config:15 url: https://${req.headers('X-Custom-Header')}.internalEven more ways to control and transform your traffic
Stop cobbling your infrastructure together.
Security, performance, and resiliency built in by default.
Turn one URL into a smart pool of replicas
Scale automatically by starting endpoints with the same URL. They self-register for load balancing. Stop them and they’re out.
Accelerate traffic with smart global routing
We reduce latency for your end users by routing every request across our global network to the nearest healthy replica.
Protect origins from DDoS with no extra work
ngrok automatically blocks volumetric attacks from malicious actors before any traffic reaches your network.
Serve HTTP, TLS, and TCP with native support
If it runs over TCP, it works on ngrok. Websockets, gRPC, SSH, Postgres, MQTT, even Minecraft… you get the idea.
Connect to services anywhere, no firewall changes required.
Install a lightweight agent to deliver traffic to any service through a secure tunnel.
Run your services in any environment
Because your services are connected to ngrok with secure tunnels, they can be deployed anywhere. If it listens on a port and is connected to the internet, ngrok can deliver traffic to it.
Close every single inbound port
Attackers can't skirt a secure tunnel to scan or attack your origin servers, which means you whittle down your surface area and remove an entire class of attack vectors.
Precise network access, not risky customer VPNs
Drop the ngrok agent into your customers' networks and tightly scope your access to just the APIs and databases you need. Not their whole subnet.
import "ngrok"
Don't want to package and babysit an agent sidecar? Embed secure tunnels directly into your code with a native agent SDK.
Developer experience matters.
Inspect every detail of your traffic
Watch the flow in real time, then dig into the headers, body, latency, response, and more for every request.
Send traffic logs to your favorite obs platform
Export structured logs to Datadog, Cloudwatch, and Azure Monitor to combine with your existing telemetry.
Use your existing Kubernetes manifests
The ngrok Operator slurps up standardized Ingress and Gateway API resources, then transforms them into endpoints and policies that run on our cloud service and deliver traffic to your pods.
Fully programmable
We integrated ngrok into the dev tools you love. There's an API for every feature to quicky build your own integrations, too. And make them production-ready, declarative, IaC... you get the idea.
All the boxes you need to check
Join millions of developers routing billions of requests every day.
You're using ngrok right now.
ngrok received your request on our global network, filtered it through our WAF, applied a rate limit, routed it to our services, and delivered this web page to your browser.
1on_http_request:2 # say bye to anonymous proxies3 - expressions:4 - "'proxy.anonymous' in conn.client_ip.categories"5 actions:6 - type: deny7 8 # rate limit requests by IP, unless you're a search/AI bot crawling the docs9 - expressions:10 - "!('com.openai.gptbot.ipv4' in conn.client_ip.categories || 'com.google.googlebot.ipv4' in conn.client_ip.categories)"11 - "!req.url.path.startsWith('/docs')"12 actions:13 - type: rate-limit14 config:15 algorithm: sliding_window16 bucket_key:17 - conn.client_ip18 capacity: 1234519 enforce: false20 name: Limit 12345 requests per minute per ip and user agent21 rate: 60s22 23 # rate limit violators get their connection closed instead of 429 errors24 - expressions:25 - actions.ngrok.rate_limit.limited26 actions:27 - type: close-connection28 29 # this is our WAF, dawg30 - actions:31 - type: owasp-crs-request32 config:33 exclude_rule_ids:34 - 93226035 on_error: continue36 process_body: true37 38 # log all WAF decisions in our observability platform and then deny anomalous requests39 - expressions:40 - actions.ngrok.owasp_crs_request.decision == 'deny'41 actions:42 - type: log43 config:44 metadata:45 action: waf deny46 anomaly_score: ${actions.ngrok.owasp_crs_request.anomaly_score}47 first_matched_data: ${actions.ngrok.owasp_crs_request.matched_rules[0].data}48 first_matched_id: ${actions.ngrok.owasp_crs_request.matched_rules[0].id}49 first_matched_message: ${actions.ngrok.owasp_crs_request.matched_rules[0].message}50 first_matched_severity: ${actions.ngrok.owasp_crs_request.matched_rules[0].severity}51 ngrok_error_message: ${actions.ngrok.owasp_crs_request.error.message}52 phase: request53 - type: deny54 55 # block countries to comply with legal requirements56 - expressions:57 - conn.geo.country_code in ['ABC', 'DEF', 'XYZ']58 actions:59 - type: deny60 61 # "we're hiring" easter egg for the curious developer62 - actions:63 - type: add-headers64 config:65 headers:66 x-ngrok-we-are-hiring: https://ngrok.com/careers67 68 # add a custom security.txt file without storing it in s3 or something69 - expressions:70 - req.url.uri.contains('/.well-known/security.txt')71 actions:72 - type: custom-response73 config:74 content: |+75 Contact: mailto:security@ngrok.com76 Canonical: https://ngrok.com/.well-known/security.txt77 Policy: https://ngrok.com/security/disclosure-and-reward-program78 Hiring: https://ngrok.com/careers79 Preferred-Languages: en80 Expires: 2026-11-01T00:00:00Z81 headers:82 Content-Type: text/plain; charset=utf-883 status_code: 20084 85 # sugar URL for invites to our discord community (come say hi!)86 - expressions:87 - req.url.path.startsWith('/discord')88 actions:89 - type: redirect90 config:91 to: https://discord.com/invite/6r7rdGX9fr92 93 # redirect legacy /downloads path to /download94 - actions:95 - type: redirect96 config:97 from: https://ngrok.com/downloads(/|$)(.*)98 status_code: 30199 to: https://ngrok.com/download/$2100 101 # redirect legacy blog path+slug102 - expressions:103 - req.url.path.startsWith('/blog-post')104 actions:105 - type: redirect106 config:107 from: ^(https?://[^/]+)/blog-post/(.*)$108 status_code: 301109 to: $1/blog/$2110 111 # handle /docs by forwarding traffic to our docs hosted in s3112 - expressions:113 - req.url.path.startsWith('/docs')114 actions:115 - type: forward-external116 config:117 url: https://ngrok.docs-provider.com118 119 # handle /blog and asset paths by forwarding traffic to an external provider120 # sorry, we gotta be a little cheeky with these121 - expressions:122 - '["/__blog-manifest","/blog-assets","/blog"].exists(p,req.url.path.startsWith(p))'123 actions:124 - type: forward-external125 config:126 url: https://blog.deployment-provider.com127 128 # handle /, /download, /pricing/, and other paths by forwarding to the same provider, different deployment129 - expressions:130 - '["/__manifest","/assets","/download","/pricing","/schemas"].exists(p,req.url.path.startsWith(p))'131 actions:132 - type: forward-external133 config:134 url: https://frontend.deployment-provider.com135 136 # legacy website paths forward to cms137 - actions:138 - type: url-rewrite139 config:140 from: /(.+)/$141 to: /$1142 - type: forward-external143 config:144 url: https://ngrok.cms-provider.com145 146on_http_response:147 # run waf on all responses148 - actions:149 - type: owasp-crs-response150 config:151 on_error: continue152 process_body: true153 154 # as before, log all those WAF decisions and deny responses155 - expressions:156 - actions.ngrok.owasp_crs_response.decision == 'deny'157 actions:158 - type: log159 config:160 metadata:161 action: waf deny162 anomaly_score: ${actions.ngrok.owasp_crs_response.anomaly_score}163 first_matched_data: ${actions.ngrok.owasp_crs_response.matched_rules[0].data}164 first_matched_id: ${actions.ngrok.owasp_crs_response.matched_rules[0].id}165 first_matched_message: ${actions.ngrok.owasp_crs_response.matched_rules[0].message}166 first_matched_severity: ${actions.ngrok.owasp_crs_response.matched_rules[0].severity}167 ngrok_error_message: ${actions.ngrok.owasp_crs_response.error.message}168 phase: response169 - type: custom-response170 config:171 content: 403 Forbidden172 headers:173 Content-Type: text/plain; charset=utf-8174 status_code: 403You read the whole page. What are you waiting for?
No upfront costs. No contact sales. Pay only for what you use.