Skip to main content
API gateway

One API gateway. Anywhere your services run.

ngrok collapses routing, WAF, observability, failover, and auth into one gateway. Plus, it works the exact same within your VPC, across multiple clouds, on K8s clusters, or from inside a customer’s network.

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

One agent.
Every environment.

The agent connects outbound on port 443, which means no inbound ports, DNS changes, or per-environment gateway. Traffic flows through ngrok’s cloud and out to wherever your services live.

ngrok is your global entry point to anywhere your APIs run. The same agent runs on a laptop, a VM, a Raspberry Pi, an EC2 box, or a Kubernetes cluster.

Block attacks, rate-limit abusers, and authenticate every request at our cloud. Good traffic goes where it should. Bad traffic never reaches your VPC.

One install, one command, anywhere. Agents create unique URLs for each service that’s accessible only through the front door you control in ngrok’s cloud.

Close every port. Because the agent connects outbound, attackers have no way to poke at your origin servers, which remain fully locked down.

# In front of your API servicengrok http 3000 --url https://api.internal
# In front of your app servicengrok http 8080 --url https://app.internal

Build on an expressive rules system. No more cryptic nginx configs or Lua plugins. Instead, compose rules across teams and offload processing to ngrok’s cloud.

Route to new services, simply. Append a new forward-internal rule to your existing policy and you’re off, no matter where the service runs.

on_http_request:  # Block anonymous proxies, Tor exit nodes, and threat lists  - expressions:      - >-        'proxy.anonymous' in conn.client_ip.categories        || 'blocklist.org.firehol.level_1' in conn.client_ip.categories    actions:      - type: deny   # Rate-limit abusers per client IP  - actions:      - type: rate-limit        config:          name: per-ip          algorithm: sliding_window          capacity: 100          rate: 60s          bucket_key:            - conn.client_ip   # Block OWASP top 10 attacks inbound to your apps  - actions:      - type: owasp-crs-request        config:          on_error: deny   # Add security and tracing headers on the way in  - actions:      - type: add-headers        config:          headers:            x-request-id: ${req.id}            strict-transport-security: max-age=31536000   # Route to the internal endpoints you created with the agent  - expressions:      - req.url.path.startsWith('/v1/')    actions:      - type: forward-internal        config:          url: https://api.internal  - actions:      - type: forward-internal        config:          url: https://app.internal on_http_response:  # Block OWASP top 10 attacks outbound from your apps  - actions:      - type: owasp-crs-response        config:          on_error: deny   # Compress responses on the way out  - actions:      - type: compress-response

Why ngrok?

Stop running five tools to do one job. Routing, WAF, rate limiting, auth, and certs all live in one gateway.

Add modern controls to APIs you can’t rewrite. Enforce JWT validation, OAuth, rate limits, and IP rules at our network.

Works the same everywhere your APIs do. AWS, Azure, on-prem, K8s, and customer networks behind one gateway.

Platform owns the front door, teams own their routes. Layered policy that scales from day one.

Self-service from $20/month.

Pay only for what you use and scale as you grow. It really can be that simple.

See pricing
Traffic Policy

Bring your messy config.
Leave with something far simpler.

This is a real production nginx config from a real developer. They shed a ton of code and complexity on their way to something that's far more expressive and elegant. And yes, you can have this too.

1user www-data;2worker_processes auto;3pid /run/nginx.pid;4include /etc/nginx/modules-enabled/*.conf;5worker_rlimit_nofile 65536;6 7events {8    worker_connections 4096;9    multi_accept on;10    use epoll;11    accept_mutex off;12}13 14http {15    # Anti-indexing and security headers16    add_header X-Robots-Tag "noindex, nofollow, noarchive, nosnippet" always;17    add_header X-Frame-Options "DENY" always;18    add_header X-Environment "production" always;19 20    # Basic settings - production optimized21    sendfile on;22    tcp_nopush on;23    tcp_nodelay on;24 25    # Hash table sizes26    types_hash_max_size 4096;27    types_hash_bucket_size 128;28    server_names_hash_bucket_size 128;29    server_names_hash_max_size 2048;30 31    server_tokens off;32 33    # Memory optimizations34    open_file_cache max=10000 inactive=20s;35    open_file_cache_valid 30s;36    open_file_cache_min_uses 2;37    open_file_cache_errors on;38 39    # Client buffers and timeouts40    client_body_buffer_size 128k;41    client_header_buffer_size 4k;42    client_max_body_size 100m;43    large_client_header_buffers 8 32k;44    client_body_timeout 60;45    client_header_timeout 60;46    keepalive_timeout 120;47    send_timeout 60;48    include /etc/nginx/mime.types;49    default_type application/octet-stream;50 51    # Enhanced logging format52    log_format main '$remote_addr - $remote_user [$time_local] "$request" '53                    '$status $body_bytes_sent "$http_referer" '54                    '"$http_user_agent" "$http_x_forwarded_for" '55                    'rt=$request_time uct="$upstream_connect_time" '56                    'uht="$upstream_header_time" urt="$upstream_response_time"';57    access_log /var/log/nginx/access.log main buffer=512k flush=1m;58    error_log /var/log/nginx/error.log warn;59 60    # Gzip compression61    gzip on;62    gzip_comp_level 6;63    gzip_min_length 1024;64    gzip_proxied any;65    gzip_vary on;66    gzip_disable "msie6";67    gzip_types application/json application/xml text/css68               text/javascript image/svg+xml font/ttf;69 70    include /etc/nginx/conf.d/*.conf;71    include /etc/nginx/sites-enabled/*;72}73 74# /etc/nginx/sites-enabled/api.example.com75upstream backend_api {76    server 127.0.0.1:3000;77    keepalive 32;78}79 80server {81    server_name api.example.com;82    listen 443 ssl http2;83 84    # TLS managed by Certbot85    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;86    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;87    include /etc/letsencrypt/options-ssl-nginx.conf;88    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;89    ssl_stapling on;90    ssl_stapling_verify on;91 92    # Security headers93    add_header Strict-Transport-Security "max-age=31536000; preload" always;94    add_header X-Content-Type-Options nosniff always;95    add_header X-XSS-Protection "1; mode=block" always;96    add_header Referrer-Policy "strict-origin-when-cross-origin" always;97 98    # Main proxy99    location / {100        proxy_pass http://backend_api;101        proxy_set_header Host $host;102        proxy_set_header X-Real-IP $remote_addr;103        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;104        proxy_set_header X-Forwarded-Proto $scheme;105        proxy_http_version 1.1;106        proxy_set_header Upgrade $http_upgrade;107        proxy_set_header Connection "upgrade";108 109        # Proxy timeouts and buffering110        proxy_connect_timeout 120s;111        proxy_send_timeout 180s;112        proxy_read_timeout 300s;113        proxy_buffering on;114        proxy_buffer_size 4k;115        proxy_buffers 8 4k;116        proxy_busy_buffers_size 8k;117 118        # Upstream resilience119        proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;120        proxy_next_upstream_timeout 120s;121        proxy_next_upstream_tries 3;122    }123 124    # Static asset caching125    location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|txt)$ {126        expires 1y;127        add_header Cache-Control "public, immutable";128        proxy_pass http://backend_api;129    }130 131    # Health check132    location /health {133        access_log off;134        proxy_pass http://backend_api;135        proxy_connect_timeout 10s;136        proxy_send_timeout 10s;137        proxy_read_timeout 10s;138    }139 140    # Block hidden files141    location ~ /\. {142        deny all;143        access_log off;144        log_not_found off;145    }146}147 148# HTTP -> HTTPS redirect (managed by Certbot)149server {150    if ($host = api.example.com) {151        return 301 https://$host$request_uri;152    }153    listen 80;154    server_name api.example.com;155    return 404;156}
Every environment

Wherever your APIs live,
ngrok meets them there.

Cloud-provider gateways stop at their own cloud. ngrok installs and runs the exact same way in every environment to put one hostname in front of your services wherever they live.

Go multicloud without per-cloud gateways

The agent runs in each cloud and connects outbound. One hostname spans AWS, Azure, and GCP. Endpoint Pools route traffic by region and fail over when a cloud goes down. No DNS headaches or managing a daisy chain of load balancers.

Ingress to multiple clusters

Each cluster runs the ngrok Operator and connects outbound. Route north-south traffic across all of them under one hostname and scale up as easily as adding more replicas of a pod.

Add connectivity to private and on-prem services

The ngrok agent connects outbound from any network and on any hardware, so services once reachable only through a VPN-esque kludge open up to specific identities.

Reach into customer networks, too

Embed the agent in software you ship. Each customer install becomes a routable origin under one gateway, whether you have one install or ten thousand.

Composable policy

Platform owns the front door.
Service teams own their routes.

Most gateways force you to choose between centralized control and team velocity. ngrok lets the right team manage the right policy and composes it all together on our cloud.

Fig. 1 – Platform team’s front door policy
on_http_request:  # Block anonymous proxies and Tor exit nodes  - expressions:      - "'proxy.anonymous' in conn.client_ip.categories"    actions:      - type: deny   # Rate-limit abusers per client IP  - actions:      - type: rate-limit        config:          name: per-ip          algorithm: sliding_window          capacity: 100          rate: 60s          bucket_key:            - conn.client_ip   # Block OWASP top 10 attacks at ngrok's cloud  - actions:      - type: owasp-crs-request        config:          on_error: deny   # ... and a whole bunch more
Fig. 2 – The API team’s api.yaml policy
on_http_request:  # Rewrite legacy /v1/* paths to /v2/*  - expressions:      - req.url.path.startsWith('/v1/')    actions:      - type: url-rewrite        config:          from: ^/v1/(.*)$          to: /v2/$1   # Ship a structured event for every request  # to the observability pipeline  - actions:      - type: log        config:          metadata:            team: api-platform            request_id: ${req.id}            method: ${req.method}            path: ${req.url.path}            client_ip: ${conn.client_ip}            country: ${conn.geo.country_code}            user_agent: ${req.user_agent.name}            is_bot: ${req.user_agent.is_bot}
Decoration

Stop running five tools. Start running one gateway.

Free to start. Three minutes from signup to first request.

Decoration

Frequently asked questions