Skip to main content
NewIntroducing AI Gateway

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

  • Veritas
  • Sonar
  • Schneider Electric
  • Okta
  • Microsoft
  • GitHub
  • Zoom
  • Twilio
  • Databricks
  • Cyera
  • Tyler Technologies
  • Solink
Decoration

Try ngrok by sharing a local app. Right now.

$ brew install ngrokDownload and setup instructions
Decoration
Decoration

Okay, but how does it work?

Connect anything with Endpoints, Traffic Policy, and Secure Tunnels.

Decoration
Endpoints & Traffic Policy

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.

Fig. 1 – Traffic flows through
Endpoints & Traffic Policy
Endpoints and 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.

Fig. 2 – Traffic policy examples
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')}.internal

Even more ways to control and transform your traffic

Explore actions
Mutual TLS
OAuth + OIDC
Basic Auth
URL rewrite + redirects
Modify headers
Custom response
IP restrictions
Webhook verification

Stop cobbling your infrastructure together.

Security, performance, and resiliency built in by default.

Secure Tunnels

Connect to services anywhere, no firewall changes required.

Install a lightweight agent to deliver traffic to any service through a secure tunnel.

Fig. 3 – Anatomy of a secure tunnel
Anatomy of a secure tunnel
Environment Agnostic

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.

Least privileged access
Zero Open Ports

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.

No open ports
Least Privileged Access

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.

Least privileged access
Native SDKs

import "ngrok"

Don’t want to package and babysit an agent sidecar? Embed secure tunnels directly into your code with a native agent SDK.

Import ngrok
More about secure tunnels

Join millions of developers routing billions of requests every day.

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
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
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
georges
@multiplegeorges

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.

Fig. 4 – 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

  # 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: 403
Decoration

You read the whole page. What are you waiting for?

No upfront costs. No contact sales. Pay only for what you use.

Decoration