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
From 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.
on_http_request:
# send requests with the /api path prefix to the api service
- expressions:
# conditions are CEL expressions, see https://cel.dev
- req.url.path.startsWith('/api')
actions:
- type: forward-internal
config:
url: https://api.internal
# route dynamically based on a header using CEL interpolation
- actions:
- type: forward-internal
config:
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 build your own integrations, too.
All the boxes you need to check
Join millions of developers routing billions of requests every day.
ngrok is so effing amazing
ngrok opened the door for Databricks to rapidly onboard big enterprise customers with comprehensive security much less operational complexity.
ngrok has been super user friendly, works seamlessly, and never gets in our way. Today, our engineers spend less time troubleshooting proxy issues and more time building.
ngrok continues to wow us with great APIs and automation. Establishing secure remote connectivity with our customer’s environments via REST APIs is fast and safe ... and amazing!
ngrok is so useful that I actually enjoy paying for it.
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.
on_http_request:
# say bye to anonymous proxies
- expressions:
- "'proxy.anonymous' in conn.client_ip.categories"
actions:
- type: deny
# rate limit requests by IP, unless you're a search/AI bot crawling the docs
- expressions:
- "!('com.openai.gptbot.ipv4' in conn.client_ip.categories || 'com.google.googlebot.ipv4' in conn.client_ip.categories)"
- "!req.url.path.startsWith('/docs')"
actions:
- type: rate-limit
config:
algorithm: sliding_window
bucket_key:
- conn.client_ip
capacity: 12345
enforce: false
name: Limit 12345 requests per minute per ip and user agent
rate: 60s
# rate limit violators get their connection closed instead of 429 errors
- expressions:
- actions.ngrok.rate_limit.limited
actions:
- type: close-connection
# this is our WAF, dawg
- actions:
- type: owasp-crs-request
config:
exclude_rule_ids:
- 932260
on_error: continue
process_body: true
# log all WAF decisions in our observability platform and then deny anomalous requests
- expressions:
- actions.ngrok.owasp_crs_request.decision == 'deny'
actions:
- type: log
config:
metadata:
action: waf deny
anomaly_score: ${actions.ngrok.owasp_crs_request.anomaly_score}
first_matched_data: ${actions.ngrok.owasp_crs_request.matched_rules[0].data}
first_matched_id: ${actions.ngrok.owasp_crs_request.matched_rules[0].id}
first_matched_message: ${actions.ngrok.owasp_crs_request.matched_rules[0].message}
first_matched_severity: ${actions.ngrok.owasp_crs_request.matched_rules[0].severity}
ngrok_error_message: ${actions.ngrok.owasp_crs_request.error.message}
phase: request
- type: deny
# block countries to comply with legal requirements
- expressions:
- conn.geo.country_code in ['ABC', 'DEF', 'XYZ']
actions:
- type: deny
# "we're hiring" easter egg for the curious developer
- actions:
- type: add-headers
config:
headers:
x-ngrok-we-are-hiring: https://ngrok.com/careers
# add a custom security.txt file without storing it in s3 or something
- expressions:
- req.url.uri.contains('/.well-known/security.txt')
actions:
- type: custom-response
config:
content: |+
Contact: mailto:security@ngrok.com
Canonical: https://ngrok.com/.well-known/security.txt
Policy: https://ngrok.com/security/disclosure-and-reward-program
Hiring: https://ngrok.com/careers
Preferred-Languages: en
Expires: 2026-11-01T00:00:00Z
headers:
Content-Type: text/plain; charset=utf-8
status_code: 200
# sugar URL for invites to our discord community (come say hi!)
- expressions:
- req.url.path.startsWith('/discord')
actions:
- type: redirect
config:
to: https://discord.com/invite/6r7rdGX9fr
# redirect legacy /downloads path to /download
- actions:
- type: redirect
config:
from: https://ngrok.com/downloads(/|$)(.*)
status_code: 301
to: https://ngrok.com/download/$2
# redirect legacy blog path+slug
- expressions:
- req.url.path.startsWith('/blog-post')
actions:
- type: redirect
config:
from: ^(https?://[^/]+)/blog-post/(.*)$
status_code: 301
to: $1/blog/$2
# handle /docs by forwarding traffic to our docs hosted in s3
- expressions:
- req.url.path.startsWith('/docs')
actions:
- type: forward-external
config:
url: https://ngrok.docs-provider.com
# handle /blog and asset paths by forwarding traffic to an external provider
# sorry, we gotta be a little cheeky with these
- expressions:
- '["/__blog-manifest","/blog-assets","/blog"].exists(p,req.url.path.startsWith(p))'
actions:
- type: forward-external
config:
url: https://blog.deployment-provider.com
# handle /, /download, /pricing/, and other paths by forwarding to the same provider, different deployment
- expressions:
- '["/__manifest","/assets","/download","/pricing","/schemas"].exists(p,req.url.path.startsWith(p))'
actions:
- type: forward-external
config:
url: https://frontend.deployment-provider.com
# legacy website paths forward to cms
- actions:
- type: url-rewrite
config:
from: /(.+)/$
to: /$1
- type: forward-external
config:
url: https://ngrok.cms-provider.com
on_http_response:
# run waf on all responses
- actions:
- type: owasp-crs-response
config:
on_error: continue
process_body: true
# as before, log all those WAF decisions and deny responses
- expressions:
- actions.ngrok.owasp_crs_response.decision == 'deny'
actions:
- type: log
config:
metadata:
action: waf deny
anomaly_score: ${actions.ngrok.owasp_crs_response.anomaly_score}
first_matched_data: ${actions.ngrok.owasp_crs_response.matched_rules[0].data}
first_matched_id: ${actions.ngrok.owasp_crs_response.matched_rules[0].id}
first_matched_message: ${actions.ngrok.owasp_crs_response.matched_rules[0].message}
first_matched_severity: ${actions.ngrok.owasp_crs_response.matched_rules[0].severity}
ngrok_error_message: ${actions.ngrok.owasp_crs_response.error.message}
phase: response
- type: custom-response
config:
content: 403 Forbidden
headers:
Content-Type: text/plain; charset=utf-8
status_code: 403You read the whole page. What are you waiting for?
No upfront costs. No contact sales. Pay only for what you use.