Skip to main content

Connect anything,
anywhere.

ngrok is developer infrastructure that routes and secures traffic to your apps, APIs, and AI models.

  • Calendly
  • Cyera
  • Databricks
  • GitHub
  • Grafana
  • Harvey
  • Hugging Face
  • Mercor
  • Microsoft
  • Okta
  • Open AI
  • Perplexity
  • Ramp
  • Schneider Electric
  • Twilio
  • Vercel
  • Windsurf
  • Zoom
Share localhost

Try ngrok by sharing a local app. Right now.

$ brew install ngrok
$ scoop install ngrok
$ docker pull ngrok/ngrok
Read https://ngrok.com/agent-setup/prompt.md and follow it to get my app online with ngrok.

Developers go on and on about us.

Scott Motte
@motdotla

ngrok is so effing amazing

Ihor Leshko
Director of Engineering

ngrok opened the door for Databricks to rapidly onboard big enterprise customers with comprehensive security much less operational complexity.

Databricks
Kevin Simper
@kevinsimper

I wouldn't know how to work without ngrok.

Lyzi Diamond
@lyzidiamond

OMG life changer. I can't imagine how I lived without ngrok.

Wesley Gorman
Senior Director, Engineering, Zendesk

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.

Zendesk
Brian Ketelsen
@bketelsen

Just used ngrok for the first time to show off a new GopherCon web layout. Wow, that's awesome.

Cassidy Williams
@cassidoo

ngrok saves me so much time during development. What a tool.

Austin Cottrell
Cloud Infrastructure Engineer

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!

Copado
Steve Kinney
@stevekinney

ngrok makes the hard parts of networking feel effortless.

Every security checkbox, checked.

  • SOC2 Type II
  • HIPAA & BAA
  • GDPR
  • CCPA
  • EU-US DPF
  • RBAC
  • SSO & SCIM
  • Audit logs
  • Data residency
  • White-labeling
Trust Center

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.

ngrok.com’s Traffic Policy
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   # 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: 403
Meet the people who build ngrok on ngrok

Your turn.
Go put something on the internet.