# What is ngrok? Source: https://ngrok.com/docs/about/index ngrok is developer infrastructure that routes and secures traffic to your apps, APIs, and AI models, wherever they run. ngrok is developer infrastructure that routes and secures traffic to your apps, APIs, and AI models. It manages connectivity between all of the tools and platforms you use to put your services online, no matter where they're deployed or what network they're on. ngrok's core products are Gateway, AI Gateway, and Share Localhost. Gateway owns the connectivity primitives, and the other products integrate them. ## Products An all-in-one cloud networking platform that secures, transforms, and routes traffic to your services. Unlike cloud provider gateways locked to their own networks, ngrok works the same in AWS, Azure, GCP, on-prem, and your customer's networks. Route, secure, and manage traffic to any LLM—cloud or local—through one endpoint. Use OpenAI, Anthropic, and more without signing up for each provider. Expose a service running on `localhost` to the internet, even from behind a NAT or firewall. Skip port forwarding, DNS changes, and firewall rules entirely. ## Why ngrok? ngrok works everywhere, and it works *the same* everywhere: AWS, GCP, Azure, your on-prem data center, a Raspberry Pi, or your laptop. Collapse the reverse proxy, load balancer, API gateway, WAF, and DDoS protection into one platform with unified configuration and observability. Authentication, attack protection, and traffic acceleration are handled for you, so you can focus on your app instead of networking configuration. Stop wrestling with CIDRs, ports, and TLS certificates. Work with application primitives like authentication and request routing instead. Run ngrok in local development exactly the way you run it in production, eliminating the bugs that come from environment drift. Public, predictable pricing that anyone can start with using a credit card. You pay only for what you use. ## What can you do with ngrok? ### Build and test * **[Share a local app](/docs/share-localhost/overview)**: Put anything on localhost behind a stable, secure public URL. * **[Test webhooks locally](/docs/share-localhost/webhooks)**: Inspect, replay, and verify requests while you build. * **[Connect local MCP servers](/docs/gateway/api-gateway/mcp-gateway)**: Let ChatGPT and Claude reach tools on your machine. ### Play * **[Host a Minecraft server](/docs/gateway/examples/minecraft)**: Let your friends join, and block the griefers, without port forwarding. ### Deploy and run * **[Route traffic to self-hosted models](/docs/ai-gateway/guides/use-a-model-you-run-yourself)**: Privately connect to any hardware, in any network, over an AI gateway. * **[Run preview and CI workloads](/docs/gateway/examples/ephemeral-workloads)**: Give every ephemeral service a secure public URL. ### Deliver and connect * **[Deliver and secure APIs](/docs/gateway/api-gateway/get-started)**: Route, authenticate, and protect requests through an API gateway. * **[Reach customer networks](/docs/gateway/site-to-site-connectivity/overview)**: Connect to customer APIs and databases with site-to-site connectivity. * **[Receive webhooks on-prem](/docs/gateway/webhooks-to-on-prem/overview)**: Verify signatures for 70+ providers at one webhook gateway. * **[Connect privately across networks](/docs/gateway/endpoints/internal-endpoints)**: Reach across network boundaries without public URLs, open ports, or VPNs. * **[Connect device fleets](/docs/gateway/device-gateway/overview)**: Reach devices in the field through a device gateway, no inbound ports. * **[Open SSH and RDP sessions](/docs/gateway/ssh-rdp)**: Reach a shell or desktop on a remote machine without a VPN. ## Gateway primitives Gateway owns the primitives that move traffic. AI Gateway and Share Localhost build on them, so what you learn once applies wherever you use ngrok. * [Endpoints](/docs/gateway/endpoints/): The URLs traffic arrives on, whether created by an agent or in the cloud. * [Traffic Policy](/docs/gateway/traffic-policy/): Filter, match, manage, and orchestrate traffic to your endpoints. * [Domains & TLS](/docs/gateway/domains/): Serve traffic on your own hostnames, and manage the certificates that terminate TLS. * [Traffic observability](/docs/obs/): Capture and replay requests and responses. * [Identity and access](/docs/iam/): Manage credentials for human users and automated processes. ## How ngrok works Each product has its own architecture and request flow. How ngrok's global cloud service and agent software deliver traffic to your upstream services. How ngrok.ai routes requests, validates access keys, and handles failover across model providers. How the ngrok agent creates encrypted tunnels that deliver internet traffic to your local machine. ## What's next? * Start with the product that fits your use case: [Gateway](/docs/gateway/overview), [AI Gateway](/docs/ai-gateway/overview), or [Share Localhost](/docs/share-localhost/overview). * Browse the [Gateway examples collection](/docs/gateway/examples/) to see common patterns implemented end to end. * Check the [FAQ](/docs/faq) for common questions, or the [glossary](/docs/glossary) for ngrok terminology. # Agent SDKs Source: https://ngrok.com/docs/agent-sdks/index Learn about ngrok Agent SDKs for embedding ngrok directly into your applications using Go, JavaScript, Python, and Rust libraries. Agent SDKs enable you to embed ngrok directly into your application. They allow you to programmatically create ngrok endpoints. You handle connections from ngrok's cloud service just as if you opened a socket to listen on a port. ## Example usage 1. Install the SDK: ```sh theme={null} go get golang.ngrok.com/ngrok/v2 ``` 2. Add the following to your app: ```go theme={null} import "golang.ngrok.com/ngrok/v2" func connectNgrok() { fwd, err := ngrok.Forward(context.Background(), ngrok.WithUpstream("http://localhost:8085"), ) if err != nil { log.Fatal(err) } log.Println("Available at:", fwd.URL()) select {} } ``` 3. Set your authtoken and restart your app: ```sh theme={null} export NGROK_AUTHTOKEN= ``` 1. Install the SDK: ```sh theme={null} npm install @ngrok/ngrok ``` 2. Add the following to your app: ```js theme={null} const ngrok = require("@ngrok/ngrok"); async function forwardToApp() { const forwarder = await ngrok.forward({ addr: "localhost:8085", authtoken_from_env: true, }); console.log(`Available at: ${forwarder.url()}`); } ``` 3. Set your authtoken and restart your app: ```sh theme={null} export NGROK_AUTHTOKEN= ``` 1. Install the SDK: ```sh theme={null} pip install ngrok ``` 2. Add the following to your app: ```python theme={null} import ngrok def connect_ngrok(): forwarder = ngrok.forward("localhost:8085", authtoken_from_env=True) print(f"Available at: {forwarder.url()}") ``` 3. Set your authtoken and restart your app: ```sh theme={null} export NGROK_AUTHTOKEN= ``` 1. Install the SDK: ```sh theme={null} cargo add ngrok ``` 2. Add the following to your app: ```rust theme={null} use ngrok::config::ForwarderBuilder; use ngrok::prelude::*; use url::Url; async fn connect_ngrok() -> Result<(), Box> { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let mut listener = sess .http_endpoint() .listen_and_forward(Url::parse("http://localhost:8085")?) .await?; println!("Available at: {:?}", listener.url()); let _ = listener.join().await; Ok(()) } ``` 3. Set your authtoken and restart your app: ```sh theme={null} export NGROK_AUTHTOKEN= ``` ## Supported languages | Language | Docs | Quickstart | Repository | Status | | - | - | - | - | - | | Go | [ngrok-go docs](https://pkg.go.dev/golang.ngrok.com/ngrok/v2) | [ngrok-go quickstart](/docs/getting-started/go/) | [github.com/ngrok/ngrok-go](https://github.com/ngrok/ngrok-go) | Stable | | Rust | [ngrok-rust docs](https://docs.rs/ngrok/latest/ngrok/) | [ngrok-rust quickstart](/docs/getting-started/rust/) | [github.com/ngrok/ngrok-rust](https://github.com/ngrok/ngrok-rust) | Stable | | Python | [ngrok-python docs](https://ngrok.github.io/ngrok-python/) | [ngrok-python quickstart](/docs/getting-started/python/) | [github.com/ngrok/ngrok-python](https://github.com/ngrok/ngrok-python) | Stable | | JavaScript | [ngrok-javascript docs](https://ngrok.github.io/ngrok-javascript/) | [ngrok-javascript quickstart](/docs/getting-started/javascript/) | [github.com/ngrok/ngrok-javascript](https://github.com/ngrok/ngrok-javascript) | Stable | | Java | | | [github.com/ngrok/ngrok-java](https://github.com/ngrok/ngrok-java) | Alpha | ## When to use Agent SDKs Agent SDKs are often a better fit for your use case over using the [ngrok agent](/docs/gateway/agent/). This is especially true when running ngrok with production apps. Agent SDKs are a better choice if: * You don't want to manage the lifetime of a separate agent process * You don't want to bundle and distribute the ngrok agent * The ngrok agent doesn't run on your target platform * The ngrok agent's resource requirements are too high for your target platform * You want fine-grained programmatic control over the agent's functionality ## Agent SDKs pricing The agent SDKs are available to all ngrok users at no additional charge. You only incur costs if the resources provisioned by the SDKs incur a cost. For more information, see the [ngrok Pricing page](https://ngrok.com/pricing). # Claude Code Source: https://ngrok.com/docs/ai-gateway/agents/claude-code Route Claude Code through the ngrok AI Gateway with two environment variables to track usage, fail over, and bill to one access key. [Claude Code](https://code.claude.com/docs) is Anthropic's agentic coding tool for the terminal, IDEs, and desktop. It speaks the Anthropic Messages API, so pointing its base URL at the AI Gateway sends every model request through ngrok.ai, where it shows up in [observability](/docs/ai-gateway/observability) and follows your [access key configuration](/docs/ai-gateway/guides/access-key-configurations). ## What you'll need * [Claude Code](https://code.claude.com/docs/en/setup) installed. * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai). ## Point the CLI at the gateway Set two environment variables in your shell: ```bash theme={null} export ANTHROPIC_BASE_URL="https://gateway.ngrok.ai" export ANTHROPIC_AUTH_TOKEN="ng-xxxxx-g1-xxxxx" ``` * `ANTHROPIC_BASE_URL` is the gateway URL without `/v1`, since Claude Code appends `/v1/messages` itself * `ANTHROPIC_AUTH_TOKEN` is your access key, which Claude Code sends as an `Authorization: Bearer` header `ANTHROPIC_AUTH_TOKEN` takes precedence over a saved claude.ai login, so Claude Code uses your access key as soon as you start it. If `ANTHROPIC_API_KEY` is also set in your environment, unset it to avoid an auth conflict warning. To keep the configuration across terminals and for background agents, move the same values into the `env` block of `~/.claude/settings.json`: ```json theme={null} { "env": { "ANTHROPIC_BASE_URL": "https://gateway.ngrok.ai", "ANTHROPIC_AUTH_TOKEN": "ng-xxxxx-g1-xxxxx" } } ``` Don't put your access key in a project's `.claude/settings.json`. That file is committed and shared with everyone who clones the repository. ## Test the connection Before you start Claude Code, send a short request straight to the gateway from the same shell: ```bash theme={null} curl https://gateway.ngrok.ai/v1/messages \ -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{"model": "claude-sonnet-5-5", "max_tokens": 16, "messages": [{"role": "user", "content": "Say hello."}]}' ``` A JSON response that starts with `{"id":"msg_` means the URL and access key work. A `401` means the gateway rejected the access key. Then start `claude`, run `/status`, and check two lines: * `Anthropic base URL` shows `https://gateway.ngrok.ai` * `Auth token` names `ANTHROPIC_AUTH_TOKEN` Send any prompt, then confirm the request in AI Gateway [observability](/docs/ai-gateway/observability) in [app.ngrok.ai](https://app.ngrok.ai). ## Choose a model Claude Code's built-in model aliases, such as `opus`, `sonnet`, and `haiku`, resolve to Anthropic model IDs like `claude-sonnet-5-5`. The gateway finds those IDs in the [model catalog](/docs/ai-gateway/reference/model-catalog), so `/model` and `--model` work without extra setup. To pin the model behind each alias, set these variables alongside the base URL: ```bash theme={null} export ANTHROPIC_DEFAULT_OPUS_MODEL="claude-opus-5-5" export ANTHROPIC_DEFAULT_SONNET_MODEL="claude-sonnet-5-5" export ANTHROPIC_DEFAULT_HAIKU_MODEL="claude-haiku-5-5" ``` Use the catalog's model IDs rather than dated snapshot IDs like `claude-haiku-4-5-20251001`, which the gateway doesn't match. To add the gateway's Claude models to the `/model` picker, turn on Claude Code's gateway model discovery: ```bash theme={null} export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 ``` At startup, Claude Code reads the gateway's model list, which only includes models your access key is allowed to call, and adds the Claude models in it, such as `anthropic/claude-sonnet-5-5`. The gateway doesn't serve Anthropic's token counting endpoint, so `/context` shows estimated token counts. Claude Code only works with Claude models through the gateway. `/v1/messages` only reaches providers that support the Anthropic Messages API, and Anthropic doesn't support routing Claude Code to other models. ## Use other surfaces The CLI reads the shell variables and `~/.claude/settings.json`. Other surfaces need the same two values in a different place: * VS Code extension: set them in `claudeCode.environmentVariables` in your VS Code user settings * Claude Agent SDK: pass them in the SDK's `env` option, which sets the environment of the Claude Code process it spawns * Claude desktop app: turn on Developer Mode, then enter the gateway details under **Developer** > **Configure Third-Party Inference** See Claude Code's guide to [connecting to an LLM gateway](https://code.claude.com/docs/en/llm-gateway-connect#configure-each-surface) for each surface. ## Troubleshooting | Symptom | Fix | | - | - | | `401` errors | Check that `ANTHROPIC_AUTH_TOKEN` holds a current access key from [app.ngrok.ai](https://app.ngrok.ai/keys) | | Claude Code asks you to log in | Set the variables in your shell or in `~/.claude/settings.json`, not in a project settings file, which Claude Code reads only after its first-run setup | | `/status` doesn't show the gateway URL | Start `claude` from the shell where you exported the variables, or move them to `~/.claude/settings.json` | | A request fails with a model error | Check that the model ID matches the [model catalog](/docs/ai-gateway/reference/model-catalog) exactly, without a date suffix, and that your [access key configuration](/docs/ai-gateway/guides/access-key-configurations) allows it | | Long responses stop partway through | Set `totalTimeout` to `15m` in [account settings](/docs/ai-gateway/guides/account-settings#streaming-responses). A saved total timeout caps the whole stream, and saving any account setting stores the `2m` default | For gateway-side errors, see [Debug failed requests](/docs/ai-gateway/guides/debugging). ## Next steps * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Scope what an access key can call * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Observability](/docs/ai-gateway/observability): See which model served each request # Codex Source: https://ngrok.com/docs/ai-gateway/agents/codex Add the ngrok AI Gateway as a custom model provider in the OpenAI Codex CLI to track usage and route requests with one access key. [Codex](https://github.com/openai/codex) is OpenAI's coding agent for the terminal. It reaches the AI Gateway as a custom model provider over the OpenAI Responses API, so its requests show up in [observability](/docs/ai-gateway/observability) and follow your [access key configuration](/docs/ai-gateway/guides/access-key-configurations). ## What you'll need * The [Codex CLI](https://github.com/openai/codex#quickstart) installed. * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai). ## Add the gateway as a model provider Export your access key in your shell profile, such as `~/.zshrc` or `~/.bashrc`, so Codex can read it: ```bash theme={null} export NGROK_AI_GATEWAY_KEY="ng-xxxxx-g1-xxxxx" ``` Save Codex's built-in model catalog to a file: ```bash theme={null} codex debug models > ~/.codex/ngrok-models.json ``` Codex looks up each model's context window, reasoning levels, and tools in a catalog. The gateway's model list doesn't include those details, so without the file Codex warns that model metadata isn't found and falls back to generic defaults. Run the command again after you update Codex to pick up new models. Add the gateway as a provider in `~/.codex/config.toml`: ```toml theme={null} model = "gpt-5.5" model_provider = "ngrok" model_catalog_json = "~/.codex/ngrok-models.json" [model_providers.ngrok] name = "ngrok AI Gateway" base_url = "https://gateway.ngrok.ai/v1" env_key = "NGROK_AI_GATEWAY_KEY" wire_api = "responses" ``` This configuration: * Defines a provider named `ngrok` that points at the gateway * Reads your access key from `NGROK_AI_GATEWAY_KEY` and sends it as an `Authorization: Bearer` header * Uses the Responses API, the only wire protocol current Codex versions support * Makes `ngrok` the default provider, with `gpt-5.5` as the default model * Loads model details from the catalog you saved Keep the provider in your user-level `~/.codex/config.toml`. Codex ignores `model_provider` and `model_providers` in a project's `.codex/config.toml`. The provider ID can't be `openai`, `ollama`, or `lmstudio`, which Codex reserves for its built-in providers. ## Test the connection Send a single prompt from the command line: ```bash theme={null} codex exec "Explain what a hash map is in two sentences." ``` The header Codex prints before the response should list `provider: ngrok` and your model. Confirm the request in AI Gateway [observability](/docs/ai-gateway/observability) in [app.ngrok.ai](https://app.ngrok.ai). ## Choose a model Codex only supports the `/v1/responses` API. Check the [model catalog](/docs/ai-gateway/reference/model-catalog) before you point Codex at a non-OpenAI model. Pass `--model` (or `-m`) to start a session on a different model: ```bash theme={null} codex --model gpt-5.4 ``` Pick a model that's in both your saved catalog and the [ngrok.ai model catalog](/docs/ai-gateway/reference/model-catalog), and use its plain ID. A `provider:model` name such as `openai:gpt-5.5` reaches the gateway, but Codex doesn't find it in the catalog and falls back to generic defaults. ## Keep OpenAI as your default To keep Codex on your OpenAI login by default and use the gateway only when you ask for it, put the gateway settings in a profile instead of `config.toml`. Create `~/.codex/ngrok.config.toml`: ```toml theme={null} model_provider = "ngrok" model = "gpt-5.5" model_catalog_json = "~/.codex/ngrok-models.json" [model_providers.ngrok] name = "ngrok AI Gateway" base_url = "https://gateway.ngrok.ai/v1" env_key = "NGROK_AI_GATEWAY_KEY" wire_api = "responses" ``` Start a gateway session with the profile: ```bash theme={null} codex --profile ngrok ``` Codex loads `config.toml` first, then layers the profile on top, so sessions without `--profile ngrok` stay on OpenAI. ## Troubleshooting | Symptom | Fix | | - | - | | `Reconnecting...` messages, then `401 Unauthorized` | Check that `NGROK_AI_GATEWAY_KEY` is exported in the shell that starts Codex, and that it holds a current access key | | Requests still go to OpenAI | Move `model_provider` and `[model_providers.ngrok]` to `~/.codex/config.toml` or a profile, since a project config can't set them | | `Model metadata for not found` at startup | Save the model catalog and set `model_catalog_json`, then use a plain model ID that's in it | | Long responses stop partway through | Set `totalTimeout` to `15m` in [account settings](/docs/ai-gateway/guides/account-settings#streaming-responses). A saved total timeout caps the whole stream, and saving any account setting stores the `2m` default | For gateway-side errors, see [Debug failed requests](/docs/ai-gateway/guides/debugging). ## Next steps * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Scope what an access key can call * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Observability](/docs/ai-gateway/observability): See which model served each request # Cursor Source: https://ngrok.com/docs/ai-gateway/agents/cursor Point Cursor's OpenAI base URL override at the ngrok AI Gateway to chat with catalog or self-hosted models, and learn what it doesn't cover. [Cursor](https://cursor.com) is a code editor with a built-in agent. It reaches custom models by overriding the base URL of its OpenAI provider, so the AI Gateway stands in for OpenAI. This needs a paid Cursor plan. Free plans can only use Cursor's **Auto** model and can't select a named model, which is what a model you add yourself is. ## What you'll need * [Cursor](https://cursor.com/download) installed, on a paid plan. * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai). ## Point Cursor at the gateway Open **Cursor Settings**, go to **Models**, and expand **API Keys**: 1. Turn on **OpenAI API Key** and paste your ngrok.ai access key. The field is labeled OpenAI, but Cursor sends the key to whatever endpoint you configure below. 2. Turn on **Override OpenAI Base URL** and enter `https://gateway.ngrok.ai/v1`. Keep the `/v1` suffix: Cursor appends `/chat/completions` to whatever you enter. 3. Select **+ Add model**, enter a model ID from the [model catalog](/docs/ai-gateway/reference/model-catalog), such as `gpt-5.5`, and enable it. Use `provider:model` to pin a provider. 4. Select **Verify** to confirm the gateway answers. The base URL override is global. While it's on, Cursor routes its other OpenAI-model traffic to the gateway too, which fails for models your access key can't reach. Turn the override off to go back to Cursor's own models. ## Test the connection Pick your model in the chat model selector and send a prompt: Explain what a hash map is in two sentences. Confirm the request in AI Gateway [observability](/docs/ai-gateway/observability) in [app.ngrok.ai](https://app.ngrok.ai). ## What the override doesn't cover Expect chat, not the whole editor. Three Cursor behaviors limit how far a custom model reaches: * Tab completion always uses Cursor's built-in models. It never reaches your endpoint. * Agent and Edit also rely on Cursor's own models, so Cursor may refuse them while a custom API key is set and ask you to disable the key. * Where agent runs do work, Cursor's tool definitions over an overridden base URL aren't strictly OpenAI-shaped, so tool calls can fail against a self-hosted model even when plain chat succeeds. ## Use a model you run yourself To reach a model on your own machine, register it as a [custom provider](/docs/ai-gateway/concepts/custom-providers), then add its `provider:model` name as a model, such as `ollama:llama3.2`. See [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself) for the full setup. ## Next steps * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Scope what an access key can call * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Observability](/docs/ai-gateway/observability): See which model served each request # Hermes Source: https://ngrok.com/docs/ai-gateway/agents/hermes Install the ngrok AI Gateway plugin for Hermes Agent to use your gateway's models as a Hermes model provider with one access key. [Hermes Agent](https://hermes-agent.nousresearch.com) is Nous Research's open-source AI agent for the terminal. The [ngrok AI Gateway plugin](https://github.com/ngrok/hermes-ngrok) adds the gateway as a Hermes model provider, so Hermes can use every model your access key reaches. ## What you'll need * [Hermes Agent](https://hermes-agent.nousresearch.com) 0.21.6 or later installed. * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai/keys). ## Install the plugin Install the plugin with the Hermes CLI: ```bash theme={null} hermes plugins install ngrok-ai-gateway ``` Enter your access key when prompted. Hermes saves it as `NGROK_AI_GATEWAY_KEY`. Plugins are installed per Hermes profile, so install it again in any other profile that needs it. ## Choose a model Run `hermes model` and select **ngrok AI Gateway**, or pass `--provider ngrok` when you start Hermes. The model list comes from your gateway, so it shows the models your access key is allowed to call, by full catalog ID such as `openai/gpt-5.5`. Model IDs follow the gateway's [model naming](/docs/ai-gateway/concepts/models): a model ID such as `gpt-5.5`, or `provider:model` to pin a provider, such as `openai:gpt-5.5`. ## Test the connection Send a single prompt from the command line: ```bash theme={null} hermes chat --provider ngrok --model gpt-5.5 -q "Explain what a hash map is in two sentences." ``` Confirm the request in AI Gateway [observability](/docs/ai-gateway/observability) in [app.ngrok.ai](https://app.ngrok.ai). ## Use a cheaper model for background tasks Background tasks like context compression and title generation use your main model. To send them to a cheaper model through the gateway, set the provider and model for each task in `~/.hermes/config.yaml`: ```yaml theme={null} auxiliary: compression: provider: ngrok model: gpt-5.4-mini title_generation: provider: ngrok model: gpt-5.4-mini ``` ## What the plugin sends The plugin sends chat and model list requests to `https://gateway.ngrok.ai/v1` with your access key. Each request carries a `User-Agent: hermes-agent/ (ngrok-plugin)` header. The plugin makes no other network requests, doesn't read files, and doesn't run background processes. ## Next steps * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Scope what an access key can call * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Observability](/docs/ai-gateway/observability): See which model served each request # Agents Source: https://ngrok.com/docs/ai-gateway/agents/index Route Claude Code, Codex, Cursor, Hermes, and other coding agents through the ngrok AI Gateway to share model access and track usage. **Prerequisite**: Complete the [Quickstart](/docs/ai-gateway/quickstart) to create an access key on [app.ngrok.ai](https://app.ngrok.ai). Coding agents that let you change their base URL can send their model requests through the AI Gateway. Each agent then authenticates with an ngrok.ai [access key](/docs/ai-gateway/concepts/access-keys) instead of a separate provider account, and every request shows up in one place. Routing agents through the gateway gives you: * One access key for every provider in the [model catalog](/docs/ai-gateway/reference/model-catalog), billed to your credits or your own provider keys * Per-request [observability](/docs/ai-gateway/observability), with usage you can group by access key * [Fallback models](/docs/ai-gateway/guides/configure-fallback-models) and automatic retries when a provider fails * Limits on which providers and models each key can call * A path to [models you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself), such as Ollama or vLLM ## Supported agents Anthropic's agentic coding tool, configured with two environment variables OpenAI's terminal coding agent, configured as a custom model provider AI code editor, configured with an OpenAI base URL override Nous Research's terminal agent, configured with the ngrok plugin Open-source terminal agent, configured as a custom provider Terminal coding agent, configured in its models file Code editor with an agent panel, configured as an OpenAI-compatible provider Any agent that accepts a custom OpenAI or Anthropic base URL ## How each agent connects Each agent speaks one API format, which decides the base URL it needs: | Agent | API format | Base URL | | - | - | - | | Claude Code | Anthropic Messages | `https://gateway.ngrok.ai` | | Codex | OpenAI Responses | `https://gateway.ngrok.ai/v1` | | Cursor | OpenAI Chat Completions | `https://gateway.ngrok.ai/v1` | | Hermes | OpenAI Chat Completions | `https://gateway.ngrok.ai/v1`, set by the plugin | | OpenCode | OpenAI Chat Completions | `https://gateway.ngrok.ai/v1` | | Pi | OpenAI Chat Completions | `https://gateway.ngrok.ai/v1` | | Zed | OpenAI Chat Completions | `https://gateway.ngrok.ai/v1` | The format also decides which models an agent can reach. Chat Completions reaches the most providers. The Messages and Responses formats only reach providers that support them, so Claude Code works with Claude models and Codex works best with OpenAI models. ## Set up for agent traffic Coding agents send long, streaming requests, often many per task. A few settings keep that traffic manageable: * Create a separate access key for each agent or teammate, so you can [group usage by access key](/docs/ai-gateway/observability) and revoke one without touching the others. * Set `totalTimeout` to `15m` in [account settings](/docs/ai-gateway/guides/account-settings#streaming-responses). A saved total timeout caps the whole stream, and saving any account setting stores the `2m` default, which can cut off a long agent response. * Scope each key to the models it needs with an [access key configuration](/docs/ai-gateway/guides/restrict-providers-and-models). ## Next steps * [Claude Code](/docs/ai-gateway/agents/claude-code): Route Claude Code through the gateway * [Codex](/docs/ai-gateway/agents/codex): Add the gateway as a Codex model provider * [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself): Reach a model on your own machine, such as Ollama or vLLM # OpenCode Source: https://ngrok.com/docs/ai-gateway/agents/opencode Connect OpenCode to the ngrok AI Gateway to reach any model in the catalog, or a model you run yourself, with one access key. [OpenCode](https://opencode.ai) is an open-source terminal coding agent. It reaches the AI Gateway as a custom OpenAI-compatible provider, so one access key covers every model you add to its config. ## What you'll need * [OpenCode](https://opencode.ai/docs/#install) installed. * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai). ## Add the gateway as a provider Export your access key so OpenCode can read it: ```bash theme={null} export NGROK_AI_GATEWAY_KEY="ng-xxxxx-g1-xxxxx" ``` Add the gateway as a provider in `~/.config/opencode/opencode.json`, or in an `opencode.json` beside your project: ```json theme={null} { "$schema": "https://opencode.ai/config.json", "provider": { "ngrok": { "npm": "@ai-sdk/openai-compatible", "name": "ngrok AI Gateway", "options": { "baseURL": "https://gateway.ngrok.ai/v1", "apiKey": "{env:NGROK_AI_GATEWAY_KEY}" }, "models": { "gpt-5.5": { "name": "GPT-5.5 (via ngrok)", "limit": { "context": 1050000, "output": 128000 } }, "claude-sonnet-5-5": { "name": "Claude Sonnet 5.5 (via ngrok)", "limit": { "context": 1000000, "output": 128000 } } } } } } ``` Each key in `models` is the model name OpenCode sends to the gateway. Use any model ID from the [model catalog](/docs/ai-gateway/reference/model-catalog), or `provider:model` to pin a provider. The `limit` values are optional, but they let OpenCode track how much context is left. ## Test the connection Start OpenCode, run `/models`, and pick one of the models under **ngrok AI Gateway**. Or send a single prompt from the command line: ```bash theme={null} opencode run -m "ngrok/gpt-5.5" "Explain what a hash map is in two sentences." ``` The `-m` value is your provider ID, a slash, then the model key from `opencode.json`. Confirm the request in AI Gateway [observability](/docs/ai-gateway/observability) in [app.ngrok.ai](https://app.ngrok.ai). ## Use a model you run yourself To reach a model on your own machine, register it as a [custom provider](/docs/ai-gateway/concepts/custom-providers), then add its `provider:model` name to `models`, such as `ollama:llama3.2`. See [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself) for the full setup. ## Next steps * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Scope what an access key can call * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Observability](/docs/ai-gateway/observability): See which model served each request # Other agents Source: https://ngrok.com/docs/ai-gateway/agents/other Connect any coding agent that accepts a custom OpenAI or Anthropic base URL to the ngrok AI Gateway with three values. Coding agents that let you replace their built-in provider with a custom endpoint can usually reach the AI Gateway, even without a dedicated guide. Want instructions for a specific coding agent? Log in to [app.ngrok.ai](https://app.ngrok.ai), click **Give feedback**, and let the team know which one. ## What you'll need * A coding agent that accepts a custom base URL and API key. * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai). ## Connect your agent Every agent needs the same three values: | Value | What to enter | | - | - | | Base URL | `https://gateway.ngrok.ai/v1` for OpenAI-format agents, `https://gateway.ngrok.ai` for Anthropic-format agents | | API key | Your [access key](https://app.ngrok.ai/keys), such as `ng-xxxxx-g1-xxxxx` | | Model | A model ID from the [model catalog](/docs/ai-gateway/reference/model-catalog), or `provider:model` to pin a provider | Anthropic-format agents append `/v1/messages` to the base URL themselves, so leave `/v1` off for those. Agents that fill their model picker from `GET /v1/models` see the models your access key is allowed to call, listed by full catalog ID such as `openai/gpt-5.5`. Those IDs work as the model value, too. ## Pick an API format The gateway serves these endpoints, so an agent can use whichever format it speaks: | Endpoint | Format | | - | - | | `/v1/chat/completions` | OpenAI Chat Completions | | `/v1/responses` | OpenAI Responses | | `/v1/messages` | Anthropic Messages | `/v1/messages` only reaches providers that support the Anthropic Messages surface. For a model you run yourself with Ollama, LM Studio, or vLLM, point the agent at `/v1/chat/completions` or `/v1/responses`. Access keys work in both the `Authorization: Bearer` and `x-api-key` headers, so you don't need to match the header your agent sends. ## Test the connection Send a prompt from the agent, then confirm the request in AI Gateway [observability](/docs/ai-gateway/observability) in [app.ngrok.ai](https://app.ngrok.ai). If the agent reports an error, check [Debugging](/docs/ai-gateway/guides/debugging) and the [error codes reference](/docs/ai-gateway/reference/error-codes). ## Next steps * [Use with other SDKs](/docs/ai-gateway/sdks/other): Raw HTTP examples for each API format * [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself): Reach a model on your own machine, such as Ollama or vLLM * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Scope what an access key can call # Pi Source: https://ngrok.com/docs/ai-gateway/agents/pi Connect the Pi terminal coding agent to the ngrok AI Gateway to reach any model in the catalog, or a model you run yourself. [Pi](https://pi.dev) is a terminal coding agent. It reaches the AI Gateway as a custom provider in its models file, so one access key covers every model you list there. ## What you'll need * [Pi](https://pi.dev) installed. * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai). ## Add the gateway as a provider Export your access key so Pi can read it: ```bash theme={null} export NGROK_AI_GATEWAY_KEY="ng-xxxxx-g1-xxxxx" ``` Add the gateway as a provider in `~/.pi/agent/models.json`: ```json theme={null} { "providers": { "ngrok": { "baseUrl": "https://gateway.ngrok.ai/v1", "apiKey": "$NGROK_AI_GATEWAY_KEY", "api": "openai-completions", "models": [ { "id": "gpt-5.5" }, { "id": "claude-sonnet-5-5" } ] } } } ``` `id` is the model name Pi sends to the gateway: a model ID from the [model catalog](/docs/ai-gateway/reference/model-catalog), or `provider:model` to pin a provider. It's the only field a model entry needs, though you can add `name`, `contextWindow`, and `maxTokens` to label it and track context accurately. ## Test the connection Send a single prompt from the command line: ```bash theme={null} pi --provider ngrok --model "gpt-5.5" -p "Explain what a hash map is in two sentences." ``` In an interactive session, run `/model` and pick one of the **ngrok** models instead. Confirm the request in AI Gateway [observability](/docs/ai-gateway/observability) in [app.ngrok.ai](https://app.ngrok.ai). ## Use a model you run yourself To reach a model on your own machine, register it as a [custom provider](/docs/ai-gateway/concepts/custom-providers), then add its `provider:model` name to `models`, such as `ollama:llama3.2`. See [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself) for the full setup. ## Next steps * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Scope what an access key can call * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Observability](/docs/ai-gateway/observability): See which model served each request # Zed Source: https://ngrok.com/docs/ai-gateway/agents/zed Connect the Zed editor's agent panel to the ngrok AI Gateway as an OpenAI-compatible provider to use catalog or self-hosted models. [Zed](https://zed.dev) is a code editor with a built-in agent panel. It reaches the AI Gateway as an OpenAI-compatible LLM provider, so the agent panel can use any model you list in your settings. ## What you'll need * [Zed](https://zed.dev/download) installed. * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai). ## Add the gateway as a provider Add the provider to your Zed `settings.json`: ```json theme={null} { "language_models": { "openai_compatible": { "ngrok": { "api_url": "https://gateway.ngrok.ai/v1", "available_models": [ { "name": "gpt-5.5", "display_name": "GPT-5.5 (via ngrok)", "max_tokens": 1050000 }, { "name": "claude-sonnet-5-5", "display_name": "Claude Sonnet 5.5 (via ngrok)", "max_tokens": 1000000 } ] } } } } ``` For each model: * `name` is the model name Zed sends to the gateway: a model ID from the [model catalog](/docs/ai-gateway/reference/model-catalog), or `provider:model` to pin a provider * `max_tokens` is the model's context window * `capabilities` is optional; set `"images": true` inside it for models that accept images Next, add your access key through the Zed UI. Open the command palette, run **agent: open settings**, select **LLM Providers**, then **Configure**, find **ngrok** in the provider list, paste your access key, and press **Enter**. ## Test the connection Create a new agent thread, pick one of your models from the selector at the bottom of the agent panel's message editor, and send a prompt: Explain what a hash map is in two sentences. Confirm the request in AI Gateway [observability](/docs/ai-gateway/observability) in [app.ngrok.ai](https://app.ngrok.ai). ## Use a model you run yourself To reach a model on your own machine, register it as a [custom provider](/docs/ai-gateway/concepts/custom-providers), then add its `provider:model` name to `available_models`, such as `ollama:llama3.2`. See [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself) for the full setup. ## Next steps * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Scope what an access key can call * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Observability](/docs/ai-gateway/observability): See which model served each request # Create an AI Gateway access key configuration Source: https://ngrok.com/docs/ai-gateway/api-reference/access-key-configurations/create openapi/ai-gateway.yaml POST /access-key-configurations Create a reusable access key configuration with provider/model scope and routing rules. # Delete an AI Gateway access key configuration Source: https://ngrok.com/docs/ai-gateway/api-reference/access-key-configurations/delete openapi/ai-gateway.yaml DELETE /access-key-configurations/{id} Delete an AI Gateway access key configuration by ID. # Get an AI Gateway access key configuration Source: https://ngrok.com/docs/ai-gateway/api-reference/access-key-configurations/get openapi/ai-gateway.yaml GET /access-key-configurations/{id} Get a single AI Gateway access key configuration by ID. # List AI Gateway access key configurations Source: https://ngrok.com/docs/ai-gateway/api-reference/access-key-configurations/list openapi/ai-gateway.yaml GET /access-key-configurations List all AI Gateway access key configurations owned by the authenticated account. # Update an AI Gateway access key configuration Source: https://ngrok.com/docs/ai-gateway/api-reference/access-key-configurations/update openapi/ai-gateway.yaml PATCH /access-key-configurations/{id} Apply a partial update to an AI Gateway access key configuration. # Create an AI Gateway access key Source: https://ngrok.com/docs/ai-gateway/api-reference/access-keys/create openapi/ai-gateway.yaml POST /access-keys Create a new ngrok-managed access key for AI Gateway traffic. # Delete an AI Gateway access key Source: https://ngrok.com/docs/ai-gateway/api-reference/access-keys/delete openapi/ai-gateway.yaml DELETE /access-keys/{id} Delete an AI Gateway access key by ID. # Get an AI Gateway access key Source: https://ngrok.com/docs/ai-gateway/api-reference/access-keys/get openapi/ai-gateway.yaml GET /access-keys/{id} Get a single AI Gateway access key by ID. # List AI Gateway access keys Source: https://ngrok.com/docs/ai-gateway/api-reference/access-keys/list openapi/ai-gateway.yaml GET /access-keys List all AI Gateway access keys owned by the authenticated account. # Update an AI Gateway access key Source: https://ngrok.com/docs/ai-gateway/api-reference/access-keys/update openapi/ai-gateway.yaml PATCH /access-keys/{id} Update mutable fields on an existing AI Gateway access key. # Create an AI Gateway API key Source: https://ngrok.com/docs/ai-gateway/api-reference/api-keys/create openapi/ai-gateway.yaml POST /api-keys Create a new AI Gateway API key for the authenticated account. # Delete an AI Gateway API key Source: https://ngrok.com/docs/ai-gateway/api-reference/api-keys/delete openapi/ai-gateway.yaml DELETE /api-keys/{id} Delete an AI Gateway API key by ID. # Get an AI Gateway API key Source: https://ngrok.com/docs/ai-gateway/api-reference/api-keys/get openapi/ai-gateway.yaml GET /api-keys/{id} Get a single AI Gateway API key by ID. # List AI Gateway API keys Source: https://ngrok.com/docs/ai-gateway/api-reference/api-keys/list openapi/ai-gateway.yaml GET /api-keys List all AI Gateway API keys owned by the authenticated account. # Update an AI Gateway API key Source: https://ngrok.com/docs/ai-gateway/api-reference/api-keys/update openapi/ai-gateway.yaml PATCH /api-keys/{id} Update mutable fields on an existing AI Gateway API key. # Get credit balance Source: https://ngrok.com/docs/ai-gateway/api-reference/credits/balance openapi/ai-gateway.yaml GET /credits/balance Get the account's current AI Gateway credit balance. Eventually consistent with usage events. # Create a Stripe checkout session Source: https://ngrok.com/docs/ai-gateway/api-reference/credits/checkout-session openapi/ai-gateway.yaml POST /credits/checkout-session Create a Stripe-hosted checkout session to purchase AI Gateway credits. # Start a credit purchase Source: https://ngrok.com/docs/ai-gateway/api-reference/credits/purchase-create openapi/ai-gateway.yaml POST /credits/purchases Start an off-session charge of the account's saved card to buy AI Gateway credits, then poll the purchase for its terminal outcome. # Get a credit purchase Source: https://ngrok.com/docs/ai-gateway/api-reference/credits/purchase-get openapi/ai-gateway.yaml GET /credits/purchases/{id} Get one credit purchase attempt by ID to poll it to a terminal state and read the decline reason recorded on failure. # List credit transactions Source: https://ngrok.com/docs/ai-gateway/api-reference/credits/transactions openapi/ai-gateway.yaml GET /credits/transactions Returns a merged, date-descending list of credit purchases and metered usage invoices for the account. Read from the billing provider in real time. # Get AI Gateway configuration Source: https://ngrok.com/docs/ai-gateway/api-reference/gateway-config/get openapi/ai-gateway.yaml GET /gateway-config Get the AI Gateway configuration for the authenticated account. # Update AI Gateway configuration Source: https://ngrok.com/docs/ai-gateway/api-reference/gateway-config/update openapi/ai-gateway.yaml PATCH /gateway-config Update the AI Gateway configuration for the authenticated account. Omitted fields preserve their existing stored value. # List available AI Gateway models Source: https://ngrok.com/docs/ai-gateway/api-reference/models/list openapi/ai-gateway.yaml GET /v1/models List every model available to your account, including built-in catalog models and your own user-defined provider models. # AI Gateway API Source: https://ngrok.com/docs/ai-gateway/api-reference/overview Set up and run your AI Gateway from your own code with the management API. *** The ngrok.ai API lets you manage ngrok.ai directly from your code, so you don't have to rely on the dashboard. You can use it to create access keys, add provider keys, set up routing and access rules, register custom providers, check usage, and keep an eye on your credit balance. Note, that the API doesn't handle model requests itself. To call models, send your requests to `https://gateway.ngrok.ai` using an access key. ## Base URL When you make requests to the ngrok.ai API, send them to: ```text theme={null} https://api.ngrok.ai ``` Note that the ngrok.ai API uses JSON for both requests and responses, and all calls must be made over HTTPS. ## Authentication To authenticate, include your [ngrok.ai API key](/docs/ai-gateway/concepts/access-keys#credentials-overview) in the `Authorization` header of each request: ```bash theme={null} curl https://api.ngrok.ai/access-keys \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" ``` You can create and manage your ngrok.ai API key on the settings page in the dashboard. Treat this key like a secret, since it allows you to manage the resources in your account. ## OpenAPI spec The API is described by a public OpenAPI 3.0 spec. Use it to generate a client in any language, or load it into your API tooling: ```text theme={null} https://api.ngrok.ai/openapi.yaml ``` ## What you can manage Use the AI Gateway API to manage: * [Access keys](/docs/ai-gateway/api-reference/access-keys/list): Manage keys for making gateway requests * [Access key configurations](/docs/ai-gateway/api-reference/access-key-configurations/list): Manage access controls and rules for which providers, models, and keys an access key can use * [Providers](/docs/ai-gateway/api-reference/providers/list): Manage which providers the gateway can route to * [Provider keys](/docs/ai-gateway/api-reference/provider-keys/list): Manage your credentials for providers * [API keys](/docs/ai-gateway/api-reference/api-keys/list): Manage keys that grant ngrok.ai API access * [Configuration](/docs/ai-gateway/api-reference/gateway-config/get): Manage your account level gateway settings * [Credits](/docs/ai-gateway/api-reference/credits/balance): View your credit balance and transactions, and start a purchase * [Models](/docs/ai-gateway/api-reference/models/list): List every model your account can reach, with pricing and provider metrics * [Usage](/docs/ai-gateway/api-reference/usage/list): View data on requests, tokens, providers, models, and costs ## Common workflows Use the ngrok.ai API to automate common setup and operational tasks. ### Create access keys Create access keys for applications, environments, teams, or customers. Each access key can have its own configuration for routing, model access, and provider credentials. ### Add provider keys Store upstream provider credentials so ngrok.ai can call providers on your behalf without exposing those credentials in your application code. ### Configure routing and access Define access key configurations to control which providers and models an access key can call, and which provider keys are used for upstream authentication. ### Register custom providers Create custom providers for self-hosted models, private deployments, or other OpenAI-compatible and Anthropic-compatible APIs. ### Monitor usage and credits Query usage and credit data to track requests, tokens, costs, and remaining balance. ## Next steps * [Access keys](/docs/ai-gateway/api-reference/access-keys/list): Create and manage access keys * [Provider keys](/docs/ai-gateway/api-reference/provider-keys/list): Add and rotate upstream provider credentials * [Access key configurations](/docs/ai-gateway/api-reference/access-key-configurations/list): Configure access, routing, and credential selection * [Providers](/docs/ai-gateway/api-reference/providers/list): Manage built-in and custom providers * [Usage](/docs/ai-gateway/api-reference/usage/list): Query request usage # Create an AI Gateway provider key Source: https://ngrok.com/docs/ai-gateway/api-reference/provider-keys/create openapi/ai-gateway.yaml POST /provider-keys Create a new AI Gateway provider key for your account. Reference it from an access key configuration's routing rule steps to use it. # Delete an AI Gateway provider key Source: https://ngrok.com/docs/ai-gateway/api-reference/provider-keys/delete openapi/ai-gateway.yaml DELETE /provider-keys/{id} Delete an AI Gateway provider key by ID. # Get an AI Gateway provider key Source: https://ngrok.com/docs/ai-gateway/api-reference/provider-keys/get openapi/ai-gateway.yaml GET /provider-keys/{id} Get a single AI Gateway provider key by ID. # List AI Gateway provider keys Source: https://ngrok.com/docs/ai-gateway/api-reference/provider-keys/list openapi/ai-gateway.yaml GET /provider-keys List all AI Gateway provider keys owned by the authenticated account. # Rotate an AI Gateway provider key Source: https://ngrok.com/docs/ai-gateway/api-reference/provider-keys/rotate openapi/ai-gateway.yaml POST /provider-keys/{id}/rotate Update an AI Gateway provider key by ID to a new value. # Update an AI Gateway provider key Source: https://ngrok.com/docs/ai-gateway/api-reference/provider-keys/update openapi/ai-gateway.yaml PATCH /provider-keys/{id} Update mutable fields on an existing AI Gateway provider key. # Create an AI Gateway provider Source: https://ngrok.com/docs/ai-gateway/api-reference/providers/create openapi/ai-gateway.yaml POST /providers Create a new user-defined AI Gateway provider for routing requests to a self-hosted or third-party LLM endpoint. # Delete an AI Gateway provider Source: https://ngrok.com/docs/ai-gateway/api-reference/providers/delete openapi/ai-gateway.yaml DELETE /providers/{id} Delete an AI Gateway provider and all of its models. Deletion is blocked while a provider key or access key configuration references the provider. # Get an AI Gateway provider Source: https://ngrok.com/docs/ai-gateway/api-reference/providers/get openapi/ai-gateway.yaml GET /providers/{id} Get a single AI Gateway provider by ID. # List AI Gateway providers Source: https://ngrok.com/docs/ai-gateway/api-reference/providers/list openapi/ai-gateway.yaml GET /providers List all user-defined AI Gateway providers for the authenticated account. Built-in providers come from the ngrok.ai catalog, not this resource. # Update an AI Gateway provider Source: https://ngrok.com/docs/ai-gateway/api-reference/providers/update openapi/ai-gateway.yaml PATCH /providers/{id} Apply a partial update to an AI Gateway provider. The providerId is immutable after creation. # List AI Gateway usage events Source: https://ngrok.com/docs/ai-gateway/api-reference/usage/list openapi/ai-gateway.yaml GET /usage List per-attempt AI Gateway usage events for the authenticated account. # AI Gateway usage overview Source: https://ngrok.com/docs/ai-gateway/api-reference/usage/overview openapi/ai-gateway.yaml GET /usage/overview Aggregated daily cost, request, and error series for the authenticated account over a time window, grouped by provider or access key. # Get the upstream attempts of one AI Gateway request Source: https://ngrok.com/docs/ai-gateway/api-reference/usage/request-attempts openapi/ai-gateway.yaml GET /usage/requests/{requestId} Get every upstream attempt the AI Gateway made for one client request, ordered by attempt number. # Changelog Source: https://ngrok.com/docs/ai-gateway/changelog AI Gateway feature updates and changes. ## Never run out of credits Turn on automatic top-up and ngrok.ai refills your balance from your saved card whenever it runs low. You choose when it tops up and how much, and you can cap how much it spends each billing period. See [Automatic top-up](/docs/ai-gateway/concepts/credits#automatic-top-up). ## Approve card payments that need an extra check Some banks ask you to confirm a payment before it goes through. When that happens while you buy credits, you now get a link to approve the payment, and your credits arrive once you do. See [Credits](/docs/ai-gateway/concepts/credits#purchasing-credits). ## Requests can be up to 32 MiB Requests larger than 32 MiB are now turned away with a clear error, before they reach a provider. Most requests are far below this. If you hit it, trim long conversation history or large embedded documents. ## Know when a setting didn't reach the model Not every model supports every setting. When the gateway has to leave a setting out because the model can't use it, the response now tells you which ones. See [Response headers](/docs/ai-gateway/how-it-works#response-headers). ## Long answers no longer get cut off Streaming responses can now keep going for up to 15 minutes, which helps with long answers and slow reasoning models. If you've set your own total timeout, it still applies. See [Streaming responses](/docs/ai-gateway/guides/account-settings#streaming-responses). ## Use more providers without signing up for them You can now pay with credits for models from Google, DeepSeek, xAI, Moonshot AI, Z.ai, Qwen Cloud, MiniMax, and more, in addition to OpenAI and Anthropic. You don't need an account or API key with those providers. The [model catalog](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials) shows which models work this way. ## See why a fallback model didn't run If a model you listed as a fallback couldn't be used, for example because your access key isn't allowed to call it, the response now names it. See [Response headers](/docs/ai-gateway/how-it-works#response-headers). ## Follow every retry for a request You can now look up a single request and see each attempt the gateway made, what happened, and why it moved on. See [Why a request failed over](/docs/ai-gateway/guides/why-a-request-failed-over). ## Request ID, attempt count, and served model on every response Gateway responses now state how the request was handled: * `Ngrok-AIG-Request-Id`: the request's ID, on every response including rejections and errors. * `Ngrok-AIG-Attempts`: how many upstream attempts the gateway made. * `Ngrok-AIG-Model`: the upstream model that produced the response. All three are exposed through CORS, so browser clients can read them. They are ngrok's own headers, so managed-key and Bring Your Own Keys callers see the same values. See [Response headers](/docs/ai-gateway/how-it-works#response-headers). The native Gemini `generateContent` and `streamGenerateContent` surface returns these headers too, where it previously returned only `Content-Type`. Bodies on that surface are still forwarded byte-for-byte. ## The ai-gateway Traffic Policy action is retired The AI Gateway no longer runs as a Traffic Policy action on your own endpoint. A policy that still contains the `ai-gateway` action fails validation with `ERR_NGROK_3820`. * Send LLM traffic to `https://gateway.ngrok.ai` with an [access key](/docs/ai-gateway/concepts/access-keys), and manage the gateway at [app.ngrok.ai](https://app.ngrok.ai). * Existing credits were migrated to [app.ngrok.ai](https://app.ngrok.ai) automatically. No action is needed to keep your balance. * Remove the `ai-gateway` action from any endpoint that still carries it. * The action's result variables, such as `actions.ngrok.ai_gateway`, are gone. Read the [response headers](/docs/ai-gateway/how-it-works#response-headers) and the [Usage API](/docs/ai-gateway/api-reference/usage/list) instead. Email [support@ngrok.com](mailto:support@ngrok.com) with questions about a migrated account. ## app.ngrok.ai and gateway.ngrok.ai Manage your AI Gateway from [app.ngrok.ai](https://app.ngrok.ai) and send traffic to a shared gateway URL: * Manage [access keys](/docs/ai-gateway/concepts/access-keys), [access key configurations](/docs/ai-gateway/guides/access-key-configurations), provider keys, credits, and settings at **[app.ngrok.ai](https://app.ngrok.ai)**. * Send LLM traffic from your applications to **`https://gateway.ngrok.ai`** with an access key only—provider keys are stored server-side. * Updated the [Quickstart](/docs/ai-gateway/quickstart), [Overview](/docs/ai-gateway/overview), [How It Works](/docs/ai-gateway/how-it-works), and [Access Keys](/docs/ai-gateway/concepts/access-keys). * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys) are now account-scoped in app.ngrok.ai BYOK. * Endpoint-based Traffic Policy setup is deprecated. ## AI Gateway API keys & credits You can now authenticate requests to your AI Gateway with **AI Gateway API Keys**—no provider accounts needed. The AI Gateway manages the provider keys for OpenAI and Anthropic on your behalf. Purchase prepaid credits to fund usage. * [AI Gateway API Keys](/docs/ai-gateway/concepts/access-keys): Create keys to authenticate requests * [Credits](/docs/ai-gateway/concepts/credits): Understand pricing and balance management * [Bring Your Own Keys](/docs/ai-gateway/concepts/bring-your-own-keys): Use your own provider keys for additional providers * Updated [Quickstart](/docs/ai-gateway/quickstart), [Overview](/docs/ai-gateway/overview), and all [SDK guides](/docs/ai-gateway/sdks) to use AI Gateway API Keys as the primary path ## Native Anthropic SDK support Use Anthropic's official Python and TypeScript SDKs directly with the AI Gateway. The gateway now supports the native `/v1/messages` API format alongside the OpenAI-compatible format. See the [Anthropic SDK guide](/docs/ai-gateway/sdks/anthropic) for setup instructions. ## Groq provider support Groq is now a built-in provider. Access Llama and Mixtral models running on Groq's LPU hardware with automatic failover and observability. ## Expanded model catalog Added new providers to the model catalog: Hyperbolic, InceptionLabs, Inference.net, and OpenRouter. Updated model aliases and provider-specific model IDs for improved compatibility. ## Early access release Initial Early Access release of the AI Gateway. ### Features * OpenAI-compatible API for routing requests to multiple AI providers * Support for OpenAI, Anthropic, Google, and other providers * Multi-provider failover across models, providers, and API keys * Multi-key failover per provider * CEL-based model selection strategies * Custom provider support via internal endpoints * Token counting and usage metrics # Access Keys Source: https://ngrok.com/docs/ai-gateway/concepts/access-keys The credential your application uses to authenticate with the AI Gateway. An **access key** authenticates your application to `https://gateway.ngrok.ai`. Send it with every request—the gateway uses it to verify who is calling and whether the request is allowed through. ## Access keys vs provider keys The AI Gateway splits authentication into two credentials so your application never handles upstream secrets. | | Access key | Provider key | | - | - | - | | **What it is** | The credential your app sends to the gateway | An upstream API key for OpenAI, Anthropic, or another provider | | **Where it lives** | In your application | In [app.ngrok.ai](https://app.ngrok.ai), attached through an [access key configuration](/docs/ai-gateway/guides/access-key-configurations) | | **Who uses it** | Your code, as the SDK's `api_key` | The AI Gateway, injected server-side on each request | Because the two are separate, you can rotate or revoke an access key for one client without touching your [provider keys](/docs/ai-gateway/guides/attaching-provider-keys), and your upstream secrets never ship in client code. Many built-in providers, including **OpenAI**, **Anthropic**, **Google**, and **DeepSeek**, let you [pay with credits](/docs/ai-gateway/guides/pay-with-credits). There are no provider keys to store at all. See [How It Works](/docs/ai-gateway/how-it-works) for the full request flow. ## Use in your application ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) ``` See the [Quickstart](/docs/ai-gateway/quickstart) to create your first key. ## Create and manage keys Create a separate key per application or team so you can revoke one client without affecting others. ### Via the ngrok dashboard 1. Open [app.ngrok.ai](https://app.ngrok.ai) → **Keys**. 2. Click **New access key**. 3. Enter a name and optionally assign a configuration. 4. Copy the token immediately—it's only shown once. ### Via the AI Gateway API 1. Create an [AI Gateway API key](#credentials-overview). 2. Send a request to the [Access Keys API](/docs/ai-gateway/api-reference/access-keys/create), authorized with that key: ```bash theme={null} curl -X POST https://api.ngrok.ai/access-keys \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Production web app" }' ``` ## Things to know ### Your key token is only shown once When you create a key, the token (starting with `ng-`) is displayed exactly once. If you lose it, delete the key and create a new one. ### Requests require credits Every request consumes [credits](/docs/ai-gateway/concepts/credits) for the ngrok.ai processing fee. When you pay with credits, credits also cover model cost. When you bring your own key, model costs go to your provider account. When your balance reaches zero, new requests fail until you purchase more credits. ### Invalid keys are rejected Malformed or deleted access keys are rejected immediately with a `401 Unauthorized`. See [Error Codes](/docs/ai-gateway/reference/error-codes) to look up that and other gateway responses. ## Credentials overview | Credential | What it's for | | - | - | | **ngrok.ai access keys** | Authenticate AI requests to `gateway.ngrok.ai` (required) | | **ngrok.ai API keys** | Automate AI Gateway resources via `api.ngrok.ai` | | **ngrok API keys** | Manage ngrok platform resources via the [ngrok API](https://api.ngrok.com) | | **Your provider keys** | Upstream credentials. See [Bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys) | ## Next steps Scope providers and route traffic per key Store upstream credentials Per-client keys and revocation How usage is billed # Bring Your Own Keys Source: https://ngrok.com/docs/ai-gateway/concepts/bring-your-own-keys Use your own provider API keys with the AI Gateway. Bringing your own keys is necessary when: * you want upstream billing on your provider account * you need a built-in provider you [can't pay for with credits](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials) * you're running [custom providers](/docs/ai-gateway/concepts/custom-providers) Your application authenticates with an [access key](/docs/ai-gateway/concepts/access-keys). Store provider keys in [app.ngrok.ai](https://app.ngrok.ai) and attach them through an [access key configuration](/docs/ai-gateway/guides/access-key-configurations). ## Built-in providers | | Providers you can pay for with credits | Other built-in providers | | - | - | - | | Provider account needed | Optional | Yes | | Default credentials | ngrok.ai supplies the key, paid with credits | Provider key required in configuration | | What credits pay for | Processing fee; plus model cost when you pay with credits | Processing fee only | For providers you can pay for with credits, create a configuration routing rule with **Bring your own API key** instead of **ngrok** when you want to use your provider account. ## Custom providers Custom providers are endpoints you define, such as Ollama, vLLM, or any OpenAI- or Anthropic-compatible API. See [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself). Custom providers require provider keys in the configuration when the upstream needs authentication. See [Custom providers](/docs/ai-gateway/concepts/custom-providers). ## Setup flow 1. [Add provider keys](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key). 2. Create an [access key configuration](/docs/ai-gateway/guides/access-key-configurations) with routing rules and scope. 3. Assign the configuration to an [access key](/docs/ai-gateway/concepts/access-keys). 4. Send requests with the access key only. ## Guides Store upstream credentials Attach keys and set scope Self-hosted and third-party endpoints # Credits Source: https://ngrok.com/docs/ai-gateway/concepts/credits Understanding AI Gateway credits, pricing, and balance management. AI Gateway credits are a prepaid balance you buy in [app.ngrok.ai](https://app.ngrok.ai). Every request through `https://gateway.ngrok.ai` pays a small [processing fee](#processing-fee) from your credits. If you don't use your own provider key, credits also pay for the model. See [How credits are consumed](#how-credits-are-consumed). ## Accounts and plans You don't need an ngrok subscription to use the AI Gateway. Sign in to [app.ngrok.ai](https://app.ngrok.ai) with your ngrok account, buy credits, and create an [access key](/docs/ai-gateway/concepts/access-keys). AI Gateway credits are separate from your ngrok plan. Your ngrok plan doesn't pay for AI Gateway usage, and credits don't pay for other ngrok features. | Manage | Where | | - | - | | Access keys, providers, credits, and usage | [app.ngrok.ai](https://app.ngrok.ai) | | Your ngrok plan, team members, tunnels, and endpoints | [dashboard.ngrok.com](https://dashboard.ngrok.com) | To reach a model you run yourself through an [internal endpoint](/docs/ai-gateway/concepts/custom-providers), set up the endpoint in dashboard.ngrok.com. Some ngrok features need a paid plan; see [pricing](https://ngrok.com/pricing). ## Purchasing credits 1. Open the [Billing page](https://app.ngrok.ai/billing) and select **Add credits**. If you haven't saved a card yet, you'll add one first. 2. Pick an amount, or enter your own. The minimum is \$5, and your balance can be at most \$500. 3. Select **Buy** to confirm. To buy credits from code, use the [Credits API](/docs/ai-gateway/api-reference/credits/purchase-create). Credits expire **one year** after purchase. ## Automatic top-up Automatic top-up adds credits from your saved card when your balance runs low. To set it up: 1. Open the [Billing page](https://app.ngrok.ai/billing) and make sure you have a saved card. 2. Select **Enable auto top-up**, then turn on **Enable auto top-up** in the dialog. 3. Set **When your balance reaches** (at least \$5) and **Top up to** (up to \$500). 4. Optional: select **Set a spending limit** and enter a **Maximum spend every billing period**. 5. Select **Save changes**. ## How credits are consumed | If the request uses | Credits pay for | You pay separately | | - | - | - | | An ngrok.ai key | Processing fee and model usage, including hosted tools like web search | Nothing | | Your own provider key | Processing fee | Model usage, billed by your provider | | A model you run yourself | Processing fee | Your own infrastructure | ## Balance tracking You can view your balance on the [Billing page](https://app.ngrok.ai/billing), or via the API: ```bash theme={null} curl https://api.ngrok.ai/credits/balance \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" ``` ## What happens when credits run out When your credit balance reaches zero, the gateway rejects new requests with HTTP 402 and [`ERR_NGROK_27206`](/docs/ai-gateway/reference/error-codes#err_ngrok_27206) until you purchase more credits. That's true even if you use your own provider keys, since every request pays a small processing fee from your credits. To avoid this, turn on [automatic top-up](#automatic-top-up) so your balance refills before it runs out. The balance updates shortly after usage, so requests already in flight can take it slightly below zero. The [balance API](/docs/ai-gateway/api-reference/credits/balance) reports a negative value when that happens. ## Processing fee Every successful request through the gateway incurs a processing fee at **USD \$0.05 per million tokens**. The fee is based on the tokens in your request and response. It applies to all traffic—requests paid with credits, your own [provider keys](/docs/ai-gateway/guides/attaching-provider-keys), and [custom providers](/docs/ai-gateway/concepts/custom-providers)—and does not vary by model or provider. With [`ngrok/auto`](/docs/ai-gateway/guides/automatic-model-selection#billing), the tokens ngrok.ai reads to pick a model also count toward the fee. When you pay with credits, credits also cover the provider's model cost. When you bring your own key, model costs stay on your provider account and credits cover only the processing fee. ## Next steps * [Access Keys](/docs/ai-gateway/concepts/access-keys): Authenticate requests to the gateway * [Quickstart](/docs/ai-gateway/quickstart): Purchase credits and make your first request * [Bring Your Own Keys](/docs/ai-gateway/concepts/bring-your-own-keys): Use your own provider API keys # Custom Providers Source: https://ngrok.com/docs/ai-gateway/concepts/custom-providers Connect custom endpoints and self-hosted models to the AI Gateway. **Custom providers** are upstream endpoints you define, such as self-hosted Ollama or vLLM, a private deployment, or any OpenAI- or Anthropic-compatible API not in the [built-in catalog](/docs/ai-gateway/reference/model-catalog). Use a custom provider when you want the AI Gateway to call a model running on your machine, private network, or cloud GPU. Traffic still authenticates to the gateway with an [access key](/docs/ai-gateway/concepts/access-keys). Attach provider keys and routing rules through an [access key configuration](/docs/ai-gateway/guides/access-key-configurations). ## When to use one Use a custom provider when: * You run models locally with Ollama, LM Studio, or vLLM. * You host models on your own cloud infrastructure. * You have a private OpenAI- or Anthropic-compatible API. * You need a provider that isn't in the built-in catalog. ## What a custom provider defines A custom provider tells the gateway: * Which provider ID to use in requests, such as `my-ollama`. * Which base URL to call, such as `https://my-ollama.internal`. * Which API surface the endpoint supports. * Which model IDs are available. You call custom provider models with the same `provider:model` format as built-in providers: ```json theme={null} { "model": "my-ollama:llama3.2", "messages": [{"role": "user", "content": "Hello"}] } ``` ## URL requirements | URL type | Scheme | Example | | - | - | - | | External | `https://` only | `https://api.example.com/v1` | | ngrok internal | `http://` or `https://` | `https://my-service.internal` | HTTP is only allowed for ngrok `.internal` endpoints. External URLs must use HTTPS. ## Self-hosted on a local network To reach models on your machine or private network, expose the service with an ngrok internal endpoint, then use that URL as the custom provider's `baseUrl`. Reaching internal endpoints may require a ngrok platform plan. See [Credits](/docs/ai-gateway/concepts/credits). For the full setup flow, see [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself). ## Next steps * [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself): Connect a local or private model * [Ollama guide](/docs/ai-gateway/custom-providers/ollama): Connect Ollama * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations): Attach keys and scope access * [Model catalog](/docs/ai-gateway/reference/model-catalog): Built-in providers and models # Models Source: https://ngrok.com/docs/ai-gateway/concepts/models Understanding model naming and request formats in the AI Gateway. Models are the AI systems that process your requests. You specify the model in each request body, and you can use an [access key configuration](/docs/ai-gateway/guides/access-key-configurations) to restrict which models an access key may call. ## Specifying models ### Model name only ```json theme={null} { "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}] } ``` The gateway looks up `gpt-4o` in the [model catalog](/docs/ai-gateway/reference/model-catalog) and routes to OpenAI. If OpenAI fails, it can try other providers that serve the same model. The full catalog ID, `author/model` such as `openai/gpt-4o`, works the same way. ### Provider prefix ```json theme={null} { "model": "anthropic:claude-sonnet-4-6", "messages": [{"role": "user", "content": "Hello"}] } ``` Use `provider:model` when you want to force a specific provider or target [a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself). ## Multiple models (failover) List fallback models with the `models` array: ```json theme={null} { "model": "gpt-4o", "models": ["gpt-4o-mini", "anthropic:claude-sonnet-4-6"], "messages": [{"role": "user", "content": "Hello"}] } ``` The gateway tries `model` first, then each entry in `models` in order, until one succeeds. ## Unknown models (pass-through) If a model is not in the catalog, the gateway still routes it when you include a provider prefix: ```json theme={null} { "model": "openai:some-new-model", "messages": [{"role": "user", "content": "Hello"}] } ``` The provider returns an error if the model does not exist. Pass-through models must still be allowed by the access key's configuration. ## Model and provider names Model names are not case-sensitive: `gpt-4o`, `GPT-4o`, and `Gpt-4O` all resolve to the same model. Provider names are matched exactly, so use the lowercase **Provider ID** from the [model catalog](/docs/ai-gateway/reference/model-catalog). Name models and providers by their primary catalog ID. The gateway does not resolve alternate names for either. ## Model capabilities Models differ in input/output modalities, context window, and features. See the [model catalog](/docs/ai-gateway/reference/model-catalog) for details. ## Next steps * [Model catalog](/docs/ai-gateway/reference/model-catalog): Supported models by provider * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Model IDs and provider prefixes * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Limit models per key # Providers Source: https://ngrok.com/docs/ai-gateway/concepts/providers Built-in and custom AI providers in the ngrok AI Gateway. Providers are the AI services that host and serve models. The AI Gateway routes your requests to providers and handles authentication, failover, and observability. Manage providers at [app.ngrok.ai](https://app.ngrok.ai). If you're deciding which setup path to use, start with [Choose how to reach providers](/docs/ai-gateway/guides/configuring-providers). The dashboard separates **built-in** catalog providers from **custom** providers you define. ## Built-in providers Built-in providers are public AI APIs with a known model catalog: OpenAI, Anthropic, Google, Groq, and others listed in the [model catalog](/docs/ai-gateway/reference/model-catalog). You can send requests with an [access key](/docs/ai-gateway/concepts/access-keys). Your access key authenticates you to the gateway, not to the upstream provider. For many built-in providers, including **OpenAI**, **Anthropic**, **Google**, and **DeepSeek**, the AI Gateway can supply the provider credentials when you have [credits](/docs/ai-gateway/concepts/credits). The [model catalog](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials) shows which providers support it. You aren't required to have your own provider account or provider API keys—the AI Gateway handles upstream authentication and bills model cost through credits. For providers you can't pay for with credits, [bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key). Model costs go to your provider account; credits still cover the gateway processing fee. This is generally referred to as BYOK (bring your own key). GPT and o-series models. Pay with credits. Claude models. Pay with credits. Supports both OpenAI and Anthropic SDK formats. Access hundreds of models from multiple providers through a single API. Gemini models from Google AI Studio. Pay with credits. LPU-accelerated inference for open-source models (GPT-OSS, Llama 4, Qwen3). High-performance reasoning and chat models. Pay with credits. Open-source model hosting with high-performance inference. Diffusion-based language models for fast text generation. Distributed inference network for AI models at scale. Grok models. Pay with credits. Models served by Mistral AI. Bring your own key. Kimi models. Pay with credits. GLM models. Pay with credits. Qwen models and other open models. Pay with credits. MiniMax models. Pay with credits. Models from Tencent, DeepSeek, Moonshot AI, Z.ai, MiniMax, and more. Pay with credits. System One models that answer typed questions. Pay with credits. ## Custom providers Custom providers are upstream endpoints you define, such as self-hosted Ollama or vLLM, a private deployment, or any OpenAI- or Anthropic-compatible API that isn't in the built-in catalog. See [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself). Custom providers require a provider key when the upstream needs authentication. See [Custom providers](/docs/ai-gateway/concepts/custom-providers) for the concept. Run open-source models locally with Ollama. High-performance inference server. Desktop app for local model inference. Microsoft's OpenAI service on Azure. ## How provider selection works When a request arrives, the gateway determines which provider to use: 1. **Explicit provider prefix**: if the model name includes a provider prefix (for example, `openai:gpt-4o` or `openrouter:anthropic/claude-3.5-sonnet`), that provider is used 2. **Catalog lookup**: the gateway looks up the model ID in its [catalog](/docs/ai-gateway/reference/model-catalog) to find the default provider 3. **Request selection**: [model names](/docs/ai-gateway/guides/model-selection-strategies) and fallback lists can override the default ## Next steps * [Model Catalog](/docs/ai-gateway/reference/model-catalog): browse all available models by provider * [Bring Your Own Keys](/docs/ai-gateway/concepts/bring-your-own-keys): add provider keys for built-in and custom providers * [Multi-provider failover](/docs/ai-gateway/examples/multi-provider-failover): automatic failover across providers # Azure OpenAI Source: https://ngrok.com/docs/ai-gateway/custom-providers/azure-openai Route AI requests to Azure OpenAI Service through the ngrok AI Gateway. [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) provides OpenAI models on Microsoft Azure. Connect your deployment to the AI Gateway as a [custom provider](/docs/ai-gateway/concepts/custom-providers). ## What you'll need * [ngrok account](https://app.ngrok.ai) with AI Gateway access * [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) resource with a deployed model * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai) ## Overview Azure OpenAI uses deployment-specific URLs and requires an `api-version` query parameter. The gateway injects your Azure API key server-side. ```mermaid theme={null} graph LR A[Client] --> B[gateway.ngrok.ai] B --> C[Azure OpenAI] ``` ## Getting started From the Azure Portal, collect: 1. **Resource endpoint**: `https://your-resource.openai.azure.com` 2. **API key**: from **Keys and Endpoint** 3. **Deployment name**: the name of your model deployment 4. **API version**: a supported version (for example `2024-02-15-preview`) Azure requires the deployment path and `api-version` in the base URL. See [Create a custom provider](/docs/ai-gateway/guides/use-a-model-you-run-yourself#create-a-custom-provider) with: * **Provider ID**: `azure-openai` * **Base URL**: `https://your-resource.openai.azure.com/openai/deployments/your-deployment?api-version=2024-02-15-preview` * **API format**: OpenAI Chat Completions * **Models**: the model name for this deployment (for example `gpt-4o`) Include `api-version` as a query parameter on the base URL. Azure rejects requests without it. [Add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) for your Azure deployment. Create an [access key configuration](/docs/ai-gateway/guides/access-key-configurations) that: 1. Allows the `azure-openai` provider in the access scope 2. Adds a routing rule for `azure-openai` with **Bring your own API key** and your stored key attached Assign the configuration to your access key. ```python Python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" ) response = client.chat.completions.create( model="azure-openai:gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```typescript TypeScript theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx" }); const response = await client.chat.completions.create({ model: "azure-openai:gpt-4o", messages: [{ role: "user", content: "Hello!" }] }); console.log(response.choices[0].message.content); ``` ## Multiple deployments and regions Create a separate custom provider for each Azure deployment or region. Give each a unique provider ID (for example `azure-gpt4`, `azure-eastus`) with its own deployment URL and model list. Attach the same or different provider keys per deployment. Pin a region with `azure-eastus:gpt-4o`, or list multiple models for failover: `models: ["azure-eastus:gpt-4o", "openai:gpt-4o"]`. ## Troubleshooting | Symptom | Fix | | - | - | | 401 unauthorized | Verify the provider key in app.ngrok.ai matches Azure Portal and is attached in your access key configuration | | 404 deployment not found | Check the deployment name in the base URL matches Azure Portal | | API version errors | Update the `api-version` query parameter to a [supported version](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference) | | 429 rate limiting | Add multiple provider keys for failover or request a quota increase in Azure Portal | ## Next steps * [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself): URL requirements and configuration * [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys): Store upstream credentials * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations): Route traffic per access key * [Quickstart](/docs/ai-gateway/quickstart): Create your first access key # LM Studio Source: https://ngrok.com/docs/ai-gateway/custom-providers/lm-studio Route AI requests to local LM Studio models through the ngrok AI Gateway. [LM Studio](https://lmstudio.ai) is a desktop app for running LLMs locally with an [OpenAI-compatible API](https://lmstudio.ai/docs/developer/openai-compat). Connect it to the AI Gateway as a [custom provider](/docs/ai-gateway/concepts/custom-providers). ## What you'll need * [ngrok account](https://app.ngrok.ai) with AI Gateway access * [LM Studio](https://lmstudio.ai/download) installed * [ngrok agent](https://download.ngrok.com) installed * A model downloaded in LM Studio * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai) ## Overview LM Studio runs a local HTTP server. Expose it with an ngrok internal endpoint, register the endpoint as a custom provider, then route traffic through the gateway. ```mermaid theme={null} graph LR A[Client] --> B[gateway.ngrok.ai] B --> C[ngrok Internal Endpoint] C --> D[LM Studio localhost:1234] ``` ## Getting started Download a model and start the server: 1. Open LM Studio and download a model from the **Discover** tab 2. Go to **Developer**, select the model, and click **Start Server** ```bash theme={null} lms get llama-3.2-3b-instruct@q4_k_m lms server start ``` By default, LM Studio listens on port `1234`. Verify the server is running: ```bash theme={null} curl http://localhost:1234/v1/models ``` Use the model ID exactly as LM Studio reports it from `GET /v1/models`. Create an [internal endpoint](/docs/ai-gateway/guides/use-a-model-you-run-yourself#connect-a-local-model-with-an-internal-endpoint): ```bash theme={null} ngrok http 1234 --url https://lm-studio.internal ``` Internal endpoints (`.internal` domains) are private to your ngrok account, meaning they're not reachable from the public internet. Use the same ngrok account here and in the AI Gateway, otherwise the gateway can't reach the endpoint. See [Create a custom provider](/docs/ai-gateway/guides/use-a-model-you-run-yourself#create-a-custom-provider). Use provider ID `lm-studio`, base URL `https://lm-studio.internal`, and your model IDs. LM Studio doesn't require upstream authentication, so you can skip provider keys. Create an [access key configuration](/docs/ai-gateway/guides/access-key-configurations) that allows the `lm-studio` provider, then assign it to your access key. ```python Python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" ) response = client.chat.completions.create( model="lm-studio:llama-3.2-3b-instruct", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```typescript TypeScript theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx" }); const response = await client.chat.completions.create({ model: "lm-studio:llama-3.2-3b-instruct", messages: [{ role: "user", content: "Hello!" }] }); console.log(response.choices[0].message.content); ``` ```bash cURL theme={null} curl https://gateway.ngrok.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ng-xxxxx-g1-xxxxx" \ -d '{ "model": "lm-studio:llama-3.2-3b-instruct", "messages": [{"role": "user", "content": "Hello!"}] }' ``` ## Tips * **Embeddings**: The AI Gateway doesn't serve `/v1/embeddings`. Call LM Studio's embeddings endpoint directly. * **Slow first response**: Pre-load the model in LM Studio or enable "Keep model in memory" in settings. Increase `perRequestTimeout` in [account settings](/docs/ai-gateway/guides/account-settings) if needed. * **Cloud fallback**: Add a built-in provider to your access key configuration and use `models: ["lm-studio:llama-3.2-3b-instruct", "openai:gpt-4o"]` for failover. ## Troubleshooting | Symptom | Fix | | - | - | | Connection refused | Confirm the LM Studio server is running and the ngrok tunnel is active | | Model not found | Check `curl http://localhost:1234/v1/models` and match the model ID exactly | | Out of memory | Use a smaller model or lower quantization (Q4 instead of Q8) | | Port in use | Change the port in LM Studio settings and update your ngrok command | ## Next steps * [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself): URL requirements and local networking * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations): Scope providers per key * [Quickstart](/docs/ai-gateway/quickstart): Create your first access key # Ollama Source: https://ngrok.com/docs/ai-gateway/custom-providers/ollama Route AI requests to local Ollama models through the ngrok AI Gateway. [Ollama](https://ollama.ai) runs open-source large language models locally. This guide shows you how to connect Ollama to the ngrok AI Gateway as a [custom provider](/docs/ai-gateway/concepts/custom-providers). ## What you'll need * [ngrok account](https://app.ngrok.ai) with AI Gateway access * [Ollama](https://ollama.ai/download) installed locally * [ngrok agent](https://download.ngrok.com) installed * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai) ## Overview Ollama runs on HTTP locally. Expose it with an ngrok internal endpoint, register the endpoint as a custom provider, then route traffic through the gateway. ```mermaid theme={null} graph LR A[Client] --> B[gateway.ngrok.ai] B --> C[ngrok Internal Endpoint] C --> D[Ollama localhost:11434] ``` ## Getting started Start the Ollama server: ```bash theme={null} ollama serve ``` Pull a model if you haven't already: ```bash theme={null} ollama pull llama3.2 ``` Verify Ollama is running: ```bash theme={null} curl http://localhost:11434/api/tags ``` Create an [internal endpoint](/docs/ai-gateway/guides/use-a-model-you-run-yourself#connect-a-local-model-with-an-internal-endpoint) with the [ngrok agent](/docs/gateway/agent/): ```bash theme={null} ngrok http 11434 --url https://ollama.internal ``` Internal endpoints (`.internal` domains) are private to your ngrok account, meaning they're not reachable from the public internet. Use the same ngrok account here and in the AI Gateway, otherwise the gateway can't reach the endpoint. See [Create a custom provider](/docs/ai-gateway/guides/use-a-model-you-run-yourself#create-a-custom-provider). Use provider ID `ollama`, base URL `https://ollama.internal`, and your model IDs (for example `llama3.2`). Ollama doesn't require upstream authentication, so you can skip provider keys. Create an [access key configuration](/docs/ai-gateway/guides/access-key-configurations) that allows your `ollama` provider, then assign it to your access key. See the [Quickstart](/docs/ai-gateway/quickstart) if you haven't created an access key yet. Point any OpenAI-compatible SDK at the gateway using your access key: ```python Python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" ) response = client.chat.completions.create( model="ollama:llama3.2", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```typescript TypeScript theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx" }); const response = await client.chat.completions.create({ model: "ollama:llama3.2", messages: [{ role: "user", content: "Hello!" }] }); console.log(response.choices[0].message.content); ``` ```bash cURL theme={null} curl https://gateway.ngrok.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ng-xxxxx-g1-xxxxx" \ -d '{ "model": "ollama:llama3.2", "messages": [{"role": "user", "content": "Hello!"}] }' ``` ## Tips * **Slow first response**: Ollama loads models into memory on first use. Increase `perRequestTimeout` in [account settings](/docs/ai-gateway/guides/account-settings) if requests time out during warm-up. * **Multiple instances**: Create separate custom providers (for example `ollama-gpu-1`, `ollama-gpu-2`) with different internal endpoints. Pin a request with `ollama-gpu-1:llama3.2` or list multiple models for failover. * **Cloud fallback**: Add a built-in provider to your access key configuration and request `models: ["ollama:llama3.2", "openai:gpt-4o"]` for cross-provider failover. See [Multi-provider failover](/docs/ai-gateway/examples/multi-provider-failover). ## Troubleshooting | Symptom | Fix | | - | - | | Connection refused | Confirm Ollama is running (`curl http://localhost:11434/api/tags`) and the ngrok tunnel is active | | Model not found | Run `ollama list`, pull the model, and match the model ID exactly (including tags like `:1b`) | | Out of memory | Use a smaller or quantized model, or set `OLLAMA_NUM_PARALLEL=1` | ## Next steps * [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself): URL requirements and local networking * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations): Scope providers per key * [Quickstart](/docs/ai-gateway/quickstart): Create your first access key # vLLM Source: https://ngrok.com/docs/ai-gateway/custom-providers/vllm Route AI requests to vLLM inference servers through the ngrok AI Gateway. [vLLM](https://github.com/vllm-project/vllm) is a high-performance inference engine with an OpenAI-compatible API. Connect your vLLM server to the AI Gateway as a [custom provider](/docs/ai-gateway/concepts/custom-providers). ## What you'll need * [ngrok account](https://app.ngrok.ai) with AI Gateway access * [vLLM](https://docs.vllm.ai/en/latest/getting_started/installation.html) installed * [ngrok agent](https://download.ngrok.com) installed * GPU with sufficient VRAM for your chosen model * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai) ## Overview vLLM provides an OpenAI-compatible server that you can expose with an ngrok internal endpoint (or use a public HTTPS URL), register as a custom provider, and attach credentials through an access key configuration. ```mermaid theme={null} graph LR A[Client] --> B[gateway.ngrok.ai] B --> C[ngrok Internal Endpoint] C --> D[vLLM Server :8000] ``` ## Getting started Start the OpenAI-compatible server: ```bash theme={null} vllm serve meta-llama/Llama-3.2-8B-Instruct ``` Verify it's running: ```bash theme={null} curl http://localhost:8000/v1/models ``` Create an [internal endpoint](/docs/ai-gateway/guides/use-a-model-you-run-yourself#connect-a-local-model-with-an-internal-endpoint): ```bash theme={null} ngrok http 8000 --url https://vllm.internal ``` If vLLM already has a public HTTPS endpoint, skip this step and use that URL as the provider base URL instead. See [Create a custom provider](/docs/ai-gateway/guides/use-a-model-you-run-yourself#create-a-custom-provider). Use provider ID `vllm`, base URL `https://vllm.internal`, API format **OpenAI Chat Completions**, and your model IDs. If your vLLM server requires an API key (`vllm serve model --api-key your-secret-key`), [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key). Create an [access key configuration](/docs/ai-gateway/guides/access-key-configurations) that: 1. Allows the `vllm` provider in the access scope 2. Adds a routing rule with **Bring your own API key** if the server requires authentication Assign the configuration to your access key. ```python Python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" ) response = client.chat.completions.create( model="vllm:meta-llama/Llama-3.2-8B-Instruct", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```typescript TypeScript theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx" }); const response = await client.chat.completions.create({ model: "vllm:meta-llama/Llama-3.2-8B-Instruct", messages: [{ role: "user", content: "Hello!" }] }); console.log(response.choices[0].message.content); ``` ## Tips * **Secure vLLM**: Run with `--api-key` and attach the key through an [access key configuration](/docs/ai-gateway/guides/access-key-configurations). The AI Gateway adds it to upstream requests server-side. * **Gated models**: Set `HF_TOKEN` before starting vLLM for Hugging Face gated models. * **Timeouts**: Large models can be slow. Increase `perRequestTimeout` and `totalTimeout` in [account settings](/docs/ai-gateway/guides/account-settings). * **Multiple models**: Run separate vLLM instances with different internal endpoints and register each as its own custom provider. ## Troubleshooting | Symptom | Fix | | - | - | | Model loading errors | Check GPU memory with `nvidia-smi`; try `--gpu-memory-utilization 0.9` or a smaller model | | Connection timeouts | Verify the ngrok tunnel and vLLM health (`curl http://localhost:8000/health`); increase gateway timeouts | | 401 unauthorized | Confirm the provider key in app.ngrok.ai matches your vLLM `--api-key` and is attached in the access key configuration | ## Next steps * [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself): URL requirements and configuration * [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys): Store upstream credentials * [Quickstart](/docs/ai-gateway/quickstart): Create your first access key # Restrict Models and Providers Source: https://ngrok.com/docs/ai-gateway/examples/model-filtering Limit which models and providers an access key can call. Use an [access key configuration](/docs/ai-gateway/guides/access-key-configurations) to restrict providers and models. Requests outside the allowlist are rejected before they reach an upstream provider. ## Allow specific providers ```json theme={null} { "name": "OpenAI only", "access": { "providers": { "allow": ["openai"] } }, "router": { "rules": [ { "provider": "openai", "steps": [{ "type": "ngrok" }] } ] } } ``` Assign this configuration to an access key. That key can only reach OpenAI models. ## Allow specific models ```json theme={null} { "name": "Cheap models only", "access": { "models": { "allow": ["openai/gpt-4o-mini", "anthropic/claude-haiku-4-5"] } } } ``` Model IDs use the catalog form `author/model`, as shown in the [model catalog](/docs/ai-gateway/reference/model-catalog). This form matches the model on every provider that serves it. To pin a provider, prefix it: `openai:openai/gpt-4o`. See [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models#allow-specific-models). ## Block pass-through models If clients use provider prefixes for uncatalogued models, include only known model IDs in the allowlist. Requests for models not listed are rejected. ## Via dashboard Follow [Create a configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) in [app.ngrok.ai](https://app.ngrok.ai) to set allowed providers and models, then assign the configuration to your access keys. ## Next steps * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations): Full reference * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Model IDs and provider prefixes * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Limit models per key * [Securing Your Gateway](/docs/ai-gateway/guides/securing-endpoints): Per-client keys # Multi-Key Failover Source: https://ngrok.com/docs/ai-gateway/examples/multi-key-failover Configure multiple provider keys for automatic failover across rate limits and errors. Add multiple provider keys to the same routing rule in an [access key configuration](/docs/ai-gateway/guides/access-key-configurations). The gateway tries keys in order when one hits a rate limit or an auth error. Your application sends only an [access key](/docs/ai-gateway/concepts/access-keys)—provider keys are stored in app.ngrok.ai and referenced by the configuration. ## Setup 1. [Add two or more provider keys](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) for the same provider 2. Create or edit a configuration with an ordered `user` step listing both key IDs 3. Assign the configuration to your access key ```json theme={null} { "router": { "rules": [ { "provider": "openai", "steps": [ { "type": "user", "keySelectionStrategy": "ordered", "keys": [ { "id": "aigpk_primary" }, { "id": "aigpk_backup" } ] } ] } ] } } ``` ## Failover sequence ``` 1. Request arrives with access key 2. Try primary OpenAI key → Rate limited (429) 3. Try backup OpenAI key → Success ``` ## Next steps * [Multi-key failover guide](/docs/ai-gateway/guides/key-selection-failover): Full details * [Bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys): Store credentials * [Multi-provider failover](/docs/ai-gateway/examples/multi-provider-failover): Fail over across providers # Multi-Provider Failover Source: https://ngrok.com/docs/ai-gateway/examples/multi-provider-failover Fail over between models and providers in AI Gateway requests. You can configure fail over by listing multiple models in a request. The gateway tries each model in order until one succeeds. Combine this with an [access key configuration](/docs/ai-gateway/guides/access-key-configurations) that allows the providers you need. Your app sends an [access key](/docs/ai-gateway/concepts/access-keys); the AI Gateway routes to each provider with the credentials from your configuration. ## Basic example ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="gpt-4o", extra_body={ "models": ["anthropic:claude-sonnet-4-6"], }, messages=[{"role": "user", "content": "Hello!"}], ) ``` Or in raw JSON: ```json theme={null} { "model": "gpt-4o", "models": ["anthropic:claude-sonnet-4-6"], "messages": [{"role": "user", "content": "Hello"}] } ``` ## How it works 1. Request arrives with your access key 2. Gateway tries `gpt-4o` (OpenAI) 3. On failure, tries `anthropic:claude-sonnet-4-6` 4. Returns the first successful response Both providers must be allowed by the access key's configuration, with routing rules that supply credentials (pay with credits or provider keys). ## Configuration example Allow OpenAI and Anthropic on one key: ```json theme={null} { "name": "Multi-provider production", "access": { "providers": { "allow": ["openai", "anthropic"] } }, "router": { "rules": [ { "provider": "openai", "steps": [{ "type": "ngrok" }] }, { "provider": "anthropic", "steps": [{ "type": "ngrok" }] } ] } } ``` Use provider keys instead of `ngrok` steps when billing should go to your provider accounts. ## Next steps * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Model IDs and provider prefixes * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Fallback ordering and access rules * [Multi-key failover](/docs/ai-gateway/guides/key-selection-failover): Fail over between provider keys * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations): Scope and routing # FAQ Source: https://ngrok.com/docs/ai-gateway/faq Frequently asked questions about ngrok.ai. *** ## Getting started ### What URL do I send requests to? Send model requests to `https://gateway.ngrok.ai`. If you are using an OpenAI-compatible SDK, set the base URL to: ```text theme={null} https://gateway.ngrok.ai/v1 ``` For example: ```typescript theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx" }); ``` See the [Quickstart](/docs/ai-gateway/quickstart) for a full example. ### Can I use my existing OpenAI SDK? Yes. The AI Gateway supports OpenAI-compatible requests, so you can usually keep your SDK and change the base URL and API key. Use your ngrok.ai access key as the API key, and set the base URL to `https://gateway.ngrok.ai/v1`. See [SDK Integration](/docs/ai-gateway/sdks) for examples. ### Do I need a provider account to use ngrok.ai? Not always. For many providers, including OpenAI, Anthropic, Google, and DeepSeek, you can skip adding your own provider key and pay with credits instead. For other built-in providers, or if you want usage billed to your own provider account, you can add a provider key. ### Do I need credits? Yes. Every request through ngrok.ai require credits for the gateway processing fee. If ngrok.ai supplies the upstream provider credentials, credits also pay the upstream model cost. If you bring your own provider key, the provider bills you for model usage and credits only pay the gateway processing fee. See [Credits](/docs/ai-gateway/concepts/credits). ### Do I need a ngrok subscription plan? No, not to use the AI Gateway itself. You can sign in at [app.ngrok.ai](https://app.ngrok.ai), purchase AI Gateway credits, create an access key, and send requests to `https://gateway.ngrok.ai`. You may need a ngrok platform plan for platform features on the same account, such as team members or internal endpoints for custom providers. Manage those features from [dashboard.ngrok.com](https://dashboard.ngrok.com). See [Credits](/docs/ai-gateway/concepts/credits). ### Can my team sign in with SSO? Yes. app.ngrok.ai uses your ngrok account to sign in, so it uses your account's single sign-on (SAML or OpenID Connect, with providers like Okta). SSO requires the Pay-as-you-go plan with the SSO & RBAC add-on. Set it up on your [ngrok account settings](https://dashboard.ngrok.com/settings). See [Dashboard Single Sign-On](/docs/iam/sso). Role-based permissions inside app.ngrok.ai aren't available yet. ## Keys and credentials ### Which key do I put in my app? Use an access key. Your app sends the access key to `https://gateway.ngrok.ai`. Do not put provider keys, ngrok API keys, or AI Gateway API keys in your app's model requests. See [Access keys](/docs/ai-gateway/concepts/access-keys). ### What is the difference between access keys, provider keys, AI Gateway API keys, and ngrok API keys? | Key | Used for | | - | - | | Access key | Authenticates model requests to `gateway.ngrok.ai` | | Provider key | Authenticates ngrok.ai to an upstream provider | | AI Gateway API key | Automates AI Gateway resources through `api.ngrok.ai` | | ngrok API key | Manages ngrok platform resources through `api.ngrok.com` | Most applications only need an access key in runtime code. ### What are access keys? Access keys are what your app uses to authenticate model requests to `https://gateway.ngrok.ai`. It’s a good idea to create separate access keys for different applications, environments, or teams so you can easily track usage, control access, and revoke keys if needed. ### Can I create multiple access keys? Yes, you can. It’s a good idea to create separate access keys for each app, environment, client, or team. Doing this makes it easier to track usage, revoke access when needed, and apply different access key configurations. ### What happens if I lose an access key? Access key tokens are only shown once when they are created. If you lose one, delete the old access key, create a new one, and update your application with the new token. ### Are access keys scoped to endpoints? No. Access keys are account-scoped and work with `https://gateway.ngrok.ai`. You do not need to create or manage a custom endpoint URL to use the AI Gateway. ### Can I use different keys for different teams? Yes. Create separate access keys for each team or client. To control what each key can call, assign different [access key configurations](/docs/ai-gateway/guides/access-key-configurations). Configurations can limit providers, models, and routing credentials. ### Do I need provider keys? It depends on the provider and how you want billing to work. For models you can [pay for with credits](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials), you don't need to add your own provider key. That includes models from OpenAI, Anthropic, Google, DeepSeek, xAI, Moonshot AI, Z.ai, and others. For other built-in providers, or if you want upstream usage billed to your own provider account, add a [provider key](/docs/ai-gateway/guides/attaching-provider-keys). ### Can I bring my own key for every provider? Yes. Every built-in provider accepts your own provider key, including OpenAI and Anthropic. ngrok-managed keys, where you pay with ngrok.ai credits instead of a provider account, are available for supported models from OpenAI, Anthropic, Google, DeepSeek, xAI, Moonshot AI, Z.ai, and others. The [model catalog](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials) shows which providers need your own key. Add provider keys on the [Providers page](https://app.ngrok.ai/providers) in app.ngrok.ai or with the [Provider Keys API](/docs/ai-gateway/api-reference/provider-keys/create). See [Bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys). ### Are provider keys exposed to my app? No. Your app sends an access key to the AI Gateway. Provider keys are stored in app.ngrok.ai and used server-side when ngrok.ai calls the upstream provider. They do not need to be stored in your application code. ### Does ngrok.ai provide provider API keys? No. ngrok.ai does not give you provider API keys to copy or use outside the gateway. For models you can pay for with credits, ngrok.ai supplies the provider credentials. For other providers, or when you want to use your own provider account, add your own provider key. ### How do I rotate keys? For access keys, create a new access key, update your application, then delete the old access key. For provider keys, rotate the stored provider key value so configurations that reference the key can keep using the same key ID. See [Bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys#rotating-provider-keys). ### Can I manage the AI Gateway with an API? Yes. The AI Gateway API at `https://api.ngrok.ai` manages access keys, access key configurations, providers, provider keys, gateway settings, credits, models, and usage. Authenticate with an AI Gateway API key, which you create on the [Settings](https://app.ngrok.ai/settings) page. The full [OpenAPI spec](https://api.ngrok.ai/openapi.yaml) is public, so you can generate a client in any language. See the [AI Gateway API reference](/docs/ai-gateway/api-reference/overview). ## Credits and billing ### What do credits pay for? Credits always pay the AI Gateway processing fee. When you pay with credits, credits also pay the provider's model cost. If you bring your own provider key, the provider bills you for model usage and credits only pay the gateway processing fee. ### How much is the processing fee? The ngrok.ai processing fee is `$0.05` per million tokens. ### Do credits expire? Yes. Credits expire 365 days after purchase. ### What happens when credits run out? The gateway rejects new requests with HTTP 402 and `ERR_NGROK_27206` until you purchase more credits from the [Billing page](https://app.ngrok.ai/billing). That's true even if you use your own provider keys, since every request pays a small processing fee from your credits. To avoid this, turn on [automatic top-up](/docs/ai-gateway/concepts/credits#automatic-top-up) so your balance refills before it runs out. ### What's the minimum credit purchase? The minimum credit purchase is `$5.00`. You can purchase credits from the [Billing page](https://app.ngrok.ai/billing). Your balance can't go above `$500.00`. ### Can credits top up automatically? Yes. Turn on automatic top-up on the [Billing page](https://app.ngrok.ai/billing). When your balance falls to an amount you choose, ngrok.ai charges your saved card and tops it back up. You can also set a maximum to spend each billing period. See [Automatic top-up](/docs/ai-gateway/concepts/credits#automatic-top-up). ### Are AI Gateway credits the same as ngrok platform billing? No. AI Gateway credits are purchased and used in [app.ngrok.ai](https://app.ngrok.ai). ngrok platform billing for products managed through [dashboard.ngrok.com](https://dashboard.ngrok.com) is separate, and certain features such as team members and internal endpoints for self-hosted models require a ngrok subscription. ## Models and providers ### Which providers are supported? The AI Gateway includes built-in support for: * OpenAI * Anthropic * Google * Groq * DeepSeek * OpenRouter * Hyperbolic * InceptionLabs * Inference.net You can also connect self-hosted or private OpenAI-compatible and Anthropic-compatible endpoints as custom providers. See the [Model Catalog](/docs/ai-gateway/reference/model-catalog) for the full list. ### Which providers can I call without adding a provider key? You can see which providers require a provider key in the Model Catalog by checking the “needs provider keys” column. Other built-in providers require you to add your own provider key before sending traffic. ### How do I choose a model? Set the `model` field in your request. Use a model ID when you want ngrok.ai to resolve the provider: ```json theme={null} { "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}] } ``` Use `provider:model` when you want to choose the provider explicitly: ```json theme={null} { "model": "openai:gpt-4o", "messages": [{"role": "user", "content": "Hello"}] } ``` See [Choose a model](/docs/ai-gateway/guides/model-selection-strategies). ### How do I know which models are available? Built-in providers and models are listed in the [Model Catalog](/docs/ai-gateway/reference/model-catalog). The catalog includes provider IDs, model IDs, context windows, output token limits, modalities, and provider key requirements. Custom providers declare their own models when you create them. ### Can clients use models not in the catalog? Yes, if the request uses a provider-qualified model name and the provider allows pass-through for that model. For example: ```json theme={null} { "model": "openai:gpt-5-preview", "messages": [{"role": "user", "content": "Hello"}] } ``` To restrict an access key to approved models only, configure a model allowlist in the [access key configuration](/docs/ai-gateway/guides/access-key-configurations). ### Can I use self-hosted models? Yes. Create a custom provider for a model running on your machine, private network, or cloud GPU. See [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself). ### What is a custom provider? A custom provider is an upstream endpoint you define, such as Ollama, vLLM, LM Studio, a private deployment, or another OpenAI-compatible or Anthropic-compatible API. Custom providers let you call models you run yourself through the same gateway URL as built-in providers. See [Custom Providers](/docs/ai-gateway/concepts/custom-providers). ### Why isn't my custom model working? Check that: 1. The upstream exposes a supported OpenAI or Anthropic API surface. 2. The custom provider has the correct base URL. 3. The custom provider declares the model ID you are calling. 4. The endpoint is reachable from ngrok.ai. 5. The access key configuration allows the provider and model. 6. Provider credentials are attached if the upstream requires authentication. See [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself). ## Failover and timeouts ### How does failover work? The gateway tries candidates in order. It can fail over between provider keys for the same provider, then between fallback models listed in the request. The gateway does not retry the same model and provider key combination. It moves to the next available candidate. See [Error Handling](/docs/ai-gateway/guides/error-handling). ### How do I configure fallback models? Use `model` for the primary model and `models` for fallback models. ```json theme={null} { "model": "openai:gpt-4o", "models": ["anthropic:claude-sonnet-4-6"], "messages": [{"role": "user", "content": "Hello"}] } ``` The gateway tries the primary model first, then each fallback model in order until one succeeds. See [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models). ### Can I set fallbacks on the gateway instead of in each request? Not yet. Fallback models are set per request, in the `models` list. On the gateway side today, you can: * Attach multiple provider keys for the same provider, so the gateway [fails over between keys](/docs/ai-gateway/guides/key-selection-failover) on rate limits, quota limits, or auth errors. * Use [access key configurations](/docs/ai-gateway/guides/access-key-configurations) to control which providers and models each access key can reach. To change a default model for many clients without changing their code, put the `model` and `models` values in a shared client wrapper or config your apps already read. ### Can the gateway fail over to a different provider? Yes, if the request includes fallback models from different providers. For example, you can try OpenAI first and fall back to Anthropic: ```json theme={null} { "model": "openai:gpt-4o", "models": ["anthropic:claude-sonnet-4-6"], "messages": [{"role": "user", "content": "Hello"}] } ``` Each provider and model must be allowed by the access key configuration. ### How long does the gateway try before giving up? By default, each upstream attempt can take up to 60 seconds, and the whole request, including every failover attempt, up to 120 seconds. The gateway makes at most 10 upstream attempts per request. | Bound | Default | Configurable | | - | - | - | | Per-request timeout (one upstream attempt) | 60s | Yes | | Total timeout (all attempts) | 120s | Yes | | Upstream attempts per request | 10 | No | Change the timeouts in [Account Settings](/docs/ai-gateway/guides/account-settings) or with the [gateway configuration API](/docs/ai-gateway/api-reference/gateway-config/update). You can also set account-wide input and output token limits there. If the total timeout is reached, the gateway stops trying candidates and returns an error. See [Handle gateway errors](/docs/ai-gateway/guides/error-handling). ### Can I disable automatic failover? Not directly. To narrow failover behavior, use a single provider key per provider, send requests with a single model, avoid fallback models, and set shorter timeouts in [Account Settings](/docs/ai-gateway/guides/account-settings). ### Why am I getting rate limited even with multiple keys? Check that multiple provider keys are attached to the relevant provider in the access key configuration. The gateway only fails over between keys that are available to the request. See [Key selection and failover](/docs/ai-gateway/guides/key-selection-failover). ## Security and privacy ### Does ngrok.ai store request and response bodies? The gateway processes request and response bodies in memory by default and doesn't store them unless you turn on request capture. To turn on capture, open **Settings > Data Retention** in app.ngrok.ai and enable **Request Capture**. Capture stores the request and response headers and bodies for your request and for each attempt the gateway sends to a provider, including streamed responses. Captures are kept for 30 days and then deleted. See [Request capture](/docs/ai-gateway/guides/account-settings#request-capture). The gateway always records request metadata for usage events and debugging, such as token counts, latencies, status codes, providers, and models. See [Observability](/docs/ai-gateway/observability) for more details. ### How long does ngrok.ai keep my data? | Data | Retention | | - | - | | Captured request and response headers and bodies (only if you turn capture on) | 30 days | | Usage events (request metadata: tokens, latency, status, provider, model, cost) | 90 days | ### Can I see individual request and response bodies? Yes, if you've turned on request capture. Captures are available for 30 days on the [Logs](https://app.ngrok.ai/usage) page. Without capture, usage records include only metadata such as token counts and latency, not full request or response bodies. See [Observability](/docs/ai-gateway/observability) for what is captured. ### Can I use the gateway for sensitive data? Yes, but review your own security and compliance requirements. The AI Gateway doesn't store request and response bodies unless you turn on capture. If you do, captured bodies are kept for 30 days. Requests still go to the upstream provider you call, so review the data handling terms for any provider you use. ### How do I redact PII automatically? Automatic redaction of request and response bodies isn’t available on ngrok.ai just yet. Captures do redact credential headers, such as `Authorization`, `x-api-key`, and cookies. Until then, redact any sensitive data in your application before sending it to the gateway. ### How do I secure my gateway when using my own provider keys? You can store your provider keys safely in app.ngrok.ai, and have your application send only access keys. You can use access key configurations to gently control which providers and models each key can call. If an access key is ever exposed, simply revoke it and create a new one. See [Securing Your Gateway](/docs/ai-gateway/guides/securing-endpoints). ## Monitoring and debugging ### How do I view usage and request metrics? You can check out the **Usage** page in app.ngrok.ai, or query usage events with the AI Gateway API. Usage events include helpful details like request time, provider, model, token counts, latency, and status. For more, see [Observability](/docs/ai-gateway/observability). ### How do I debug which provider was used? Take a look at usage events in [app.ngrok.ai](https://app.ngrok.ai), or query them with the AI Gateway API to see what’s happening behind the scenes. If you want more predictable routing while debugging, try using `provider:model` in your request, like `openai:gpt-4o`. ### Why are my requests failing silently? Start with the response status code and error message. Then check: 1. The access key is valid. 2. The access key configuration allows the requested provider and model. 3. The provider key is present if the provider requires one. 4. The model name is valid. 5. The account has credits. 6. The upstream provider is available. See [Debugging](/docs/ai-gateway/guides/debugging) and [Error Codes](/docs/ai-gateway/reference/error-codes). ### Does the gateway add latency? Yes, the gateway can add a small amount of latency for parsing, token counting, routing, and failover decisions. Provider response time usually dominates total latency. ### How are tokens counted? The gateway estimates token counts with `tiktoken` and uses provider-reported counts when available. Token counts appear in [usage events](/docs/ai-gateway/observability#usage-api). ### Can I cache responses to reduce costs? Not yet. Response caching isn’t currently available on ngrok.ai, but it’s under consideration for a future release. ## Troubleshooting ### Why am I getting "provider not allowed"? The requested provider isn’t currently allowed by the access key configuration tied to your access key. To fix this, you can update the configuration to allow that provider, or send the request using an access key that already has permission to call it. See [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations) and [Error Codes](/docs/ai-gateway/reference/error-codes). ### Why am I getting "model unknown"? The gateway could not resolve the model for the request. Common causes include: * The model is not in the catalog. * The model name has a typo. * The request uses a non-catalog model without a provider prefix. * The access key configuration does not allow the model. For non-catalog models, use the `provider:model` format: ```json theme={null} { "model": "openai:gpt-5-preview", "messages": [{"role": "user", "content": "Hello"}] } ``` ### Why is my provider key not working? Check that: 1. The provider key was added to the correct provider. 2. The key is valid and has not expired. 3. The provider account has access to the requested model. 4. The provider account has enough balance or quota. 5. The access key configuration routes requests through that provider key. If usage is not appearing in your provider account, the request may be paid with credits instead of using your provider key. ### Why is failover not working? Start by thinking about the kind of failover you want. If you're looking for provider key failover, make sure you've attached multiple provider keys for the same provider in your access key configuration. If you want model or provider failover, include fallback models in your request: ```json theme={null} { "model": "openai:gpt-4o", "models": ["anthropic:claude-sonnet-4-6"], "messages": [{"role": "user", "content": "Hello"}] } ``` Every fallback provider and model must be allowed by the access key configuration. ### Why is the gateway timing out? The total request time may be hitting the timeout set in [Account Settings](/docs/ai-gateway/guides/account-settings). Try increasing the total timeout, checking upstream provider latency, or reducing the number of fallback candidates. ### Why was my request rejected before reaching a provider? The gateway can reject a request before upstream routing when: * The access key is missing or invalid. * The access key configuration does not allow the provider or model. * The model cannot be resolved. * The account has insufficient credits. * Required provider credentials are missing. Check the response error code and see [Error Codes](/docs/ai-gateway/reference/error-codes). ## Routing behavior ### Can I route based on request content? Not at the gateway layer today. For now, you can use separate access keys and configurations for each client, or handle routing in your application before sending requests to the gateway. ### How do I prioritize certain models or providers? List fallback models in preference order in the request body. ```json theme={null} { "model": "openai:gpt-4o", "models": ["anthropic:claude-sonnet-4-6"], "messages": [{"role": "user", "content": "Hello"}] } ``` For different clients or teams, create separate access keys with different configurations. ### Does the gateway support streaming? Yes. Streaming is supported, and the gateway forwards SSE streams transparently. ### What happens if a stream fails partway through? A stream that fails partway through is billed, because its first bytes were already delivered to your client. If the provider doesn't report usage for that request, the gateway estimates token counts, and the usage event is marked `usageEstimated: true`. To see what happened, look up the request by its `Ngrok-AIG-Request-Id` with the [request attempts endpoint](/docs/ai-gateway/api-reference/usage/request-attempts). See [Handle gateway errors](/docs/ai-gateway/guides/error-handling). ## See also * [Quickstart](/docs/ai-gateway/quickstart): Get started quickly * [How It Works](/docs/ai-gateway/how-it-works): Request flow and failover * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Model IDs and provider-qualified routing * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Ordered model fallback * [Bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys): Store upstream credentials * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations): Scope access and configure routing * [Error Codes](/docs/ai-gateway/reference/error-codes): Understand failures # Set up an access key configuration Source: https://ngrok.com/docs/ai-gateway/guides/access-key-configurations Scope providers and models and attach provider credentials to an access key. Every request to `https://gateway.ngrok.ai` must include an [access key](/docs/ai-gateway/concepts/access-keys). Upstream provider credentials are managed in [app.ngrok.ai](https://app.ngrok.ai) and injected server-side based on the access key's **configuration**—see [Access keys vs provider keys](/docs/ai-gateway/concepts/access-keys#access-keys-vs-provider-keys). Access key configurations control: * **Access scope**: which providers and models the key may call * **Routing rules**: whether to pay with credits or use your stored [provider keys](/docs/ai-gateway/guides/attaching-provider-keys) for each provider See [Create a configuration](#create-a-configuration) below, or the [Access Key Configurations API](/docs/ai-gateway/api-reference/access-key-configurations/create). ## How it fits together ```mermaid theme={null} flowchart LR App["Your app"] -->|"access key only"| Gateway["gateway.ngrok.ai"] Gateway --> Config["Access key configuration"] Config --> Ngrok["Pay with credits"] Config --> BYOK["Stored provider keys"] Ngrok --> OpenAI["OpenAI / Anthropic"] BYOK --> Providers["Other built-in or custom providers"] ``` 1. Your app sends an **access key** with each request. 2. The gateway loads the configuration linked to that key (or default rules if none is linked). 3. The AI Gateway selects upstream credentials from the configuration's routing rules. ## Default behavior An access key with **no configuration** (allow-all scope) can reach any provider and model your account supports. For built-in models you [can pay for with credits](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials), an ngrok.ai key is used automatically when you have [credits](/docs/ai-gateway/concepts/credits). To use your own provider accounts, restrict scope, or control failover, create a configuration and assign it to the key. ## Create a configuration ### Via the ngrok dashboard 1. Open [app.ngrok.ai](https://app.ngrok.ai) → **Keys** → **Configurations**. 2. Click **New configuration**. 3. Set a name and optional description. 4. Under **Access**, allow only the providers and models this key should use. 5. Under **Routing**, add a rule per provider: * **ngrok**: pay with credits. ngrok.ai supplies the credentials ([supported providers](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials) only) * **Bring your own API key**: attach one or more stored provider keys (tried in order) 6. Save the configuration. 7. On **Keys**, edit an access key and assign the configuration. ### Via the AI Gateway API Create configurations with the [Access Key Configurations API](/docs/ai-gateway/api-reference/access-key-configurations/create). Authorize the request with your [AI Gateway API key](/docs/ai-gateway/concepts/access-keys#credentials-overview). ```bash theme={null} curl -X POST https://api.ngrok.ai/access-key-configurations \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Production OpenAI BYOK", "access": { "providers": { "allow": ["openai"] }, "models": { "allow": ["openai/gpt-4o"] } }, "router": { "rules": [ { "provider": "openai", "steps": [ { "type": "user", "keySelectionStrategy": "ordered", "keys": [{ "id": "aigpk_xxxxx" }] } ] } ] } }' ``` Link the configuration when creating or updating an access key: ```bash theme={null} curl -X PATCH https://api.ngrok.ai/access-keys/aigk_xxxxx \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "accessKeyConfigurationId": "aigac_xxxxx" }' ``` See the [Access Key Configurations API reference](/docs/ai-gateway/api-reference/access-key-configurations/create) and [Access Keys API](/docs/ai-gateway/api-reference/access-keys/update). ## Routing rules Each routing rule targets exactly one **provider** or one **model** and defines an ordered list of steps. A model rule uses the provider-qualified form, such as `openai:openai/gpt-5.5`, and takes precedence over a provider rule for the same provider. A configuration can have at most one rule per provider. | Step type | Meaning | | - | - | | `ngrok` | Pay with credits. ngrok.ai supplies the key ([supported providers](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials) only) | | `user` | Use attached provider keys, tried in list order | For built-in providers, you can pay with credits, bring your own key, or list your own keys first with an `ngrok` step after them as a fallback. The gateway uses the first step that has usable credentials. The `ngrok` fallback only runs when your keys hit auth errors or rate limits. When a configuration has no rule for a provider, requests to that provider are paid with credits only. Stored provider keys are not used without a rule. Custom providers require a `user` step with at least one provider key when the upstream requires authentication. ## Multi-key failover Attach multiple provider keys to the same routing rule step. The gateway tries them in order when a key hits a rate limit or auth error. Server errors and timeouts move to the next model instead. See [Multi-key failover](/docs/ai-gateway/guides/key-selection-failover). ## Restrict providers and models Use the **access** block to limit what an access key can call: ```json theme={null} { "access": { "providers": { "allow": ["openai", "anthropic"] }, "models": { "allow": ["openai/gpt-4o", "anthropic/claude-sonnet-4-6"] } } } ``` Omit `access` entirely for allow-all scope. An empty `allow` array blocks all traffic on that dimension. ## Next steps * [Access Keys](/docs/ai-gateway/concepts/access-keys): Create keys and assign configurations * [Bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys): Store upstream credentials * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Limit what a key can call * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Choose models in requests * [Securing Your Gateway](/docs/ai-gateway/guides/securing-endpoints): Per-client keys and revocation # Configure account settings Source: https://ngrok.com/docs/ai-gateway/guides/account-settings Configure account-wide AI Gateway timeouts and token limits. Account-wide gateway settings apply to every request through `https://gateway.ngrok.ai`, so you can set defaults like timeouts and token limits once instead of repeating them on every [access key configuration](/docs/ai-gateway/guides/access-key-configurations). ## Timeouts | Setting | Default | Description | | - | - | - | | `perRequestTimeout` | `60s` | Max time for a single upstream attempt | | `totalTimeout` | `120s` | Max time for a request including all failover attempts | If a single attempt exceeds `perRequestTimeout`, the gateway moves on to the next fallback. If total time exceeds `totalTimeout`, the gateway returns an error. `perRequestTimeout` must be less than or equal to `totalTimeout`. ### Streaming responses Streams use the timeouts differently, so a long completion isn't cut off: * The per-request timeout applies only until the provider sends the first byte. After that, the stream fails if the provider sends nothing for 30 seconds. * The default total timeout applies only until the first byte. After that, the stream can run for up to 15 minutes. * A `totalTimeout` you set yourself applies to the whole stream, including the time after the first byte. If you set `totalTimeout`, long streaming responses stop when it expires. Leave it unset to keep the 15-minute limit for streams. ## Token limits | Setting | Default | Description | | - | - | - | | `maxInputTokens` | none | Reject requests above estimated input tokens | | `maxOutputTokens` | none | Cap completion length | Leave token limits unset for no cap. Set them to block oversized requests before they reach a provider. Limits must be greater than zero. `maxOutputTokens` also applies when your request doesn't set an output limit: the gateway adds one. When your request asks for more, the gateway lowers it to your limit, and to the model's own maximum. ## Request capture Request capture stores the request and response headers and bodies, including prompts and completions, for your request and each attempt the gateway sends to a provider. You can inspect them on the [Logs](https://app.ngrok.ai/usage) page. Capture is off by default. To turn it on, open **Settings > Data Retention** in app.ngrok.ai and set **Request Capture** to enabled. Captures are kept for 30 days. Credential headers such as `Authorization` and `x-api-key` are redacted. Bodies are stored as sent. ## Via dashboard 1. Open [Settings](https://app.ngrok.ai/settings) in app.ngrok.ai 2. On **General**, edit **Total timeout**, **Per-request timeout**, and optional token limits 3. Save **General** also shows the gateway URL (`https://gateway.ngrok.ai`). The other Settings pages are **Data Retention** for [request capture](#request-capture), **Admin API Keys** for [AI Gateway API keys](/docs/ai-gateway/concepts/access-keys#credentials-overview), and **Account**, which links to [dashboard.ngrok.com](https://dashboard.ngrok.com) for your account details and team members. ## Via the AI Gateway API Get current settings: ```bash theme={null} curl https://api.ngrok.ai/gateway-config \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" ``` Update settings (omitted fields keep their current values): ```bash theme={null} curl -X PATCH https://api.ngrok.ai/gateway-config \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "perRequestTimeout": "60s", "maxInputTokens": 128000, "maxOutputTokens": 8192 }' ``` Clear a token limit by sending `null`: ```bash theme={null} curl -X PATCH https://api.ngrok.ai/gateway-config \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "maxInputTokens": null }' ``` Duration fields use Go duration strings (for example, `30s`, `2m`). See the [Gateway configuration API reference](/docs/ai-gateway/api-reference/gateway-config/get). ## Next steps * [How It Works](/docs/ai-gateway/how-it-works): Request flow and failover * [Choose how to reach providers](/docs/ai-gateway/guides/configuring-providers): Credits, provider keys, and self-run models * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations): Per-key scope and routing # Bring your own provider key Source: https://ngrok.com/docs/ai-gateway/guides/attaching-provider-keys Add an upstream provider key and use it through the AI Gateway. Use provider keys to let ngrok.ai call an upstream provider with your own credentials. Provider keys are the API keys you already use with OpenAI, Google, Anthropic, and others. You can add them to ngrok.ai once, and it uses them to call those providers on your behalf, keeping keys out of your app. For [certain providers](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials), like OpenAI, Anthropic, Google, and DeepSeek, you can skip provider keys and [pay with credits](/docs/ai-gateway/guides/pay-with-credits) instead. ## Add a provider key ### Via the ngrok dashboard Head over to the [Providers](https://app.ngrok.ai/providers) page and choose the provider you want from either the **Built-in** or **Custom** tab. Once you're on the provider's page, scroll down to **API keys** and add your key there. ### Via the AI Gateway API You can add your own API Keys for providers via the [Provider Keys API](/docs/ai-gateway/api-reference/provider-keys/create): ```bash theme={null} curl -X POST https://api.ngrok.ai/provider-keys \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "providerId": "openai", "name": "My OpenAI Key", "value": "sk-proj-..." }' ``` After you add a key, the dashboard only shows a redacted version (`first10***last4`). Make sure you keep a copy of the full key from your provider, since you won’t be able to see it again here. Provider keys are shared across your account, so you can use the same key with multiple access keys. When a key does not have an access configuration attached, the gateway begins using this provider key immediately. To select specific keys, define failover order, or restrict providers and models, use an access key configuration. ## Control access with a configuration An access key without a configuration uses any provider keys you've added, so it works out of the box. If you need more control, you can attach an [access key configuration](/docs/ai-gateway/guides/access-key-configurations). This lets you choose which key to use, set a fallback order, or limit which providers and models are available. First, make sure you've already [added a key](#add-a-provider-key) for the provider. Next, go to your [access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration). Then add a routing rule for the provider, choose **Bring your own API key**, and pick the key you added in the previous step. You can add more than one key for the same provider—if one fails or hits a limit, the gateway will automatically try the next one in your list. For more details, see [key selection and failover](/docs/ai-gateway/guides/key-selection-failover). When using the ngrok.ai API, reference the key's ID in the configuration's router rules: ```json theme={null} { "router": { "rules": [ { "provider": "openai", "steps": [ { "type": "user", "keySelectionStrategy": "ordered", "keys": [{ "id": "aigpk_xxxxx" }] } ] } ] } } ``` Navigate to the [Access Keys](https://app.ngrok.ai/keys) page, and assign the configuration to the [access key](/docs/ai-gateway/concepts/access-keys) your app uses. Your app only needs to send the access key. The gateway uses it to find the right configuration, chooses the provider key, and makes the request for you: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Access key, not the provider key ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Rotating provider keys [Rotating provider keys](/docs/ai-gateway/api-reference/provider-keys/rotate) lets you update its value without changing its ID, so anything already using it keeps working. This means you can keep your existing configurations as they are. Update the key by sending its new value to the [Provider Keys API](/docs/ai-gateway/api-reference/provider-keys/rotate), using your [ngrok.ai API key](/docs/ai-gateway/concepts/access-keys#credentials-overview): ```bash theme={null} curl -X POST https://api.ngrok.ai/provider-keys/aigpk_xxxxx/rotate \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "My OpenAI Key", "value": "sk-proj-..." }' ``` Send a test request with your access key. The key ID is unchanged, so there's nothing else to update. ## Common issues | Problem | What to check | | - | - | | Request is rejected before routing | The access key configuration may not allow the provider or model | | Provider returns an auth error | The provider key may be missing, invalid, expired, or unable to call the requested model | | Usage is not appearing in provider billing | The request may be paid with credits instead of using your provider key | ## Next steps * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations): Attach keys and set scope * [Bring Your Own Keys](/docs/ai-gateway/concepts/bring-your-own-keys): Built-in vs custom providers * [Key selection and failover](/docs/ai-gateway/guides/key-selection-failover): Ordered key failover # Automatic model selection Source: https://ngrok.com/docs/ai-gateway/guides/automatic-model-selection Let ngrok.ai pick a model for each request with ngrok/auto. Set the model to `ngrok/auto` when you want ngrok.ai to choose the model for each request. ngrok.ai reads the prompt, estimates how capable a model it needs, and tries the models your access key can reach in that order, starting with the cheapest model that fits. Use it when your traffic mixes simple and hard prompts and you'd rather not pay top-model prices for "hello" or a unit conversion. ## Send a request Set `model` to `ngrok/auto`: ```python Python highlight={9} theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", # Your access key ) response = client.chat.completions.create( model="ngrok/auto", messages=[{"role": "user", "content": "Explain the difference between TCP and UDP."}], ) print(response.choices[0].message.content) ``` ```bash curl theme={null} curl https://gateway.ngrok.ai/v1/chat/completions \ -H "Authorization: Bearer ng-xxxxx-g1-xxxxx" \ -H "Content-Type: application/json" \ -d '{ "model": "ngrok/auto", "messages": [{"role": "user", "content": "Explain the difference between TCP and UDP."}] }' ``` On Chat Completions (`/v1/chat/completions`), Responses (`/v1/responses`), and Messages (`/v1/messages`), leaving out `model` entirely has the same effect. Most SDKs require a model, so `ngrok/auto` is the clearer choice. `ngrok/auto` also works on these endpoints: | Endpoint | How to ask for `ngrok/auto` | | - | - | | Gemini `generateContent` | Put it in the path: `/v1beta/models/ngrok/auto:generateContent` | | Gemini Interactions (`/v1beta/interactions`) | `"model": "ngrok/auto"` in the body | | OpenAI Decisions (`/v1/decisions`) | `"model": "ngrok/auto"`. Only `gpt-6-luna` serves Decisions, so this always picks it. See [Typed-question APIs](/docs/ai-gateway/guides/decisions-and-system-one) | ## How ngrok.ai picks the model ngrok.ai sorts models into three price bands by what they charge per million input tokens: | Band | Input price | | - | - | | Low | Under \$1 per million tokens | | Medium | \$1 to under \$5 per million tokens | | High | \$5 or more per million tokens | On Chat Completions and Messages, ngrok.ai first asks a small classifier model how much capability the prompt needs: * **Simple prompts**, like greetings, common facts, short rewrites, or a regex, go to the low band. * **Ordinary professional work**, like writing or fixing a small program, a SQL query, or summarizing a document, goes to the medium band. If that work needs little step-by-step reasoning, it goes to the high band instead, because the answer depends more on writing quality. * **Hard prompts**, like multi-step reasoning, competition-style math, rigorous proofs, or expert analysis, go to the high band. If the classifier can't answer quickly, the request starts at the low band instead of failing. On the other endpoints, ngrok.ai doesn't classify the prompt, so every request starts at the low band. ### The order models are tried in Once ngrok.ai has a band, it orders every model your access key can use: 1. Models from [custom providers](/docs/ai-gateway/concepts/custom-providers) you've added a provider key for. 2. The models that do best on that kind of prompt. 3. The rest of the band, cheapest first. 4. The bands above, cheapest first. 5. The bands below, cheapest first. 6. Models with no published price. The band sets where the list starts, not which models are allowed. If the first model fails, the gateway [fails over](/docs/ai-gateway/guides/error-handling) down the list the same way it does for any other request. ### Reasoning effort On Chat Completions and Messages, the classifier also picks a reasoning effort: low, medium, or high. For each model that has a reasoning setting the gateway can set, such as `reasoning_effort` or Anthropic's adaptive thinking effort, that level replaces any reasoning setting you sent. Models without one get your request unchanged. If the classifier can't decide, your own reasoning settings apply. ## See which model answered Read the `Ngrok-AIG-Model` [response header](/docs/ai-gateway/how-it-works#response-headers). It names the model that produced the response. The `model` field in the response body comes from the provider, so use the header when you need to know what answered. The models ngrok.ai prefers can change as new models launch and prices move, so don't depend on a particular model being picked. ## Access key configurations still apply `ngrok/auto` only picks from models the access key can already reach: * The [access key configuration](/docs/ai-gateway/guides/access-key-configurations) must allow the provider and model. * The model needs usable credentials: you [pay with credits](/docs/ai-gateway/guides/pay-with-credits) or have [your own provider key](/docs/ai-gateway/guides/attaching-provider-keys) routed to it. * The model must support the endpoint you called. * The model catalog must mark the model as available to `ngrok/auto`. Narrow-purpose models, such as translation or classifier models, and models past their deprecation date are left out. To keep `ngrok/auto` inside a set of providers or models, [restrict the access key](/docs/ai-gateway/guides/restrict-providers-and-models). If none of the models the key can reach qualify, the request fails with [`ERR_NGROK_27227`](/docs/ai-gateway/reference/error-codes#err_ngrok_27227). Name a model in the request instead. ## Combining with fallback models `ngrok/auto` and a [`models` fallback list](/docs/ai-gateway/guides/configure-fallback-models) are two different ways to pick models, so don't use them together: * On Chat Completions and Messages, if you list real models alongside `ngrok/auto`, the gateway ignores `ngrok/auto` and uses your list as an ordinary fallback list. * On the Gemini endpoints, combining them returns `ERR_NGROK_27228` with the message `Invalid request: models cannot be combined with ngrok/auto`. ## Billing You pay for the model that served the response, the same as if you'd named it. The classifier isn't billed as model usage. Its input tokens count toward the [processing fee](/docs/ai-gateway/concepts/credits#processing-fee) on that request, whether you pay with credits or use your own provider keys. Requests on endpoints that skip the classifier add nothing extra. ## Next steps * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Pick a model yourself by ID or provider * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Control the failover order yourself * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Limit what `ngrok/auto` can pick # Configure fallback models Source: https://ngrok.com/docs/ai-gateway/guides/configure-fallback-models Try another model if the primary model request fails. Use fallback models when you want the AI Gateway to try another model if the primary request fails. Fallback is useful when you want better availability across providers, or when you want to try a private model first and use a hosted model as a backup. ## Add fallback models Set the primary model with `model`. Set fallback models with `models`. ```json theme={null} { "model": "gpt-4o", "models": ["gpt-4o-mini", "anthropic:claude-sonnet-4-6"], "messages": [{"role": "user", "content": "Hello"}] } ``` The gateway tries `gpt-4o` first. If that request fails with a rate limit, an auth error, a server error, or a timeout, it tries each entry in `models` in order until one succeeds. Other 4xx errors, such as an invalid request, return right away. See [what triggers failover](/docs/ai-gateway/guides/error-handling). ## Choose the provider for each fallback Fallback models can use model IDs or `provider:model`. ```json theme={null} { "model": "openai:gpt-4o", "models": [ "openai:gpt-4o-mini", "anthropic:claude-sonnet-4-6" ], "messages": [{"role": "user", "content": "Hello"}] } ``` Use provider-qualified model names when you want to control exactly which provider each fallback uses. ## Fall back from a private model to a hosted model Fallback works with [custom providers](/docs/ai-gateway/concepts/custom-providers), too. ```json theme={null} { "model": "my-ollama:llama3.2", "models": ["openai:gpt-4o-mini"], "messages": [{"role": "user", "content": "Hello"}] } ``` In this example, the gateway tries your `my-ollama` provider first. If that request fails, it falls back to OpenAI. ## Choose fallback models carefully Pick fallback models that can handle the same kind of request as the primary model. Check each model for: * Input modality. * Tool calling support. * Output size. * Context window. * Latency expectations. * Provider credentials. Use the [model catalog](/docs/ai-gateway/reference/model-catalog) to compare built-in model capabilities. ## Access restrictions still apply Fallback models must be allowed by the [access key configuration](/docs/ai-gateway/guides/access-key-configurations) assigned to the access key. If a fallback model isn't allowed, the gateway skips it and names it in the `Ngrok-AIG-Rejected-Models` response header. The request still runs with the models that are allowed. It fails only when none of the models you listed are allowed. ## Next steps * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Choose a model by ID or provider * [Automatic model selection](/docs/ai-gateway/guides/automatic-model-selection): Let ngrok.ai pick the model instead. Don't combine it with `models` * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Limit what an access key can call * [Model catalog](/docs/ai-gateway/reference/model-catalog): Compare model capabilities # Choose how to reach providers Source: https://ngrok.com/docs/ai-gateway/guides/configuring-providers Decide whether to use credits, bring your own provider key, or connect a model you run yourself. The AI Gateway can reach providers in three ways. Pick the path that matches what you're trying to do. ## Use a provider without a provider account Use [credits](/docs/ai-gateway/concepts/credits) when you want the fastest path to OpenAI, Anthropic, Google, DeepSeek, or another [provider you can pay for with credits](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials). The gateway supplies upstream credentials and bills model usage through your credit balance. Start here: [Pay with credits](/docs/ai-gateway/guides/pay-with-credits). ## Bring your own provider key Bring your own key when you want model usage billed to your provider account, need a provider that requires your own credentials, or want to use your own provider account. Start here: [Bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys). ## Use a model you run yourself Use a custom provider when your model runs on your machine, private network, cloud GPU, or another OpenAI- or Anthropic-compatible endpoint outside the built-in catalog. Start here: [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself). ## Make the provider usable from a request Adding a provider or provider key doesn't route traffic by itself. A working request needs: 1. A provider: built-in or custom. 2. A credential source: credits or a provider key, when required. 3. An [access key configuration](/docs/ai-gateway/guides/access-key-configurations) that allows the provider and model. 4. An [access key](/docs/ai-gateway/concepts/access-keys) assigned to that configuration. 5. A request with a matching `model` or `provider:model`. If requests are rejected before they reach the provider, check [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models). ## Next steps * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Use model IDs in requests * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations): Scope access and choose credentials * [Model catalog](/docs/ai-gateway/reference/model-catalog): Check providers, models, and key requirements # Debug failed requests Source: https://ngrok.com/docs/ai-gateway/guides/debugging Diagnose AI Gateway request failures on gateway.ngrok.ai. When troubleshooting a request to `https://gateway.ngrok.ai`: 1. Record the `Ngrok-AIG-Request-Id` [response header](/docs/ai-gateway/how-it-works#response-headers). Every response carries it, errors included, and it identifies the request in usage data. 2. Check the error code and message in the response body. See [Error Codes](/docs/ai-gateway/reference/error-codes). 3. Read `Ngrok-AIG-Attempts` to see whether failover ran. A count above 1 means the gateway tried more than one upstream candidate before it gave up, so the cause is upstream rather than in your request. An absent header means the request failed before any attempt, which points at the request body or the access key configuration. 4. Verify your app sends an [access key](/docs/ai-gateway/concepts/access-keys)—not an upstream provider key. 5. Confirm the access key's [configuration](/docs/ai-gateway/guides/access-key-configurations) allows the provider and model you requested, with routing credentials attached. 6. Look the request up in the dashboard at [app.ngrok.ai](https://app.ngrok.ai), or query it with the [Usage API](/docs/ai-gateway/observability#usage-api). Common fixes: | Symptom | Check | | - | - | | 401 Unauthorized | Access key missing, malformed, or deleted | | 402 Payment Required (27206) | Out of credits: [purchase credits](/docs/ai-gateway/concepts/credits). This happens even if you use your own provider keys | | No keys available for provider (27202) | Configuration missing a routing rule or provider keys for the requested provider | | Provider or model not allowed by the access policy (27231, 27232) | Model or provider blocked by the configuration's access scope | | Every model failed | The response carries the last model's error. Read `Ngrok-AIG-Attempts` and the attempt chain. See [Handle gateway errors](/docs/ai-gateway/guides/error-handling) | | Response names a model you did not expect | Read `Ngrok-AIG-Model`, not the `model` field in the body. The body is passed through byte-for-byte and keeps the provider's own wording | See [Handle gateway errors](/docs/ai-gateway/guides/error-handling) for retry and failover behavior. # Typed-question APIs Source: https://ngrok.com/docs/ai-gateway/guides/decisions-and-system-one Call OpenAI Decisions and Typesafe AI System One through ngrok.ai. Some models don't chat. You send them some input and a list of typed questions, and they send back a typed answer to each one, such as true or false, one choice from a list, or a score. That's useful for routing support tickets, flagging content, or scoring urgency without parsing free-form text. ngrok.ai serves two of these APIs: | API | Endpoint | Provider | Models | | - | - | - | - | | [OpenAI Decisions](https://developers.openai.com/api/docs/guides/decisions) | `POST /v1/decisions` | OpenAI | `gpt-6-luna` | | [Typesafe AI System One](https://docs.typesafe.ai) | `POST /v1/systemone` | [Typesafe AI](/docs/ai-gateway/providers/typesafeai) | `jev-latest`, `jev-preview`, `jev-1.13.0` | You can [pay with credits](/docs/ai-gateway/guides/pay-with-credits) for both, or [bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys) for OpenAI or Typesafe AI. ## Decisions requests A Decisions request has an `input`, in the same message format as the Responses API, and an array of `questions`. Each question has a `type` (`predicate`, `choice`, or `score`), a `name`, and `instructions`. ```bash theme={null} curl https://gateway.ngrok.ai/v1/decisions \ -H "Authorization: Bearer ng-xxxxx-g1-xxxxx" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-6-luna", "input": [ { "role": "user", "content": [ {"type": "input_text", "text": "I was charged twice for my March invoice and need one charge refunded today."} ] } ], "questions": [ {"type": "predicate", "name": "is_refund_request", "instructions": "Does the customer ask for a refund?"}, { "type": "choice", "name": "team", "instructions": "Which team owns this ticket?", "choices": [ {"value": "billing", "description": "Charges, invoices, and refunds."}, {"value": "technical", "description": "Product defects and outages."} ] } ] }' ``` The response has an `answers` array with one entry per question. A `predicate` answer has the `probability` that it's true. A `choice` answer names the `choice` it picked, with a probability for each option: ```json theme={null} { "model": "gpt-6-luna", "answers": [ {"type": "predicate", "name": "is_refund_request", "probability": 1.0}, { "type": "choice", "name": "team", "choice": "billing", "probabilities": [ {"value": "billing", "probability": 1.0}, {"value": "technical", "probability": 0.0} ], "confidence": 1.0 } ], "usage": {"input_tokens": 286, "output_tokens": 0, "total_tokens": 286} } ``` The gateway forwards `model`, `input`, `questions`, and `safety_identifier`. It removes any other top-level field and names it in the `Ngrok-AIG-Removed-Params` [response header](/docs/ai-gateway/how-it-works#response-headers). See [OpenAI's Decisions guide](https://developers.openai.com/api/docs/guides/decisions) for every question type and option. `"model": "ngrok/auto"` also works and always picks `gpt-6-luna`. Any other model returns [`ERR_NGROK_27226`](/docs/ai-gateway/reference/error-codes#err_ngrok_27226), because no other model serves Decisions. When you pay with credits, OpenAI bills a decision on input tokens only, at `gpt-6-luna`'s input rate. ## Typesafe AI requests A System One request has a `state`, the text to evaluate, and a `questions` object that maps each question ID to a typed question. ```bash theme={null} curl https://gateway.ngrok.ai/v1/systemone \ -H "Authorization: Bearer ng-xxxxx-g1-xxxxx" \ -H "Content-Type: application/json" \ -d '{ "model": "jev-latest", "state": "The server returned 500 for every request for ten minutes.", "questions": { "is_urgent": { "type": "noul", "instructions": "Is this an urgent production incident?" } } }' ``` The response maps each question ID to its answer, with a calibrated probability: ```json theme={null} { "model": "jev-1.13.0", "answers": {"is_urgent": {"type": "noul", "noul": 0.91}}, "usage": {"input_tokens": 287, "output_tokens": 23} } ``` The gateway forwards `model`, `state`, and `questions`, and removes any other top-level field. See the [Typesafe AI API reference](https://docs.typesafe.ai) for question types such as `choice` and `score`, and the [Typesafe AI provider page](/docs/ai-gateway/providers/typesafeai) for setup. Name a Jev model in the request. `ngrok/auto` doesn't pick Jev models, so it returns [`ERR_NGROK_27227`](/docs/ai-gateway/reference/error-codes#err_ngrok_27227) on this endpoint. ## What's different from chat requests Both APIs differ from Chat Completions and Messages in a few ways: * **No streaming**: A request with `"stream": true` returns `ERR_NGROK_27228` before reaching the provider. * **No fallback list**: The gateway reads only `model`. Each API has a single provider, so a `models` list is removed rather than used. * **POST only**: Each API has a single create endpoint. Other methods and paths below it return an error. * **Same billing model**: Every request pays the [processing fee](/docs/ai-gateway/concepts/credits#processing-fee). When you pay with credits, credits also cover the model cost. ## Next steps * [Typesafe AI](/docs/ai-gateway/providers/typesafeai): Set up Typesafe AI with credits or your own key * [OpenAI](/docs/ai-gateway/providers/openai): Set up OpenAI with credits or your own key * [How it works](/docs/ai-gateway/how-it-works#response-headers): Read the gateway's response headers # Handle gateway errors Source: https://ngrok.com/docs/ai-gateway/guides/error-handling Understand which failures the AI Gateway retries, the order it tries candidates in, and how timeouts bound the whole request. When an upstream attempt fails, the AI Gateway either moves to another candidate or stops and returns the error. This page explains which failures it retries, in what order, and what bounds the retrying. ## What triggers failover The gateway moves to another candidate on these failures: * Authentication failures: HTTP 401 or 403 because the provider rejected the key. A 401 or 403 for any other reason stops the request. * Rate limits: HTTP 429. * Server errors: HTTP 500 and above. * Request timeouts: HTTP 408, or the attempt exceeding your per-request timeout. * Network failures: connection errors, DNS failures, and TLS errors. Any other 4xx response stops the request immediately. A malformed request or an unsupported parameter is not going to succeed on a different provider, so the gateway returns the error rather than spending your quota on it. ## Failover order Where the gateway goes next depends on how the attempt failed. | Failure | Order the gateway tries | | - | - | | 401 or 403 | Next provider key for the same provider, then an ngrok-managed key if one is available, then the next model | | 429 | A different quota pool for the credential, then an ngrok-managed key if one is available, then the next model | | 500 and above, 408, or a network failure | The next model | Model candidates come from the `model` field first, then the `models` array in order: ```json theme={null} { "model": "gpt-4o", "models": ["anthropic:claude-sonnet-4-5", "google:gemini-3.6-flash"], "messages": [{"role": "user", "content": "Hello"}] } ``` The gateway never retries the same model and key combination, except in the one case below. A bare model ID, such as `gpt-4o`, also expands to every provider that serves that model. The model author's own provider goes first, then resellers such as OpenRouter. A `provider:model` ID, such as `openai:gpt-4o`, reaches only that provider. To fail over to a different model, list it in the `models` array, or set it up as a [fallback model](/docs/ai-gateway/guides/configure-fallback-models). ### Retrying the same provider When a provider answers 429 or 503 with a `Retry-After` header, the gateway waits and retries the same backend instead of moving on. It does that only when the wait is 5 seconds or less, only when the wait still fits inside your total timeout, and at most twice per request. Otherwise it treats the response as a rate limit or a server error and moves to the next candidate. ## What bounds the retrying Two timeouts and an attempt cap bound every request: | Bound | Default | What it limits | | - | - | - | | Per-request timeout | 60s | A single upstream attempt | | Total timeout | 120s | The whole request, including every attempt and any `Retry-After` wait | | Attempt cap | 10 | Upstream attempts per client request. Not configurable | Streams work differently after the first byte. See [Streaming responses](/docs/ai-gateway/guides/account-settings#streaming-responses). Set the timeouts for your account in [Account Settings](/docs/ai-gateway/guides/account-settings), or through the [Configuration API](/docs/ai-gateway/api-reference/gateway-config/get). When the total timeout is reached, failover stops even if candidates remain, and the gateway returns `ERR_NGROK_3810`. ## Failures that never reach failover Some requests fail before the gateway makes any upstream attempt. These return immediately, and the response carries no `Ngrok-AIG-Attempts` header: * The request body could not be parsed. * The model is unknown, or the model address is malformed. * The requested provider or model is blocked by the access key configuration's access scope. * No provider key is available for the requested provider. * The account has no credits. This happens even if you use your own provider keys. None of these are billed. The gateway bills an attempt only once its response reaches your client, so a request that never reached a provider costs nothing. A stream that fails partway through is billed, because its first bytes were already delivered. See [Error Codes](/docs/ai-gateway/reference/error-codes) for the code each of these returns. ## Best practices 1. **Attach several provider keys** per provider so an exhausted credential fails over instead of failing the request. See [Multi-key failover](/docs/ai-gateway/guides/key-selection-failover). 2. **List candidates from more than one provider** so a provider-wide outage has somewhere to go. 3. **Set timeouts to match your latency budget.** A total timeout that is too tight cuts failover short; one that is too loose makes a slow failure look like a hang. 4. **Record `Ngrok-AIG-Request-Id` from every response** so you can look a failure up afterwards. See [Debug failed requests](/docs/ai-gateway/guides/debugging). ## Next steps Error codes, causes, and solutions Diagnose a failure from the response # Upload and use files Source: https://ngrok.com/docs/ai-gateway/guides/files Upload files to OpenAI or Anthropic through ngrok.ai and reference them in requests. Use the Files API when you want to upload a document or image once and reference it by ID in later requests, instead of sending the bytes inline every time. The AI Gateway serves the OpenAI and Anthropic Files APIs at `https://gateway.ngrok.ai/v1/files`. The request and response shapes match the provider's own API, so the OpenAI and Anthropic SDKs work with one extra header. ## Endpoints | Method | Path | Description | | - | - | - | | `POST` | `/v1/files` | Upload a file. The body is `multipart/form-data`. | | `GET` | `/v1/files` | List files. | | `GET` | `/v1/files/{file_id}` | Get a file's metadata. | | `GET` | `/v1/files/{file_id}/content` | Download a file's contents. | | `DELETE` | `/v1/files/{file_id}` | Delete a file. | ## Name the provider A file request has no model, so the gateway can't work out the provider on its own. Every Files API request must send the `Ngrok-AIG-Provider` header: | Provider | Header value | | - | - | | OpenAI | `Ngrok-AIG-Provider: openai` | | Anthropic | `Ngrok-AIG-Provider: anthropic` | The gateway removes this header before it calls the provider. Other providers, including Google, aren't supported. The access key's [configuration](/docs/ai-gateway/guides/access-key-configurations) must allow the provider. ## Upload and use a file Upload the file, then reference its ID in a request to a model from the same provider. A file ID only means something to the provider that stored it. ```python OpenAI Python highlight={11} theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", # Your access key ) uploaded = client.files.create( file=open("report.pdf", "rb"), purpose="user_data", extra_headers={"Ngrok-AIG-Provider": "openai"}, ) response = client.responses.create( model="openai:gpt-5.4", input=[ { "role": "user", "content": [ {"type": "input_file", "file_id": uploaded.id}, {"type": "input_text", "text": "Summarize this report in three bullets."}, ], } ], ) print(response.output_text) ``` ```typescript OpenAI TypeScript highlight={10} theme={null} import fs from "node:fs"; import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const uploaded = await client.files.create( { file: fs.createReadStream("report.pdf"), purpose: "user_data" }, { headers: { "Ngrok-AIG-Provider": "openai" } }, ); const response = await client.responses.create({ model: "openai:gpt-5.4", input: [ { role: "user", content: [ { type: "input_file", file_id: uploaded.id }, { type: "input_text", text: "Summarize this report in three bullets." }, ], }, ], }); console.log(response.output_text); ``` ```python Anthropic Python highlight={10} theme={null} import anthropic client = anthropic.Anthropic( base_url="https://gateway.ngrok.ai", api_key="ng-xxxxx-g1-xxxxx", # Your access key ) uploaded = client.beta.files.upload( file=("report.pdf", open("report.pdf", "rb"), "application/pdf"), extra_headers={"Ngrok-AIG-Provider": "anthropic"}, ) message = client.beta.messages.create( model="anthropic:claude-sonnet-4-6", max_tokens=1024, betas=["files-api-2025-04-14"], messages=[ { "role": "user", "content": [ {"type": "document", "source": {"type": "file", "file_id": uploaded.id}}, {"type": "text", "text": "Summarize this report in three bullets."}, ], } ], ) print(message.content[0].text) ``` ```bash curl theme={null} curl https://gateway.ngrok.ai/v1/files \ -H "Authorization: Bearer ng-xxxxx-g1-xxxxx" \ -H "Ngrok-AIG-Provider: openai" \ -F purpose="user_data" \ -F file="@report.pdf" ``` The Anthropic SDK takes the base URL without `/v1`. The gateway adds the `files-api-2025-04-14` beta flag to Anthropic file requests for you, and keeps any other beta flags you send. Pass `Ngrok-AIG-Provider` on each file call rather than as a client default header, since inference requests don't need it. Files work in these request types: | Endpoint | Where the file ID goes | | - | - | | Chat Completions (`/v1/chat/completions`) | A `file` content part: `{"type": "file", "file": {"file_id": "..."}}` | | Responses (`/v1/responses`) | An `input_file` content part, or a code interpreter tool's `container.file_ids` | | Messages (`/v1/messages`) | A `document` or `image` block with `"source": {"type": "file", "file_id": "..."}` | ## Paying with credits or your own key The gateway picks credentials for a file request the same way it does for a model request to that provider: from the [routing rule](/docs/ai-gateway/guides/access-key-configurations) in the access key's configuration. If the access key has no configuration, or the configuration has no rule for that provider, the gateway uses your stored [provider key](/docs/ai-gateway/guides/attaching-provider-keys) when you have one, and [credits](/docs/ai-gateway/guides/pay-with-credits) otherwise. Each request uses one credential and doesn't switch partway. A file stored with your own key lives in your provider account, and a file uploaded with credits lives with ngrok.ai, so they're never mixed. Upload files and send the requests that use them with the same access key, so both go through the same credentials. To be sure, add a routing rule for the provider to the access key's configuration. ### With your own provider key Requests go straight through to your provider account: * Listing returns every file in your provider account, including files you uploaded outside the gateway. * You can reference any file your provider account holds. * Expiry, retention, and storage limits are the provider's own. ### When you pay with credits ngrok.ai stores the file under an ngrok.ai account at the provider and keeps track of which files belong to your account: * **Expiry**: Files expire 72 hours after upload. On OpenAI, any `expires_after` you send is replaced with that 72-hour expiry. * **Storage limit**: Your account can hold up to 5 GB of files per provider. Delete files you no longer need to free up room. Expired files stop counting once they're removed, shortly after they expire. * **Listing**: Listing only returns files your account uploaded with credits. Pages hold at most 100 files. Follow the cursor in the response (`after` for OpenAI, `after_id` for Anthropic) to read the rest. OpenAI's `order=asc` and Anthropic's `before_id` aren't supported. * **Using files**: A request can only reference files your account uploaded with credits. If a request references any other file ID, the gateway won't use credits for that model. It uses your own provider key for that provider if one is routed, and otherwise returns [`ERR_NGROK_27240`](/docs/ai-gateway/reference/error-codes#err_ngrok_27240). ## Limits * **File size**: An upload can be at most 512 MiB, whichever credentials it uses. * **Timeout**: A transfer must finish within your account's total timeout, 120 seconds by default. Raise it in [Account Settings](/docs/ai-gateway/guides/account-settings) for large files on slow connections. * **Credits**: Uploads, listings, metadata, and downloads need a positive [credit](/docs/ai-gateway/concepts/credits) balance, but they don't spend credits. Deletes work at a zero balance, so you can always free storage. ## Errors File errors use the provider's error format, so your SDK raises its usual exception. A request that doesn't name a provider gets the OpenAI format. | Code | Status | What happened | | - | - | - | | `ERR_NGROK_27240` | 404 | A model request referenced files your account didn't upload with credits. | | `ERR_NGROK_27241` | 400 | The request is missing the `Ngrok-AIG-Provider` header. | | `ERR_NGROK_27242` | 400 | The provider in `Ngrok-AIG-Provider` doesn't support the Files API. | | `ERR_NGROK_27243` | 400 | The list `limit` isn't a whole number from 1 to 100. | | `ERR_NGROK_27244` | 404 | Your account has no file with that ID for that provider. | | `ERR_NGROK_27245` | 400 | A list parameter isn't supported when you pay with credits, such as OpenAI's `order=asc` or Anthropic's `before_id`. | | `ERR_NGROK_27246` | 413 | The upload would put your account over its storage limit for that provider. | | `ERR_NGROK_27247` | 400 | The upload isn't a `multipart/form-data` request. | | `ERR_NGROK_27248` | 413 | The upload is larger than 512 MiB. | See [Error codes](/docs/ai-gateway/reference/error-codes) for every gateway error. ## Next steps * [Pay with credits](/docs/ai-gateway/guides/pay-with-credits): Call providers without your own provider keys * [Bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys): Use files stored in your provider account * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations): Control which credentials a request uses # Configure provider key failover Source: https://ngrok.com/docs/ai-gateway/guides/key-selection-failover Try another provider key when one hits a rate limit, quota limit, or auth error. Configure multiple provider keys in an [access key configuration](/docs/ai-gateway/guides/access-key-configurations) routing rule. The gateway tries keys in order when one hits a rate limit, runs out of quota, or fails authentication. ## How failover works When a routing rule step lists multiple provider keys: 1. The gateway tries the first key. 2. On a rate limit (429), or an auth failure (401 or 403) because the provider rejected the key, it tries the next key. 3. When no keys are left, the gateway falls back to paying with credits if the rule has an `ngrok` step after this one. Otherwise it moves to the next model you listed, or returns the last error. Server errors (5xx), timeouts, and connection failures don't try the next key. Another key for the same provider would likely fail the same way, so the gateway moves straight to the next model you listed. To fail over on those errors, add [fallback models](/docs/ai-gateway/guides/configure-fallback-models). Failover is configured per provider in the access key configuration—not in your application code. ## Via dashboard 1. [Add multiple provider keys](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) for the same provider. 2. [Edit an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration). 3. Add or edit a routing rule for the provider. 4. Attach multiple keys to the **Bring your own API key** step in the order you want them tried. ## Via the AI Gateway API ```bash theme={null} curl -X POST https://api.ngrok.ai/access-key-configurations \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "OpenAI with failover", "router": { "rules": [ { "provider": "openai", "steps": [ { "type": "user", "keySelectionStrategy": "ordered", "keys": [ { "id": "aigpk_primary" }, { "id": "aigpk_backup" } ] } ] } ] } }' ``` See the [Access Key Configurations API reference](/docs/ai-gateway/api-reference/access-key-configurations/create). Assign the configuration to your access key. Your app sends only the access key: ```python theme={null} client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) ``` ## Next steps * [Bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys): Store upstream credentials * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations): Routing rules and scope * [How It Works](/docs/ai-gateway/how-it-works): Full request flow # Choose a model Source: https://ngrok.com/docs/ai-gateway/guides/model-selection-strategies Choose a model by ID or route to a specific provider. When you send a request to `https://gateway.ngrok.ai`, the `model` field tells the AI Gateway which model to call. Use a model ID when you want the gateway to resolve the provider from the [model catalog](/docs/ai-gateway/reference/model-catalog). Use `provider:model` when you want to choose the provider yourself. ## Use a model ID Use a model ID when you know which model you want to call and don't need to force a specific provider. ```json theme={null} { "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}] } ``` The gateway looks up `gpt-4o` in the [model catalog](/docs/ai-gateway/reference/model-catalog) and finds every provider that serves it. It tries the model author's own provider first (OpenAI here), then any reseller that serves the same model, such as OpenRouter. Providers your access key can't use drop out. ## Route to a specific provider Use `provider:model` when you want predictable routing or need to avoid ambiguity between model names. ```json theme={null} { "model": "openai:gpt-4o", "messages": [{"role": "user", "content": "Hello"}] } ``` In this example: * `openai` is the provider ID. * `gpt-4o` is the model ID. Use this format when you have more than one provider configured, want to call a custom provider, or want the request to go to one specific upstream. ## Let ngrok.ai choose Set `model` to `ngrok/auto` to let ngrok.ai pick a model for each request, starting with the cheapest one that fits the prompt. See [Automatic model selection](/docs/ai-gateway/guides/automatic-model-selection). ## Use a custom provider model ```json theme={null} { "model": "my-ollama:llama3.2", "messages": [{"role": "user", "content": "Hello"}] } ``` Custom providers use the same `provider:model` format. In this example, `my-ollama` is your custom provider ID and `llama3.2` is the model ID configured on that provider. ## Check model capabilities Before choosing a model, check the [model catalog](/docs/ai-gateway/reference/model-catalog) for: | Field | Use it to check | | - | - | | Context window | How much input the model can accept | | Output tokens | How much output the model can return | | Modalities | Whether the model supports text, image, audio, video, or files | | Provider key required | Whether you need to bring your own provider key | ## Keep access rules in mind The request can only use providers and models allowed by the [access key configuration](/docs/ai-gateway/guides/access-key-configurations) assigned to the access key. If you need to limit what a client can call, see [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models). ## Next steps * [Model catalog](/docs/ai-gateway/reference/model-catalog): Built-in providers and models * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model if the first one fails * [Automatic model selection](/docs/ai-gateway/guides/automatic-model-selection): Let ngrok.ai pick the model * [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself): Connect Ollama, vLLM, LM Studio, or another private model # Pay with credits Source: https://ngrok.com/docs/ai-gateway/guides/pay-with-credits Call supported providers without adding provider keys. Pay with credits when you want the AI Gateway to supply the provider credentials for you, so you don't need a provider account. Your app sends an [access key](/docs/ai-gateway/concepts/access-keys) to the gateway. The gateway authenticates to the supported upstream provider, and credits cover the model cost plus the gateway processing fee. You can pay with credits for models from OpenAI, Anthropic, Google, DeepSeek, xAI, Moonshot AI, Z.ai, and other built-in providers. Availability is per model; the [model catalog](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials) shows which providers support it. For providers that require your own credentials, [bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys). ## What you'll need * An ngrok account. * An [access key](/docs/ai-gateway/concepts/access-keys) from [app.ngrok.ai](https://app.ngrok.ai). * Credits in your AI Gateway account. ## Create an access key Create an access key in [app.ngrok.ai](https://app.ngrok.ai). Use this key as the API key in your SDK. The access key authenticates your application to the gateway. It is different from a provider key, which authenticates the gateway to an upstream provider. ## Call a supported OpenAI model ```json theme={null} { "model": "openai:gpt-4o", "messages": [{"role": "user", "content": "Hello"}] } ``` You can also use the model ID directly: ```json theme={null} { "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}] } ``` ## Call a supported Anthropic model ```json theme={null} { "model": "anthropic:claude-sonnet-4-6", "messages": [{"role": "user", "content": "Hello"}] } ``` ## How billing works When you pay with credits, credits cover: * The upstream model cost. * Hosted tools the provider bills, such as web search, code execution, and image generation. * The AI Gateway processing fee. Before sending a request, the gateway checks that you can pay for it with credits. If you can't pay for a model or feature with credits, you get an error asking you to add your own provider key. See [`ERR_NGROK_27238`](/docs/ai-gateway/reference/error-codes#err_ngrok_27238). For providers or models you can't pay for with credits, or when you want usage billed directly by the provider, [bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys). ## When to bring your own key instead Bring your own provider key when: * You already have a provider account. * You want usage billed directly by the provider. * You have negotiated provider pricing. * You need a provider or model that requires your own credentials. ## Next steps * [Credits](/docs/ai-gateway/concepts/credits): Learn how credits work * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Choose model IDs and provider-qualified models * [Bring your own provider key](/docs/ai-gateway/guides/attaching-provider-keys): Use your own upstream provider credentials # Restrict providers and models Source: https://ngrok.com/docs/ai-gateway/guides/restrict-providers-and-models Limit which providers and models an access key can call. Use access key configurations to control which providers and models an access key can call. Restrictions are checked before the gateway routes a request upstream. That means a request can fail fast if a client asks for a provider or model the key shouldn't use. ## What you'll need * An [access key](/docs/ai-gateway/concepts/access-keys). * An [access key configuration](/docs/ai-gateway/guides/access-key-configurations). * The provider IDs and model IDs you want to allow. ## Allow specific providers Use provider restrictions when an access key should only call certain providers. ```json theme={null} { "access": { "providers": { "allow": ["openai", "anthropic"] } } } ``` With this configuration, requests can only route to OpenAI or Anthropic. ## Allow specific models Use model restrictions when an access key should only call certain models. ```json theme={null} { "access": { "models": { "allow": ["openai/gpt-4o", "anthropic/claude-sonnet-4-6"] } } } ``` Model entries accept three forms: | Entry | Matches | | - | - | | `gpt-4o` | The model on any provider that serves it | | `openai/gpt-4o` | The catalog ID (author and model). Matches that model on any provider that serves it, including resellers such as OpenRouter | | `openai:openai/gpt-4o` | That model on the `openai` provider only | Use the `provider:` prefix when you always want to use that provider. Combine model rules with a provider allowlist to block resellers. ## Allow providers and models together Combine provider and model restrictions when you want a key to stay within a small set of approved providers and models. ```json theme={null} { "access": { "providers": { "allow": ["openai", "anthropic"] }, "models": { "allow": ["openai/gpt-4o", "anthropic/claude-sonnet-4-6"] } } } ``` Requests outside the allowed providers or models are rejected before they reach an upstream provider. ## How this works with fallback Every model in a fallback request must be allowed. ```json theme={null} { "model": "openai:gpt-4o", "models": ["anthropic:claude-sonnet-4-6"], "messages": [{"role": "user", "content": "Hello"}] } ``` If the primary model is allowed but the fallback model is not, the gateway drops the fallback and names it in the `Ngrok-AIG-Rejected-Models` response header. The request fails only when none of the models you listed are allowed. ## Common patterns | Goal | Configuration | | - | - | | Allow only OpenAI | Allow provider `openai` | | Allow only Claude Sonnet | Allow model `anthropic/claude-sonnet-4-6` | | Give a team access to two providers | Allow both provider IDs | | Block expensive models | Allow only approved model IDs | | Limit a custom provider | Allow the custom provider ID and model IDs | ## Next steps * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations): Full configuration flow * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Use model IDs in requests * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Use allowed fallback models # Secure your gateway Source: https://ngrok.com/docs/ai-gateway/guides/securing-endpoints How access keys provide built-in authorization for the AI Gateway. [Access keys](/docs/ai-gateway/concepts/access-keys) authenticate every request to `https://gateway.ngrok.ai`. The AI Gateway validates each key before forwarding traffic. Upstream provider credentials are managed separately—see [Access keys vs provider keys](/docs/ai-gateway/concepts/access-keys#access-keys-vs-provider-keys). ## How it works 1. [Create an access key](/docs/ai-gateway/concepts/access-keys) in [app.ngrok.ai](https://app.ngrok.ai) or via the AI Gateway API 2. Send the key as `Authorization: Bearer ng-xxxxx-g1-xxxxx` (OpenAI-compatible APIs) or `x-api-key` (Anthropic native API) 3. The gateway validates the key on every request 4. Invalid or missing keys are rejected—requests do not reach providers ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) ``` The AI Gateway replaces your access key with provider credentials from your configuration before forwarding to the upstream provider. ## Per-client keys Create separate access keys for each client or application: * Revoke one client without affecting others * Track usage per key * Assign different [configurations](/docs/ai-gateway/guides/access-key-configurations) to limit scope per client Create access keys with the [Access Keys API](/docs/ai-gateway/api-reference/access-keys/create). Authorize the request with your [AI Gateway API key](/docs/ai-gateway/concepts/access-keys#credentials-overview). ```bash theme={null} curl -X POST https://api.ngrok.ai/access-keys \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Production web app" }' ``` Delete access keys with the [Access Keys API](/docs/ai-gateway/api-reference/access-keys/delete). Authorize the request with your [AI Gateway API key](/docs/ai-gateway/concepts/access-keys#credentials-overview). ```bash theme={null} curl -X DELETE https://api.ngrok.ai/access-keys/aigk_xxxxx \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" ``` See the [Access Keys API reference](/docs/ai-gateway/api-reference/access-keys/delete). Deletion is permanent. Clients using the revoked key receive authentication errors immediately. ## Protecting provider keys Store provider keys in app.ngrok.ai and reference them in configurations. Clients only ever hold the access key. See [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys). ## Next steps * [Access Keys](/docs/ai-gateway/concepts/access-keys): Create and manage keys * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations): Scope and credentials per key * [Error Codes](/docs/ai-gateway/reference/error-codes): Authentication errors # Use a model you run yourself Source: https://ngrok.com/docs/ai-gateway/guides/use-a-model-you-run-yourself Connect Ollama, vLLM, LM Studio, or another private model endpoint to the AI Gateway. Use a custom provider when you want the AI Gateway to call a model running on your machine, private network, or cloud GPU. Custom providers can point to self-hosted tools like Ollama, vLLM, LM Studio, or any OpenAI- or Anthropic-compatible API. ## What you'll need Your model server must: 1. Expose a supported API surface, such as OpenAI Chat Completions, OpenAI Responses, or Anthropic Messages. 2. Be reachable from the AI Gateway. 3. Have at least one model ID configured on the provider. ## Connect a local model with an internal endpoint If your model runs on your machine or private network, expose it with an ngrok internal endpoint. For example, if your model server listens on port `11434`: ```bash theme={null} ngrok http 11434 --url https://my-ollama.internal ``` Use the internal endpoint URL as the custom provider base URL. Internal endpoints (`.internal` domains) are private to your ngrok account, meaning they're not reachable from the public internet. Use the same ngrok account here and in the AI Gateway, otherwise the gateway can't reach the endpoint. ## Create a custom provider In [app.ngrok.ai](https://app.ngrok.ai): 1. Go to **Providers**. 2. Open the **Custom** tab. 3. Select **Add provider**. 4. Choose **External URL** or **Local**. 5. Enter a provider ID, base URL, API format, and model IDs. 6. Add a provider key if the upstream requires authentication. ## Create a custom provider with the API ```bash theme={null} curl -X POST https://api.ngrok.ai/providers \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "providerId": "my-ollama", "name": "Local Ollama", "baseUrl": "https://my-ollama.internal", "supportedApiSurfaces": [ { "format": "openai", "surface": "chat-completions" } ], "models": [{ "modelId": "llama3.2" }] }' ``` See the [Providers API reference](/docs/ai-gateway/api-reference/providers/create) for every field. ## Allow the provider on an access key An access key without a configuration can call a custom provider as soon as you create it. If you haven't added a provider key, the gateway sends the request without credentials. An access key with a configuration can call the provider only when the configuration's access scope allows it. To route authenticated traffic to the provider, or to control which access keys can call it: 1. Add a provider key if the upstream requires authentication. 2. Create or update an [access key configuration](/docs/ai-gateway/guides/access-key-configurations). 3. Allow the custom provider ID. 4. Assign the configuration to an [access key](/docs/ai-gateway/concepts/access-keys). ## Call the model Use `provider:model` in the request. ```json theme={null} { "model": "my-ollama:llama3.2", "messages": [{"role": "user", "content": "Hello"}] } ``` ## URL requirements | URL type | Scheme | Example | | - | - | - | | External | `https://` only | `https://api.example.com/v1` | | ngrok internal | `http://` or `https://` | `https://my-ollama.internal` | HTTP is only allowed for ngrok `.internal` endpoints. External URLs must use HTTPS. ## Next steps * [Ollama guide](/docs/ai-gateway/custom-providers/ollama): Connect Ollama * [vLLM guide](/docs/ai-gateway/custom-providers/vllm): Connect vLLM * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys): Store upstream credentials * [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models): Allow the custom provider on a key # Find out why a request failed over Source: https://ngrok.com/docs/ai-gateway/guides/why-a-request-failed-over Trace one AI Gateway request through every upstream attempt to see which providers it tried, in what order, and which one served it. The AI Gateway may make several attempts before returning a response. The response alone doesn’t show how many attempts were made. This guide shows you how to use a response header to find each attempt and see what happened. ## 1. Read the attempt count off the response Every response where the gateway tried at least one provider includes the `Ngrok-AIG-Attempts` header, which shows how many attempts were made. A value greater than `1` means the first attempt failed and the gateway tried again. ```bash theme={null} curl -i https://gateway.ngrok.ai/v1/chat/completions \ -H "Authorization: Bearer $NGROK_AI_ACCESS_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "models": ["anthropic:claude-sonnet-4-5", "google:gemini-3.6-flash"], "messages": [{"role": "user", "content": "Hello"}] }' ``` ```http theme={null} HTTP/2 200 ngrok-aig-request-id: rq_2abcDEF456 ngrok-aig-attempts: 3 ngrok-aig-model: gemini-3.6-flash ``` The AI Gateway made three attempts. The third candidate in the list handled the request after the first two candidates failed. Use the `Ngrok-AIG-Model` response header to identify the model that handled your request. The AI Gateway passes the provider’s response body through unchanged, including its `model` field. If the gateway switches models after a failed attempt, these values may differ. Use the header as the source of truth. ## 2. Look the request up by ID Pass the `Ngrok-AIG-Request-Id` value to the attempts endpoint: ```bash theme={null} curl https://api.ngrok.ai/usage/requests/rq_2abcDEF456 \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" ``` You get every upstream attempt for that request, ordered by attempt number: ```json theme={null} { "uri": "https://api.ngrok.ai/usage/requests/rq_2abcDEF456", "requestId": "rq_2abcDEF456", "attempts": [ { "event": { "attemptNumber": 1, "provider": "openai", "model": "gpt-4o", "statusCode": 401, "isError": true, "keySource": "user" }, "served": false, "retryAction": "next_key", "completion": "failed", "upstreamDurationMs": 88 }, { "event": { "attemptNumber": 2, "provider": "openai", "model": "gpt-4o", "statusCode": 429, "isError": true, "keySource": "user" }, "served": false, "retryAction": "next_backend", "completion": "failed", "upstreamDurationMs": 214 }, { "event": { "attemptNumber": 3, "provider": "anthropic", "model": "claude-sonnet-4-6", "statusCode": 200, "isError": false, "keySource": "ngrok" }, "served": true, "retryAction": "succeeded", "completion": "succeeded", "upstreamDurationMs": 1902 } ] } ``` This endpoint lets you only view requests from your account. It cannot modify them. If a request belongs to another account, the endpoint returns “not found” instead of “forbidden.” ## 3. Check why the AI Gateway retried or stopped For each attempt, check `completion` to see how it ended and `retryAction` to see what the AI Gateway decided to do next. Use `served` to identify the attempt that returned the response to your client. ### Identify the attempt that returned the response Look for `served: true`. At most one attempt per request has this value. Do not assume the last attempt returned a response. A request can end with a failed attempt that did not produce a response for your client. ### Check how each attempt ended The `completion` field has one of these values: * `succeeded` * `failed` * `timed_out` * `stream_failed` * `unknown` For failed attempts, read the error details to find the cause, then check `retryAction` to understand why the gateway retried or stopped. ### Follow the retry decisions | `retryAction` | What happened | | - | - | | `succeeded` | The attempt produced the response your client received. | | `next_key` | The credential failed authentication, so the gateway tried the next key for the same provider. | | `next_quota_pool` | The credential’s quota pool was exhausted, so the gateway moved to another pool. | | `next_backend` | The provider or model failed, so the gateway tried the next model in the failover list. | | `managed_fallback` | A customer-owned key failed, so the gateway switched to an ngrok-managed key. | | `retry_same_backend` | The provider requested a retry using `Retry-After`, so the gateway waited and retried the same backend. | | `fail_fast` | The failure could not be retried, so the gateway stopped. | | `exhausted` | No candidates remained to try. | `retryAction` is optional. It is absent from attempts recorded before the gateway began storing retry decisions. In the example above, `next_key` on attempt 1 means the first OpenAI key failed authentication, so the gateway tried the second OpenAI key. That key hit a rate limit and no keys were left, so `next_backend` on attempt 2 means the gateway moved to the next model in the failover list. Check each attempt’s error details to see why it failed. ## What this tells you to change | Pattern down the chain | What to do | | - | - | | `next_key` on every OpenAI attempt | Add more [provider keys](/docs/ai-gateway/guides/attaching-provider-keys) for that provider, or raise your quota with them | | `next_backend` on the first candidate every time | Your primary model is the wrong default. Reorder the [fallback list](/docs/ai-gateway/guides/configure-fallback-models) | | A single `fail_fast` attempt | The request itself was rejected. Check the status code and the [error code](/docs/ai-gateway/reference/error-codes); failover would not have helped | | `exhausted` at the end | Every candidate failed. Widen the candidate list or attach credentials for more providers | | `managed_fallback` you did not expect | Your own key is failing and ngrok.ai credits are absorbing the traffic. Check the key | ## What this endpoint does not include This endpoint does not include captured request and response bodies, which could contain sensitive information. ## Next steps How failover decides what to try next Diagnose a failure from the response alone # How It Works Source: https://ngrok.com/docs/ai-gateway/how-it-works Understand the ngrok.ai request flow and failover behavior. ## Request flow When you send a request to `https://gateway.ngrok.ai`: ```mermaid theme={null} sequenceDiagram participant Client participant Gateway as gateway.ngrok.ai participant Provider as AI Provider Client->>Gateway: POST /v1/chat/completions (access key) Gateway->>Gateway: Validate access key Gateway->>Gateway: Apply configuration and select model Gateway->>Provider: Forward request (injected credentials) Provider-->>Gateway: Response (or error) Gateway-->>Client: Return response ``` 1. **Your app sends a request** with your [access key](/docs/ai-gateway/concepts/access-keys) to `https://gateway.ngrok.ai` 2. **The gateway validates the key** and loads its [access key configuration](/docs/ai-gateway/guides/access-key-configurations) 3. **The gateway resolves the model** from your request body 4. **Provider credentials are selected** from the configuration's routing rules: a key ngrok.ai supplies when you pay with credits, or your stored [provider keys](/docs/ai-gateway/guides/attaching-provider-keys) 5. **Unsupported parameters are stripped** for the chosen model before forwarding, and listed in the `Ngrok-AIG-Removed-Params` [response header](#response-headers) 6. **The request is forwarded** to the upstream provider 7. **On failure, the gateway retries** with the next provider key or model candidate 8. **The response is returned** to your app and usage is recorded Manage keys, configurations, providers, and credits at [app.ngrok.ai](https://app.ngrok.ai). Your apps send traffic to `gateway.ngrok.ai`. ## Authentication Every request must include a valid **access key**. Send it as `Authorization: Bearer ng-xxxxx-g1-xxxxx` (OpenAI-compatible APIs) or `x-api-key` (Anthropic native API). Provider keys stay in [app.ngrok.ai](https://app.ngrok.ai). The AI Gateway injects those from your configuration after validating the access key. ## Response headers Every response from `gateway.ngrok.ai` carries ngrok's own headers describing how the gateway handled the request. | Header | Value | When present | | - | - | - | | `Ngrok-AIG-Request-Id` | The request's ID. Use it to look the request up afterwards. | Every response, including rejections and errors. | | `Ngrok-AIG-Attempts` | How many upstream attempts the gateway made. | Once the gateway tries a provider, on both success and failure. Absent when the request failed before any attempt, such as a malformed body or an unknown model. | | `Ngrok-AIG-Model` | The upstream model that produced the response. | Success only. | | `Ngrok-AIG-Rejected-Models` | Comma-separated models from your request that never ran, for example because the access scope blocks them or no credentials are available. Lists at most 4 models. | Once the gateway tries a provider, when at least one requested model was dropped. | | `Ngrok-AIG-Removed-Params` | Comma-separated request fields the gateway didn't send to the provider that served the response, because that provider or model doesn't support them. | Success only, when the gateway removed at least one field. | All five are exposed through CORS, so browser clients can read them. These are ngrok's headers rather than the provider's, so a managed-key caller and a [Bring Your Own Keys](/docs/ai-gateway/concepts/bring-your-own-keys) caller see the same values. Provider rate-limit headers behave differently: the gateway forwards those only when the attempt used your own provider key. ### The model header is authoritative The gateway passes the response body through byte-for-byte. The `model` or `modelVersion` field inside the body is the provider's own word and is never rewritten to name the model that served the response. On a multi-model failover the body can therefore name a different model than the header. Read `Ngrok-AIG-Model` when you need to know what answered. ### Streamed responses On a streamed response the headers commit with the first flushed byte. The attempt count is final at that point, because the gateway cannot retry once bytes have reached your client. ### Trace IDs are not a substitute `Ngrok-Trace-Id` is emitted only when the trace is sampled, it is not exposed through CORS, and it identifies the whole client request rather than a single attempt. Use `Ngrok-AIG-Request-Id` to identify a request. ## Model selection The gateway determines which model and provider to use from your request. | Model in request | What happens | | - | - | | `gpt-4o` | Catalog lookup → OpenAI first, then other providers that serve the same model | | `openai:gpt-4o` | Explicit OpenAI routing | | `your-provider:your-model` | A [model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself) | | `models` array | Fail over through listed models in order | | `ngrok/auto` | ngrok.ai picks a model for each request. See [Automatic model selection](/docs/ai-gateway/guides/automatic-model-selection) | To choose model IDs and provider-qualified model names, see [Choose a model](/docs/ai-gateway/guides/model-selection-strategies). ## Access key configurations Configurations define: * **Access scope**: allowed providers and models * **Routing rules**: pay with credits or use your own provider keys, per provider When a configuration has no routing rule for a provider, requests to that provider are paid with credits only. Stored provider keys are not used without a rule. An access key without a configuration uses allow-all scope. Built-in models you can [pay for with credits](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials) use an ngrok.ai key by default when you have [credits](/docs/ai-gateway/concepts/credits). ## Failover The gateway retries automatically when upstream requests fail. ### What triggers failover? * Rate limits (HTTP 429) * Auth errors (HTTP 401 or 403) because the provider rejected the key * Timeouts, server errors (HTTP 5xx), and connection failures Other 4xx errors return to your client right away, because another model would fail the same way. ### Failover order What the gateway tries next depends on the error: * **Auth error or rate limit**: the next provider key in the routing rule step. When no keys are left and the rule has an `ngrok` step after the **Bring your own API key** step, the gateway falls back to paying with credits. * **Timeout, server error, or connection failure**: the gateway skips the remaining keys for that provider and moves to the next model. The next model comes from your request's `models` array, or from another provider that serves the same model. See [Handle gateway errors](/docs/ai-gateway/guides/error-handling) for the full order. A routing rule uses the first step that has usable credentials. The gateway doesn't try later steps, except an `ngrok` step after a **Bring your own API key** step. To attach several provider keys and control the order they're tried, see [Multi-key failover](/docs/ai-gateway/guides/key-selection-failover). ## Timeouts and token limits Configure account-level limits in [Account Settings](/docs/ai-gateway/guides/account-settings). | Setting | Default | Description | | - | - | - | | Per-request timeout | 60s | Max time for a single upstream attempt | | Total timeout | 120s | Max time including all failover attempts | | Max input tokens | none | Reject requests exceeding estimated input tokens | | Max output tokens | none | Cap completion length | Streaming responses get more time after the first byte: up to 15 minutes with the default total timeout, as long as the provider keeps sending data at least every 30 seconds. See [Streaming responses](/docs/ai-gateway/guides/account-settings#streaming-responses). Request bodies are limited to 32 MiB. ## Next steps Create keys to authenticate requests Scope and route traffic per key Model IDs, provider prefixes, and custom provider models How the gateway handles failures # Observability Overview Source: https://ngrok.com/docs/ai-gateway/observability/index Monitor and debug AI Gateway requests. Monitor AI Gateway usage from [app.ngrok.ai](https://app.ngrok.ai) and the AI Gateway API. Query usage events and aggregated overview via the AI Gateway API Troubleshoot failed requests and error codes Reference for gateway error responses ## Identify a request Every gateway response carries `Ngrok-AIG-Request-Id`, plus headers such as `Ngrok-AIG-Attempts`, `Ngrok-AIG-Model`, `Ngrok-AIG-Rejected-Models`, and `Ngrok-AIG-Removed-Params` that describe how the request was served. Record the request ID in your application logs so you can tie a client-side failure to gateway usage data later. See [Response headers](/docs/ai-gateway/how-it-works#response-headers). ## Usage API Query usage with the AI Gateway API: ```bash theme={null} curl "https://api.ngrok.ai/usage/overview?startTime=2026-10-01T00:00:00Z&groupBy=accessKey" \ -H "Authorization: Bearer $AI_GATEWAY_API_KEY" ``` The overview returns daily cost, request, and error series for the window. `startTime` is required. Group by `provider` (the default) or `accessKey`. [List individual usage events](/docs/ai-gateway/api-reference/usage/list) to see one record per upstream attempt. Each event reports the provider, model, status code, token counts, and a per-attempt cost breakdown. Attempts of the same client request share a `requestId`. By default the list returns only each request's terminal attempt. Pass `includeRetries=true` to see the retries the gateway made along the way. When a provider reports no usage of its own, `usageEstimated` is `true` and the provider-reported token fields are all zero. Read `inputTokensEstimate` and `outputTokensEstimate` on those attempts instead. ## Look up one request To see every attempt the gateway made for one request, pass its `Ngrok-AIG-Request-Id` to [`GET /usage/requests/{requestId}`](/docs/ai-gateway/api-reference/usage/request-attempts). See [Why a request failed over](/docs/ai-gateway/guides/why-a-request-failed-over). ## Logs in app.ngrok.ai The [Logs](https://app.ngrok.ai/usage) page in app.ngrok.ai lists recent requests. Open a request to see its attempts. If you turn on [request capture](/docs/ai-gateway/guides/account-settings#request-capture), the request detail also shows the captured headers and bodies. **Have feedback?** Email [niji@ngrok.ai](mailto:niji@ngrok.ai) with observability requests. # AI Gateway Overview Source: https://ngrok.com/docs/ai-gateway/overview Route requests to AI providers with automatic failover, load balancing, and observability. The ngrok AI Gateway ([ngrok.ai](https://ngrok.ai)) enables your applications to call both hosted AI providers and local models through a single endpoint. Control who has access, decide what they can call, and choose whether requests use credits, your provider keys, or a model you run yourself. ## Why use the AI Gateway? Use OpenAI, Anthropic, Google, DeepSeek, xAI, and other built-in providers with an access key and credits. No provider signup required. Route to local models like Ollama or vLLM alongside cloud providers with [a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself). If one provider or key fails, ngrok.ai automatically tries the next model or key. Works with official and third-party SDKs. Point the base URL at `gateway.ngrok.ai` and use your access key. ## Quick example Sign up at [app.ngrok.ai](https://app.ngrok.ai) and point your SDK at the gateway with your [access key](/docs/ai-gateway/concepts/access-keys): ```python Python (OpenAI) highlight={4-5} theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) ``` ```python Python (Anthropic) highlight={4-5} theme={null} import anthropic client = anthropic.Anthropic( base_url="https://gateway.ngrok.ai", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) message = client.messages.create( model="claude-opus-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) ``` ```typescript TypeScript (OpenAI) highlight={4-5} theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx" // Your access key }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }] }); ``` ```typescript TypeScript (Anthropic) highlight={4-5} theme={null} import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://gateway.ngrok.ai", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const message = await client.messages.create({ model: "claude-opus-4-6", max_tokens: 1024, messages: [{ role: "user", content: "Hello!" }], }); ``` ```bash cURL (OpenAI) highlight={3} theme={null} curl https://gateway.ngrok.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ng-xxxxx-g1-xxxxx" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}] }' ``` ```bash cURL (Anthropic) highlight={3} theme={null} curl https://gateway.ngrok.ai/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: ng-xxxxx-g1-xxxxx" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-opus-4-5", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello!"}] }' ``` You don't need to sign up for OpenAI or bring your own OpenAI key to make this request. Your ngrok.ai credits will cover any provider costs. Every request consumes [credits](/docs/ai-gateway/concepts/credits). No subscription plan required to get started. See [Credits](/docs/ai-gateway/concepts/credits) for more details and when you might need a ngrok subscription for features like team members or internal endpoints. On each request, the AI Gateway: 1. Receives your request with your access key at `gateway.ngrok.ai` 2. Validates the key and loads your account configuration 3. Selects which model and provider to use 4. Forwards the request using a key ngrok.ai supplies (paid with credits) or your provider keys 5. Retries with the next key or model on failure 6. Returns the response ## What can you do? | Use Case | Description | | - | - | | **Use without provider accounts** | Get started with OpenAI, Anthropic, Google, and [other providers](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials) using an access key and [credits](/docs/ai-gateway/concepts/credits) | | **Per-key routing** | [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) scope providers, models, and credentials | | **Multi-model failover** | List multiple models in a request; the gateway tries each in order | | **Multi-key failover** | Attach multiple provider keys in a configuration routing rule | | **Self-hosted models** | Route to Ollama, vLLM, or other servers with [a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself) | ## Next steps Get ngrok.ai integrated in 5 minutes Learn about managing access to ngrok.ai Request flow and failover behavior Connect your application to ngrok.ai Use your own provider API keys for providers Manage keys, providers, settings, and usage from your own code # Anthropic Source: https://ngrok.com/docs/ai-gateway/providers/anthropic Use Anthropic Claude models through the ngrok AI Gateway. This guide walks through using [Anthropic](https://anthropic.com) Claude models with the AI Gateway. Anthropic is a **built-in provider** you can pay for with credits, so you can use Claude models without an Anthropic account of your own, or bring your own key. ## Pay with credits (no Anthropic account) You can use Claude models without your own Anthropic account. ngrok.ai supplies the Anthropic credentials and bills the model cost to your credits. Follow the [quickstart](/docs/ai-gateway/quickstart) to create an [access key](/docs/ai-gateway/concepts/access-keys) and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Point your SDK at the gateway with your access key: ```python Python theme={null} import anthropic client = anthropic.Anthropic( base_url="https://gateway.ngrok.ai", api_key="ng-xxxxx-g1-xxxxx", # Access key ) message = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) ``` ```bash cURL theme={null} curl https://gateway.ngrok.ai/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: ng-xxxxx-g1-xxxxx" \ -H "anthropic-version: 2023-06-01" \ -d '{"model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello!"}]}' ``` The gateway supports both the Anthropic Messages API (`/v1/messages`) and OpenAI-compatible Chat Completions. ## Bring your own Anthropic key Want requests billed to your own Anthropic account instead of through credits? Store your Anthropic key once—the gateway attaches it for you on every request. Follow the [quickstart](/docs/ai-gateway/quickstart) to create an [access key](/docs/ai-gateway/concepts/access-keys) in [app.ngrok.ai](https://app.ngrok.ai) if you don't have one yet. [Add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) for Anthropic. ngrok stores it encrypted, so it never has to live in your application or get shared around—and you can reuse it across as many access keys as you need. [Create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with an Anthropic routing rule set to **Bring your own API key**, then assign it to your access key. Your code doesn't change. Send the same access key as before—the gateway swaps in your Anthropic key before the request leaves for Anthropic. ## Explicit provider routing ```python theme={null} message = client.messages.create( model="anthropic:claude-sonnet-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Available models Browse the [model catalog](/docs/ai-gateway/reference/model-catalog#anthropic) for the full list of Claude models you can call through the gateway. ## Files Anthropic also supports the [Files API](/docs/ai-gateway/guides/files) for uploading documents and images once and referencing them by ID. ## Next steps * [Access Keys](/docs/ai-gateway/concepts/access-keys) * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations) * [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys) # DeepSeek Source: https://ngrok.com/docs/ai-gateway/providers/deepseek Use DeepSeek models through the ngrok AI Gateway. [DeepSeek](https://www.deepseek.com) provides high-performance reasoning and chat models. DeepSeek supports Chat Completions, Responses, and Anthropic Messages requests. DeepSeek is a **built-in provider** you can pay for with credits, so a provider key is optional. Add your own DeepSeek key to bill usage to your DeepSeek account instead of your ngrok.ai credits. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Skip this step to pay with credits. To bring your own key, sign up at [platform.deepseek.com](https://platform.deepseek.com) and create an API key. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) and [create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `deepseek` routing rule that uses it. Send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="deepseek-v4-flash", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#deepseek). ## Next steps * [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations) # Google Source: https://ngrok.com/docs/ai-gateway/providers/google Use Google Gemini Chat Completions, GenerateContent, and Interactions through the ngrok AI Gateway. [Google AI Studio](https://aistudio.google.com) provides Gemini models. The gateway supports three APIs: * OpenAI-compatible Chat Completions: if your OpenAI client already uses the gateway URL and your ngrok access key, change only the model name and send requests to `/v1/chat/completions`. * Native Gemini GenerateContent: send requests to `/v1beta/models/{model}:generateContent` or `:streamGenerateContent` with a Gemini client. * Native Gemini Interactions: send stateless create requests to `/v1beta/interactions`, with a JSON response or a stream of server-sent events (SSE). The OpenAI Responses API (`/v1/responses`) doesn't reach Google models. Google is a **built-in provider** you can pay for with credits, so a provider key is optional. Add your own Google key to bill usage to your Google account instead of your ngrok.ai credits. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Skip this step to pay with credits. To bring your own key, generate one at [aistudio.google.com](https://aistudio.google.com), then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key). [Create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `google` routing rule and allow Google in the access scope. To use your own Google key, set the routing rule to **Bring your own API key** and select your stored Google key. Assign the configuration to your access key. Follow the [Google Gen AI SDK guide](/docs/ai-gateway/sdks/google) to install the SDK and send requests. ## Native Gemini credentials See [SDK authentication](/docs/ai-gateway/sdks/google#authentication) for access key headers and credential precedence. ## Generate content requests See [GenerateContent requests](/docs/ai-gateway/sdks/google#generate-content-requests) for native endpoints and cURL examples, or [SDK streaming](/docs/ai-gateway/sdks/google#streaming) for Python and TypeScript examples. ## Interactions requests See [SDK Interactions](/docs/ai-gateway/sdks/google#interactions) for examples and gateway restrictions, or [Interactions requests](/docs/ai-gateway/sdks/google#interactions-requests) for the REST endpoint. ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#google). ## Next steps * [Google Gen AI SDK](/docs/ai-gateway/sdks/google) * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # Groq Source: https://ngrok.com/docs/ai-gateway/providers/groq Use Groq LPU-accelerated models through the ngrok AI Gateway. [Groq](https://groq.com) provides high-speed inference for open-source models (GPT-OSS, Llama 4, Qwen3, and others) using their custom LPU hardware. Even though Groq is a **built-in provider**, it still requires you to **bring your own key** because you can't pay for it with credits at this time. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key in [app.ngrok.ai](https://app.ngrok.ai). Sign up at [console.groq.com](https://console.groq.com) and create an API key. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key). [Create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `groq` routing rule and allow Groq in the access scope. Assign the configuration to your access key and send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="groq:openai/gpt-oss-120b", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#groq). ## Next steps * [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations) # Hyperbolic Source: https://ngrok.com/docs/ai-gateway/providers/hyperbolic Use Hyperbolic open-source models through the ngrok AI Gateway. [Hyperbolic](https://hyperbolic.xyz) provides high-performance inference for open-source models. Even though Hyperbolic is a **built-in provider**, it still requires you to **bring your own key** because you can't pay for it with credits at this time. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key in [app.ngrok.ai](https://app.ngrok.ai). Sign up at [app.hyperbolic.xyz](https://app.hyperbolic.xyz) and generate an API key. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key). [Create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `hyperbolic` routing rule and allow Hyperbolic in the access scope. Assign the configuration to your access key and send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="hyperbolic:meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#hyperbolic). ## Next steps * [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations) # InceptionLabs Source: https://ngrok.com/docs/ai-gateway/providers/inceptionlabs Use InceptionLabs diffusion language models through the ngrok AI Gateway. [InceptionLabs](https://www.inceptionlabs.ai) develops diffusion-based language models for fast, efficient text generation. Even though InceptionLabs is a **built-in provider**, it still requires you to **bring your own key** because you can't pay for it with credits at this time. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key in [app.ngrok.ai](https://app.ngrok.ai). Sign up at [inceptionlabs.ai](https://www.inceptionlabs.ai) and obtain an API key. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key). [Create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with an `inceptionlabs` routing rule and allow InceptionLabs in the access scope. Assign the configuration to your access key and send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="inceptionlabs:mercury-coder-small-beta", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#inceptionlabs). ## Next steps * [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations) # Inference.net Source: https://ngrok.com/docs/ai-gateway/providers/inference-net Use Inference.net distributed inference through the ngrok AI Gateway. [Inference.net](https://inference.net) provides a distributed inference network for running AI models at scale. Even though Inference.net is a **built-in provider**, it still requires you to **bring your own key** because you can't pay for it with credits at this time. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key in [app.ngrok.ai](https://app.ngrok.ai). Sign up at [inference.net](https://inference.net) and generate an API key. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key). [Create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with an `inference.net` routing rule and allow Inference.net in the access scope. Assign the configuration to your access key and send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="inference.net:meta-llama/llama-3.1-8b-instruct/fp-8", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#inference-net). ## Next steps * [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations) # MiniMax Source: https://ngrok.com/docs/ai-gateway/providers/minimax Use MiniMax M-series models through the ngrok AI Gateway. [MiniMax](https://www.minimax.io) builds the MiniMax M-series models. MiniMax supports Chat Completions, Responses, and Anthropic Messages requests. MiniMax is a **built-in provider** you can pay for with credits, so a provider key is optional. Add your own MiniMax key to bill usage to your MiniMax account instead of your ngrok.ai credits. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Skip this step to pay with credits. To bring your own key, create an API key in your [MiniMax](https://www.minimax.io) account. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) and [create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `minimax` routing rule that uses it. Send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="minimax:minimax-m3", messages=[{"role": "user", "content": "Hello!"}] ) ``` Tencent TokenHub also serves MiniMax M3, so the `minimax:` prefix keeps the request on MiniMax. ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#minimax). Each `-highspeed` model is a separate model ID, not an option on the base model. ## Next steps * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # Mistral AI Source: https://ngrok.com/docs/ai-gateway/providers/mistral Use Mistral AI models through the ngrok AI Gateway. [Mistral AI](https://mistral.ai) serves its own models and open models from other labs through its API. Mistral AI supports Chat Completions requests. Even though Mistral AI is a **built-in provider**, it still requires you to **bring your own key** because you can't pay for it with credits at this time. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key in [app.ngrok.ai](https://app.ngrok.ai). Create an API key in your [Mistral AI](https://mistral.ai) account. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key). [Create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `mistral` routing rule and allow Mistral AI in the access scope. Assign the configuration to your access key and send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="mistral:z-ai/glm-5.3", messages=[{"role": "user", "content": "Hello!"}] ) ``` Other providers also serve GLM-5.3, so the `mistral:` prefix keeps the request on Mistral AI. ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#mistral-ai). To call a Mistral AI model that isn't in the catalog yet, send its ID with the `mistral:` prefix. See [catalog updates](/docs/ai-gateway/reference/model-catalog#catalog-updates). ## Next steps * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # Moonshot AI Source: https://ngrok.com/docs/ai-gateway/providers/moonshotai Use Moonshot AI Kimi models through the ngrok AI Gateway. [Moonshot AI](https://www.moonshot.ai) builds the Kimi family of models. Moonshot AI supports Chat Completions requests. Moonshot AI is a **built-in provider** you can pay for with credits, so a provider key is optional. Add your own Moonshot AI key to bill usage to your Moonshot AI account instead of your ngrok.ai credits. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Skip this step to pay with credits. To bring your own key, create an API key in your [Moonshot AI](https://www.moonshot.ai) account. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) and [create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `moonshotai` routing rule that uses it. Send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="moonshotai:kimi-k3", messages=[{"role": "user", "content": "Hello!"}] ) ``` QwenCloud and Tencent TokenHub also serve Kimi models, so the `moonshotai:` prefix keeps the request on Moonshot AI. ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#moonshot-ai). ## Next steps * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # OpenAI Source: https://ngrok.com/docs/ai-gateway/providers/openai Use OpenAI models through the ngrok AI Gateway. This guide walks through using [OpenAI](https://openai.com) models with the AI Gateway. OpenAI is a **built-in provider** you can pay for with credits, so you can use OpenAI models without an OpenAI account of your own, or bring your own key. ## Pay with credits (no OpenAI account) You can use OpenAI models without your own OpenAI account. ngrok.ai supplies the OpenAI credentials and bills the model cost to your credits. Follow the [quickstart](/docs/ai-gateway/quickstart) to create an [access key](/docs/ai-gateway/concepts/access-keys) and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Point your SDK at the gateway with your access key: ```python Python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Access key ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) ``` ```bash cURL theme={null} curl https://gateway.ngrok.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ng-xxxxx-g1-xxxxx" \ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}]}' ``` ## Bring your own OpenAI key Want requests billed to your own OpenAI account instead of through credits? Store your OpenAI key once—the gateway attaches it for you on every request. Follow the [quickstart](/docs/ai-gateway/quickstart) to create an [access key](/docs/ai-gateway/concepts/access-keys) in [app.ngrok.ai](https://app.ngrok.ai) if you don't have one yet. [Add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) for OpenAI. ngrok stores it encrypted, so it never has to live in your application or get shared around—and you can reuse it across as many access keys as you need. [Create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with an OpenAI routing rule set to **Bring your own API key**, then assign it to your access key. Your code doesn't change. Send the same access key as before—the gateway swaps in your OpenAI key before the request leaves for OpenAI. ## Explicit provider routing ```python theme={null} response = client.chat.completions.create( model="openai:gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Available models Browse the [model catalog](/docs/ai-gateway/reference/model-catalog#openai) for the full list of OpenAI models you can call through the gateway. ## Files and decisions OpenAI also supports the [Files API](/docs/ai-gateway/guides/files) for uploading documents and images once, and the [Decisions API](/docs/ai-gateway/guides/decisions-and-system-one) for typed answers to questions about your input. ## Next steps * [Access Keys](/docs/ai-gateway/concepts/access-keys) * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations) * [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys) # OpenRouter Source: https://ngrok.com/docs/ai-gateway/providers/openrouter Use OpenRouter models through the ngrok AI Gateway. [OpenRouter](https://openrouter.ai) is a unified API that provides access to hundreds of models from providers like Anthropic, Google, Meta, Mistral, and more through a single endpoint. Even though OpenRouter is a **built-in provider**, it still requires you to **bring your own key** because you can't pay for it with credits at this time. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key in [app.ngrok.ai](https://app.ngrok.ai). Create an account at [openrouter.ai](https://openrouter.ai) and generate an API key from your dashboard. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key). [Create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with an `openrouter` routing rule and allow OpenRouter in the access scope. Assign the configuration to your access key and send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="openrouter:anthropic/claude-3.5-sonnet", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Model names Always prefix OpenRouter model IDs with `openrouter:`. Without the prefix, the gateway may route to the native provider instead of OpenRouter. OpenRouter uses a `provider/model` format for its model IDs. When calling through the ngrok AI Gateway, prefix with `openrouter:`: | What you want | Model string to use | | - | - | | Anthropic Claude via OpenRouter | `openrouter:anthropic/claude-3.5-sonnet` | | Google Gemini via OpenRouter | `openrouter:google/gemini-2.0-flash-001` | | Meta Llama via OpenRouter | `openrouter:meta-llama/llama-3.1-8b-instruct` | | Mistral via OpenRouter | `openrouter:mistralai/mistral-7b-instruct` | The `openrouter:` prefix tells the gateway to route to OpenRouter specifically. Without it, a slash-separated ID such as `anthropic/claude-sonnet-4-6` resolves to the native catalog model, and the gateway sends it to the native provider first. For the full list of models OpenRouter supports, see [openrouter.ai/models](https://openrouter.ai/models). ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#openrouter). ## Next steps * [Provider Keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access Key Configurations](/docs/ai-gateway/guides/access-key-configurations) * [Multi-provider failover](/docs/ai-gateway/examples/multi-provider-failover): failover patterns * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): model IDs and provider prefixes # QwenCloud Source: https://ngrok.com/docs/ai-gateway/providers/qwencloud Use Qwen and other open models from QwenCloud through the ngrok AI Gateway. [QwenCloud](https://qwencloud.com) serves the Qwen family of models, plus open models from DeepSeek, Moonshot AI, and Z.ai. QwenCloud supports Chat Completions, Responses, and Anthropic Messages requests. QwenCloud is a **built-in provider** you can pay for with credits, so a provider key is optional. Add your own QwenCloud key to bill usage to your QwenCloud account instead of your ngrok.ai credits. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Skip this step to pay with credits. To bring your own key, create an API key in your [QwenCloud](https://qwencloud.com) account. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) and [create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `qwencloud` routing rule that uses it. Send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="qwencloud:qwen/qwen3.8-max", messages=[{"role": "user", "content": "Hello!"}] ) ``` For a model that other providers also serve, such as `moonshotai/kimi-k3`, add the `qwencloud:` prefix to keep the request on QwenCloud. ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#qwencloud). ## Next steps * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # Tencent TokenHub Source: https://ngrok.com/docs/ai-gateway/providers/tokenhub Use Hunyuan, GLM, Kimi, DeepSeek, and MiniMax models from Tencent TokenHub through the ngrok AI Gateway. [Tencent TokenHub](https://www.tencentcloud.com/document/product/1300) serves models from several labs under one API key, including Tencent Hunyuan, Z.ai GLM, Moonshot AI Kimi, DeepSeek, MiniMax, Xiaomi MiMo, and StepFun. Tencent TokenHub supports Chat Completions, Responses, and Anthropic Messages requests. Tencent TokenHub is a **built-in provider** you can pay for with credits, so a provider key is optional. Add your own TokenHub key to bill usage to your Tencent Cloud account instead of your ngrok.ai credits. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Skip this step to pay with credits. To bring your own key, create a TokenHub API key in your [Tencent Cloud](https://www.tencentcloud.com/document/product/1300) account. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) and [create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `tokenhub` routing rule that uses it. Send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="tokenhub:tencent/hy3", messages=[{"role": "user", "content": "Hello!"}] ) ``` Many TokenHub models are also served by their own labs. Add the `tokenhub:` prefix, as in `tokenhub:moonshotai/kimi-k3`, to keep the request on TokenHub. ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#tencent-tokenhub). ## Next steps * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # Typesafe AI Source: https://ngrok.com/docs/ai-gateway/providers/typesafeai Use Typesafe AI System One models through the ngrok AI Gateway. [Typesafe AI](https://typesafe.ai) serves System One models. Instead of a chat conversation, a System One request sends a `state` and a set of typed `questions`. The response returns a typed answer for each question, with a calibrated probability. Typesafe AI only supports System One requests at `https://gateway.ngrok.ai/v1/systemone`. Chat Completions, Responses, and Messages requests don't reach Typesafe AI models. Typesafe AI is a **built-in provider** you can pay for with credits, so a provider key is optional. Add your own Typesafe AI key to bill usage to your Typesafe AI account instead of your ngrok.ai credits. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Skip this step to pay with credits. To bring your own key, create an API key in your [Typesafe AI](https://typesafe.ai) account. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) and [create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `typesafeai` routing rule that uses it. Send a `POST` request to `/v1/systemone` with your access key: ```bash theme={null} curl https://gateway.ngrok.ai/v1/systemone \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ng-xxxxx-g1-xxxxx" \ -d '{ "model": "jev-latest", "state": "Customer: My order arrived broken and I want a refund today.", "questions": { "urgency": { "type": "score", "instructions": "How urgent is this message?", "criteria": [ "Not urgent: no action needed.", "Somewhat urgent: handle within a few days.", "Urgent: handle today." ] } } }' ``` ## Request format A System One request body has three fields: | Field | Description | | - | - | | `model` | The model ID, such as `jev-latest`. | | `state` | The text the model evaluates. | | `questions` | An object that maps each question ID to a typed question. It must contain at least one question. | The gateway removes any other top-level field before it forwards the request. System One doesn't stream, so a request with `"stream": true` returns an error. See the [Typesafe AI API reference](https://docs.typesafe.ai) for question types and the response format. ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#typesafe-ai). `jev-latest` points at the current Jev build, and `jev-preview` points at the next one. ## Next steps * [Typed-question APIs](/docs/ai-gateway/guides/decisions-and-system-one): How System One requests work * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # xAI Source: https://ngrok.com/docs/ai-gateway/providers/xai Use xAI Grok models through the ngrok AI Gateway. [xAI](https://x.ai) builds the Grok family of models. xAI supports Chat Completions and Responses requests. Responses requests to xAI aren't stored, so `previous_response_id` isn't available. Send the full conversation in each request. xAI is a **built-in provider** you can pay for with credits, so a provider key is optional. Add your own xAI key to bill usage to your xAI account instead of your ngrok.ai credits. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Skip this step to pay with credits. To bring your own key, create an API key in your [xAI](https://x.ai) account. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) and [create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with an `xai` routing rule that uses it. Send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="grok-4.7", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#xai). ## Next steps * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # Z.ai Source: https://ngrok.com/docs/ai-gateway/providers/z-ai Use Z.ai GLM models through the ngrok AI Gateway. [Z.ai](https://z.ai/model-api), formerly Zhipu AI, builds the open-weight GLM family of models for agents, reasoning, and coding. Z.ai supports Chat Completions requests. Z.ai is a **built-in provider** you can pay for with credits, so a provider key is optional. Add your own Z.ai key to bill usage to your Z.ai account instead of your ngrok.ai credits. ## Setup Follow the [quickstart](/docs/ai-gateway/quickstart) to create an access key and purchase [credits](/docs/ai-gateway/concepts/credits) in [app.ngrok.ai](https://app.ngrok.ai). Skip this step to pay with credits. To bring your own key, create an API key in your [Z.ai](https://z.ai/model-api) account. Then [add a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key) and [create an access key configuration](/docs/ai-gateway/guides/access-key-configurations#create-a-configuration) with a `z-ai` routing rule that uses it. Send requests with your access key: ```python theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", ) response = client.chat.completions.create( model="z-ai:glm-5.3", messages=[{"role": "user", "content": "Hello!"}] ) ``` QwenCloud, Tencent TokenHub, and Mistral AI also serve GLM models, so the `z-ai:` prefix keeps the request on Z.ai. ## Available models See the [model catalog](/docs/ai-gateway/reference/model-catalog#z-ai). ## Next steps * [Provider keys](/docs/ai-gateway/guides/attaching-provider-keys) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # Quickstart Source: https://ngrok.com/docs/ai-gateway/quickstart Integrate ngrok.ai with your application in minutes. The onboarding should have you covered, but this short quickstart guide is here just in case. ## Making your first request Open [app.ngrok.ai](https://app.ngrok.ai) and sign in with your ngrok account. [Top-up at least \$5.00](https://app.ngrok.ai/billing). Every request you make requires [ngrok.ai credits](/docs/ai-gateway/concepts/credits). Open [Access Keys](https://app.ngrok.ai/keys) and create a new key. Copy the key right away—it's shown only once—and store it somewhere safe. It looks like `ng-xxxxx-g1-xxxxx`. Point your OpenAI SDK at the gateway using your access key: ```js highlight={4,5} theme={null} import OpenAI from "openai"; const openai = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx" }); const res = await openai.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello, world!" }] }); ``` No need to sign up to OpenAI or bring your own keys for this request. ngrok.ai will handle the billing for you. ## Already have credits and an access key? Complete the ngrok AI Gateway SDK integration (I've already signed up on app.ngrok.ai, purchased credits, and have an access key): 1. Ask me for my access key (format: ng-xxxxx-g1-xxxxx). The gateway URL is always [https://gateway.ngrok.ai](https://gateway.ngrok.ai) 2. Install the OpenAI SDK: `npm install openai` or `pip install openai` 3. Write a script that routes OpenAI SDK requests through the gateway by setting baseURL to [https://gateway.ngrok.ai/v1](https://gateway.ngrok.ai/v1) and apiKey to my ng-xxxxx key 4. Send a test request with model `gpt-4o` and message "State your exact model name and provider." 5. Print and verify the response Ask me for my access key before writing any code. ## Next steps * [Access keys](/docs/ai-gateway/concepts/access-keys): Understand how keys work and manage them * [Credits](/docs/ai-gateway/concepts/credits): How usage is billed * [SDK Integration](/docs/ai-gateway/sdks): Connect with OpenAI, Anthropic, and other SDKs * [Bring Your Own Keys](/docs/ai-gateway/concepts/bring-your-own-keys): Use your own provider API keys for more providers * [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself): Connect Ollama, vLLM, LM Studio, or another private model. # Error Codes Reference Source: https://ngrok.com/docs/ai-gateway/reference/error-codes When the AI Gateway encounters an error, it returns an ngrok error code and message to help identify the issue. The full list of AI Gateway error codes and their meanings are listed on this page. These errors apply to requests sent to `https://gateway.ngrok.ai` with an [access key](/docs/ai-gateway/concepts/access-keys). Every response carries the request's ID in `Ngrok-AIG-Request-Id`, so quote that value when you contact support. See [Response headers](/docs/ai-gateway/how-it-works#response-headers). ## Request and routing errors The gateway rejects these before it makes any upstream attempt, so the response carries no `Ngrok-AIG-Attempts` header and the request is not billed. ### ERR\_NGROK\_27228 **Message** Invalid request: `REASON` **HTTP Status** 400 Bad Request **Causes** * The request body is not valid JSON * A required field is missing for the API surface you called * A field holds a value the surface does not accept **Solutions** * Check the reason in the message for the offending field * Verify the body matches the API surface you posted to, whether Chat Completions, Responses, Messages, or the native Gemini surface *** ### ERR\_NGROK\_27224 **Message** Unknown model "`MODELID`".`SUGGESTION` **HTTP Status** 400 Bad Request **Causes** * The model ID is misspelled * The model is not in the [model catalog](/docs/ai-gateway/reference/model-catalog) and no custom provider defines it **Solutions** * Check the suggestion in the message, which names the closest catalog match * Look the model up in the [model catalog](/docs/ai-gateway/reference/model-catalog) * Qualify the model with its provider, such as `openai:gpt-4o`. See [Choose a model](/docs/ai-gateway/guides/model-selection-strategies) *** ### ERR\_NGROK\_27225 **Message** Unknown provider "`PROVIDERID`". **HTTP Status** 400 Bad Request **Causes** * The provider prefix is misspelled * The provider is a custom provider that does not exist on your account **Solutions** * List your providers with the [Providers API](/docs/ai-gateway/api-reference/providers/list) * [Register the custom provider](/docs/ai-gateway/concepts/custom-providers) before routing to it *** ### ERR\_NGROK\_27230 **Message** "`MODEL`" is not a known model, and "`PROVIDER`" is not a recognized provider. **HTTP Status** 400 Bad Request **Causes** * A provider-qualified model name whose prefix is neither a known provider nor part of a real model ID **Solutions** * Use `provider:model`, with the provider exactly as the [Providers API](/docs/ai-gateway/api-reference/providers/list) reports it * Drop the prefix to let the gateway resolve the model from the catalog *** ### ERR\_NGROK\_27229 **Message** Invalid model address "`ADDRESS`". **HTTP Status** 400 Bad Request **Causes** * The model field is empty, or holds more separators than `provider:model` allows **Solutions** * Send either a bare model ID or a single `provider:model` pair *** ### ERR\_NGROK\_27204 **Message** Unsupported API surface "`PATH`". **HTTP Status** 400 Bad Request **Causes** * The request path is not one the gateway serves **Solutions** * Post to `/v1/chat/completions`, `/v1/responses`, `/v1/messages`, `/v1/decisions`, `/v1/systemone`, `/v1/files`, the native Gemini path `/v1beta/models/{model}:generateContent`, or `/v1beta/interactions` * The gateway does not serve `/v1/embeddings`, `/v1/completions`, or image and audio endpoints * Point your SDK's base URL at `https://gateway.ngrok.ai/v1` rather than a full endpoint path *** ### ERR\_NGROK\_27226 **Message** Provider "`PROVIDERID`" does not support API surface "`SURFACE`". **HTTP Status** 400 Bad Request **Causes** * The provider has no equivalent of the surface you called, such as the Responses API on a provider that only serves Chat Completions **Solutions** * Call the surface the provider supports. See the provider's page under [Getting Started With](/docs/ai-gateway/providers/openai) * Choose a different provider for that surface *** ### ERR\_NGROK\_27205 **Message** API surface "`SURFACE`" is not implemented. **HTTP Status** 501 Not Implemented **Causes** * The gateway knows this endpoint but doesn't support it yet **Solutions** * Use a supported endpoint such as `/v1/chat/completions`. See [ERR\_NGROK\_27204](#err_ngrok_27204) for the full list *** ### ERR\_NGROK\_27250 **Message** ngrok doesn't store interactions yet, so "store" can't be true. Omit it or set it to false to send a stateless request. **HTTP Status** 400 Bad Request **Causes** * An Interactions API request set `store` to `true` **Solutions** * Omit `store`, or set it to `false`. See [Google SDK](/docs/ai-gateway/sdks/google) *** ## Access policy errors ### ERR\_NGROK\_27231 **Message** Provider "`PROVIDERID`" is not allowed by the access policy. **HTTP Status** 403 Forbidden **Causes** * The access key's configuration does not include the provider in its access scope **Solutions** * Allow the provider in the key's [access key configuration](/docs/ai-gateway/guides/access-key-configurations) * See [Restrict providers and models](/docs/ai-gateway/guides/restrict-providers-and-models) for how scope is evaluated *** ### ERR\_NGROK\_27232 **Message** Model "`MODELID`" is not allowed by the access policy. **HTTP Status** 403 Forbidden **Causes** * The configuration's access scope excludes the model, even though the provider is allowed **Solutions** * Add the model to the access scope in the key's [access key configuration](/docs/ai-gateway/guides/access-key-configurations) *** ## Credential and credit errors ### ERR\_NGROK\_27201 **Message** Authentication failed: `REASON` **HTTP Status** 401 Unauthorized **Causes** * No access key in the request * The access key is malformed, revoked, or deleted **Solutions** * Send your [access key](/docs/ai-gateway/concepts/access-keys) as `Authorization: Bearer ng-xxxxx-g1-xxxxx`, or as `x-api-key` on the Anthropic surface and `X-Goog-Api-Key` on the native Gemini surface * Confirm the key still exists with the [Access Keys API](/docs/ai-gateway/api-reference/access-keys/list) *** ### ERR\_NGROK\_27202 **Message** No keys available for provider "`PROVIDERID`". **HTTP Status** 502 Bad Gateway **Causes** * The access key's configuration has no routing credentials for the requested provider * Every provider key the configuration referenced was deleted **Solutions** * Add a routing rule for the provider in the key's [access key configuration](/docs/ai-gateway/guides/access-key-configurations) * [Attach a provider key](/docs/ai-gateway/guides/attaching-provider-keys#add-a-provider-key), or [pay with credits](/docs/ai-gateway/guides/pay-with-credits) where the provider supports it *** ### ERR\_NGROK\_27206 **Message** Credit check failed: `REASON` **HTTP Status** 402 Payment Required **Causes** * The account's credit balance is zero or below, or billing is not set up for the account yet * Every request needs credits, even with your own provider keys, because each one pays a small processing fee **Solutions** * Check your balance with the [Credits API](/docs/ai-gateway/api-reference/credits/balance) * [Purchase credits](https://app.ngrok.ai/billing). Your own provider key does not bypass this check *** ### ERR\_NGROK\_27233 **Message** Model "`MODELID`" is not available with an ngrok-managed key for provider "`PROVIDERID`": add your own provider key to use uncataloged models. **HTTP Status** 400 Bad Request **Causes** * The model isn't in the ngrok.ai catalog, so ngrok.ai has no price for it **Solutions** * [Attach your own provider key](/docs/ai-gateway/guides/attaching-provider-keys) for that provider * Pick a catalog model instead. See the [model catalog](/docs/ai-gateway/reference/model-catalog) *** ### ERR\_NGROK\_27239 **Message** ngrok can't serve "`MODELID`" on provider "`PROVIDERID`" with an ngrok-managed key right now. Add your own provider key to use it. **HTTP Status** 503 Service Unavailable **Causes** * You can't pay for this model with credits on that provider right now **Solutions** * Retry, or fail over to another model. See [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models) * [Attach your own provider key](/docs/ai-gateway/guides/attaching-provider-keys) for that provider *** ### ERR\_NGROK\_27236 **Message** Model "`MODELID`" is not priced for managed billing yet for provider "`PROVIDERID`"; add your own provider key to use it. **HTTP Status** 400 Bad Request **Causes** * You can't pay for this model with credits on that provider yet, because ngrok.ai doesn't have a price for it **Solutions** * [Attach your own provider key](/docs/ai-gateway/guides/attaching-provider-keys) for that provider * Pick another model from the [model catalog](/docs/ai-gateway/reference/model-catalog) *** ### ERR\_NGROK\_27238 **Message** An ngrok-managed key can't serve this request, because ngrok can't bill `FEATURE` for "`MODELID`" on provider "`PROVIDERID`". Add your own provider key to use it. **HTTP Status** 400 Bad Request **Causes** * The request uses a feature, such as a hosted tool, that you can't pay for with credits on that model **Solutions** * Remove the feature from the request * [Attach your own provider key](/docs/ai-gateway/guides/attaching-provider-keys) for that provider *** ### ERR\_NGROK\_27249 **Message** ngrok doesn't offer "`MODELID`" on provider "`PROVIDERID`" with an ngrok-managed key. Add your own provider key to use it. **HTTP Status** 400 Bad Request **Causes** * The model exists in the catalog, but you can't pay for it with credits on that provider **Solutions** * [Attach your own provider key](/docs/ai-gateway/guides/attaching-provider-keys) for that provider * Pick a model you can pay for with credits. See the [model catalog](/docs/ai-gateway/reference/model-catalog#billing-and-provider-credentials) *** ### ERR\_NGROK\_27240 **Message** Your account has no `FILES` for provider "`PROVIDERID`". An ngrok-managed key can only use files your account uploaded. Upload the file first, or add your own provider key. **HTTP Status** 404 Not Found **Causes** * The request uses a file that wasn't uploaded from your account, so you can't pay for it with credits **Solutions** * [Attach your own provider key](/docs/ai-gateway/guides/attaching-provider-keys) to use files stored in your provider account *** ## Upstream failures These arrive after the gateway tries at least one provider, so `Ngrok-AIG-Attempts` tells you how many tries the gateway made. See [Handle gateway errors](/docs/ai-gateway/guides/error-handling) for the order it tries them in. When every model fails, or the request hits the 10-attempt cap, the gateway returns the last model's error. ### ERR\_NGROK\_27211 **Message** Upstream provider rejected the API key. **HTTP Status** 401 Unauthorized **Causes** * The provider key stored in ngrok is invalid, revoked, or rotated at the provider **Solutions** * [Rotate the provider key](/docs/ai-gateway/api-reference/provider-keys/rotate) to the current value * Verify the key directly against the provider *** ### ERR\_NGROK\_27217 **Message** Upstream provider rate limit exceeded. **HTTP Status** 429 Too Many Requests **Causes** * Every credential and candidate the gateway tried was rate limited **Solutions** * Attach more [provider keys](/docs/ai-gateway/guides/attaching-provider-keys) so the gateway has another credential to try * Add candidates from other providers. See [Multi-provider failover](/docs/ai-gateway/examples/multi-provider-failover) * Slow your request rate, or raise your quota with the provider *** ### ERR\_NGROK\_27207 **Message** Upstream request timed out. **HTTP Status** 504 Gateway Timeout **Causes** * A single upstream attempt exceeded the per-request timeout **Solutions** * Raise the per-request timeout in [Account Settings](/docs/ai-gateway/guides/account-settings) * Shorten the prompt, or choose a faster model *** ### ERR\_NGROK\_3810 **Message** Request timed out after `TIMEOUT`. Try a shorter prompt or increase your timeout. **HTTP Status** 504 Gateway Timeout **Causes** * Every attempt together exceeded the total timeout * Too many slow candidates in the failover list **Solutions** * Raise the total timeout in [Account Settings](/docs/ai-gateway/guides/account-settings) * Lower the per-request timeout so a slow provider fails sooner and leaves budget for the next candidate * Reduce the number of failover candidates *** ### ERR\_NGROK\_27227 **Message** No backend available: `REASON` **HTTP Status** 502 Bad Gateway **Causes** * None of the models in the request could be used, for example because the access key can't reach them * With `ngrok/auto`, none of the models the key can reach are available for automatic selection. See [Automatic model selection](/docs/ai-gateway/guides/automatic-model-selection) **Solutions** * Confirm the configuration's routing rules cover the provider you requested * See [Handle gateway errors](/docs/ai-gateway/guides/error-handling) for the order the gateway tries models in *** ### ERR\_NGROK\_27237 **Message** Request rejected: a routing loop was detected. Double check your custom provider URLs do not cause an infinite proxy loop. **HTTP Status** 502 Bad Gateway **Causes** * A custom provider's base URL points back at the AI Gateway **Solutions** * Point the custom provider at its real upstream. See [Use a model you run yourself](/docs/ai-gateway/guides/use-a-model-you-run-yourself) *** ## Limit errors ### ERR\_NGROK\_3811 **Message** Input too large: `INPUTTOKENS` tokens (max `MAXALLOWED`). Shorten your prompt. **HTTP Status** 413 Payload Too Large **Causes** * The prompt and context exceed the account's max input tokens * Very long conversation history, or large embedded content in messages **Solutions** * Reduce the prompt length or conversation history * Raise or clear `maxInputTokens` in [Account Settings](/docs/ai-gateway/guides/account-settings) * Count tokens client-side before sending *** ### ERR\_NGROK\_AI\_GATEWAY\_REQUEST\_BODY\_TOO\_LONG **Message** Request body is too long. Max 32 MiB. **HTTP Status** 413 Payload Too Large **Causes** * The request body is larger than 32 MiB **Solutions** * Upload large documents and images with the [Files API](/docs/ai-gateway/guides/files) instead of sending them inline * Reduce the conversation history or embedded content *** ## Retired setup ### ERR\_NGROK\_3820 **Message** ```text theme={null} AI Gateway has a new home at https://app.ngrok.ai. The old ai-gateway traffic policy action has been retired. Existing credits have been migrated to https://app.ngrok.ai. If you have any questions or issues, please reach out to support@ngrok.com. ``` **HTTP Status** 403 Forbidden **Causes** * A Traffic Policy still contains the retired `ai-gateway` action **Solutions** * Remove the `ai-gateway` action from the endpoint's Traffic Policy * Send LLM traffic to `https://gateway.ngrok.ai` with an [access key](/docs/ai-gateway/concepts/access-keys) instead, and manage the gateway at [app.ngrok.ai](https://app.ngrok.ai) * Credits from the retired setup were migrated automatically. Email [support@ngrok.com](mailto:support@ngrok.com) if your balance looks wrong *** ## Other codes The gateway returns one code per upstream status class, beyond the ones above. Each has a page under [ngrok error codes](/docs/errors): | Code | Meaning | | - | - | | `ERR_NGROK_27210` | Upstream provider rejected the request | | `ERR_NGROK_27212` | Upstream provider denied access | | `ERR_NGROK_27213` | Upstream provider returned not found | | `ERR_NGROK_27214` | Upstream provider request timed out | | `ERR_NGROK_27215` | Request payload exceeded upstream provider limits | | `ERR_NGROK_27216` | Upstream provider could not process the request | | `ERR_NGROK_27218` | Upstream provider returned an internal server error | | `ERR_NGROK_27219` | Upstream provider returned a bad gateway error | | `ERR_NGROK_27220` | Upstream provider is unavailable | | `ERR_NGROK_27221` | Upstream provider gateway timed out | | `ERR_NGROK_27222` | Upstream provider returned an unexpected status | | `ERR_NGROK_27234` | Upstream returned a non-streaming response to a streaming request | | `ERR_NGROK_27235` | Upstream provider failed to respond, for example a dropped connection | | `ERR_NGROK_27241` to `ERR_NGROK_27248` | Files API errors. See [Upload and use files](/docs/ai-gateway/guides/files#errors) | ## SDK error handling Handle ngrok errors in your SDK: ```python OpenAI Python SDK theme={null} from openai import OpenAI, APIError client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" ) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello"}] ) except APIError as e: print(f"Error code: {e.code}") print(f"Message: {e.message}") if "ERR_NGROK_27224" in str(e): print("Model not found - check model name spelling") ``` ```javascript OpenAI Node.js SDK theme={null} import OpenAI from 'openai'; const client = new OpenAI({ baseURL: 'https://gateway.ngrok.ai/v1', apiKey: 'ng-xxxxx-g1-xxxxx', }); try { const response = await client.chat.completions.create({ model: 'gpt-4o', messages: [{ role: 'user', content: 'Hello' }], }); } catch (error) { if (error instanceof OpenAI.APIError) { console.log('Status:', error.status); console.log('Message:', error.message); if (error.message.includes('ERR_NGROK_27224')) { console.log('Model not found - check model name spelling'); } } } ``` ## Next steps Diagnose a failure from the response Which failures the gateway retries, and in what order # Model Catalog Source: https://ngrok.com/docs/ai-gateway/reference/model-catalog Built-in providers and models available on ngrok.ai. ngrok.ai maintains a catalog of known providers and models. This catalog enables automatic model resolution, routing, model validation, and rich metadata for selection strategies. ## How the catalog works When you send a request with a model name like `gpt-4o`, the gateway automatically: 1. Resolves the model using the catalog 2. Finds every provider that serves the model, with the model author's own provider (for example, OpenAI) first, and applies the right provider credentials 3. Routes the request to the provider’s endpoint If you want to explicitly select a provider, use the `provider:model` format in the model string. For example: ```json theme={null} { "model": "openai:gpt-4o", "messages": [{"role": "user", "content": "Hello"}] } ``` ## Built-in providers The catalog includes built-in providers—public AI APIs with known models and routing metadata. You can manage them on the [Providers page](https://app.ngrok.ai/providers). ### Authentication Send traffic using an [access key](/docs/ai-gateway/concepts/access-keys). This key authenticates your requests to the gateway, not to the upstream provider. ### Billing and provider credentials The AI Gateway splits billing into two parts: using the gateway and paying the model provider. Many built-in models don’t need a provider key. You can spot them in the tables below: if **Needs BYOK?** says “No,” you can use the model right away as long as you have credits. The gateway takes care of authentication and charges your credits. Availability is per model, so a provider can have some models you can pay for with credits and some you can't. Other providers do need a key. For those, add your provider key in app.ngrok.ai. The provider will bill you directly for model usage, and your credits will cover the gateway fee. This is often called BYOK (bring your own key). ### OpenAI | Field | Value | | - | - | | **Provider ID** | `openai` | | **Base URL** | `https://api.openai.com/v1/` | | **Website** | [openai.com](https://openai.com/) | | **API surfaces** | Responses, Chat Completions, Decisions | | **Provider key required** | No | [How to use OpenAI →](/docs/ai-gateway/providers/openai) #### OpenAI models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `gpt-6.1-sol` | 1M | 128K | Text Image | No | | `gpt-6-luna` | 1M | 128K | Text Image | No | | `gpt-6-sol` | 1M | 128K | Text Image | No | | `gpt-6-astra` | 1M | 128K | Text Image | No | | `gpt-5.6-luna` | 1M | 128K | Text Image | No | | `gpt-5.6-sol` | 1M | 128K | Text Image | No | | `gpt-5.6-terra` | 1M | 128K | Text Image | No | | `gpt-5.5` | 1M | 128K | Text Image | No | | `gpt-5.5-pro` | 1M | 128K | Text Image | No | | `gpt-5.4-mini` | 400K | 128K | Text Image | No | | `gpt-5.4-nano` | 400K | 128K | Text Image | No | | `gpt-5.4` | 1M | 128K | Text Image | No | | `gpt-5.4-pro` | 1M | 128K | Text Image | No | | `gpt-5.3-codex` | 400K | 128K | Text Image | No | | `gpt-5.2-codex` | 400K | 128K | Text Image | No | | `gpt-5.2` | 400K | 128K | Text Image | No | | `gpt-5.2-chat-latest` | 128K | 16K | Text Image | No | | `gpt-5.2-pro` | 400K | 128K | Text Image | No | | `gpt-5.1-chat-latest` | 128K | 16K | Text Image | No | | `gpt-5.1` | 400K | 128K | Text Image | No | | `gpt-5-pro` | 400K | 272K | Text Image | No | | `gpt-5` | 400K | 128K | Text Image | No | | `gpt-5-chat-latest` | 128K | 16K | Text Image | No | | `gpt-5-mini` | 400K | 128K | Text Image | No | | `gpt-5-nano` | 400K | 128K | Text Image | No | | `o3-deep-research` | 200K | 100K | Text Image | No | | `o4-mini-deep-research` | 200K | 100K | Text Image | No | | `o3-pro` | 200K | 100K | Text Image | No | | `o3` | 200K | 100K | Text Image | No | | `o4-mini` | 200K | 100K | Text Image | No | | `gpt-4.1` | 1M | 32K | Text Image | No | | `gpt-4.1-mini` | 1M | 32K | Text Image | No | | `gpt-4.1-nano` | 1M | 32K | Text Image | No | | `o1-pro` | 200K | 100K | Text Image | No | | `o3-mini` | 200K | 100K | Text | No | | `o1` | 200K | 100K | Text Image | No | | `gpt-4o-mini` | 128K | 16K | Text Image | No | | `gpt-4o` | 128K | 16K | Text Image | No | | `gpt-4-turbo` | 128K | 4K | Text Image | No | | `gpt-4` | 8K | 8K | Text | No | | `gpt-3.5-turbo` | 16K | 4K | Text | No | *** ### Anthropic | Field | Value | | - | - | | **Provider ID** | `anthropic` | | **Base URL** | `https://api.anthropic.com/v1/` | | **Website** | [anthropic.com](https://anthropic.com/) | | **API surfaces** | Chat Completions, Messages | | **Provider key required** | No | [How to use Anthropic →](/docs/ai-gateway/providers/anthropic) #### Anthropic models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `claude-haiku-5-5` | 1M | 128K | Text Image File | No | | `claude-sonnet-5-5` | 1M | 128K | Text Image File | No | | `claude-opus-5-5` | 1M | 128K | Text Image File | No | | `claude-fable-5-1` | 1M | 128K | Text Image File | No | | `claude-opus-5` | 1M | 128K | Text Image File | No | | `claude-sonnet-5` | 1M | 128K | Text Image File | No | | `claude-fable-5` | 1M | 128K | Text Image File | No | | `claude-opus-4-8` | 1M | 128K | Text Image File | No | | `claude-opus-4-7` | 1M | 128K | Text Image File | No | | `claude-sonnet-4-6` | 1M | 128K | Text Image File | No | | `claude-opus-4-6` | 1M | 128K | Text Image File | No | | `claude-opus-4-5` | 200K | 64K | Text Image File | No | | `claude-haiku-4-5` | 200K | 64K | Text Image File | No | | `claude-sonnet-4-5` | 200K | 64K | Text Image File | No | | `claude-opus-4-1` | 200K | 32K | Text Image File | No | *** ### Google | Field | Value | | - | - | | **Provider ID** | `google` | | **Base URL** | `https://generativelanguage.googleapis.com/v1beta/openai/` | | **Website** | [aistudio.google.com](https://aistudio.google.com/) | | **API surfaces** | Chat Completions, Gemini GenerateContent, Interactions | | **Provider key required** | For some models. See **Needs BYOK?** | [How to use Google →](/docs/ai-gateway/providers/google) #### Google models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `gemini-3.8-flash` | 1M | 64K | Text Image Audio Video File | No | | `gemini-3.7-flash` | 1M | 64K | Text Image Audio Video File | No | | `gemini-3.5-flash-lite` | 1M | 64K | Text Image Audio Video File | No | | `gemini-3.6-flash` | 1M | 64K | Text Image Audio Video File | No | | `gemini-3.5-flash` | 1M | 64K | Text Image Audio Video File | No | | `gemini-3.1-flash-lite` | 1M | 64K | Text Image Audio Video File | No | | `gemini-3.1-pro-preview` | 1M | 64K | Text Image Audio Video File | No | | `gemini-3.1-pro-preview-customtools` | 1M | 64K | Text Image Audio Video File | No | | `gemini-3-flash-preview` | 1M | 64K | Text Image Audio Video File | No | | `gemini-2.5-flash-lite` | 1M | 64K | Text Image Audio Video File | Yes | | `gemini-2.5-flash` | 1M | 64K | Text Image Audio Video File | Yes | | `gemini-2.5-pro` | 1M | 64K | Text Image Audio Video File | Yes | Google serves the Gemini 2.5 models only to Google projects created before mid-2026. Newer projects get a 404 stating the model is no longer available to new users. The Google project ngrok.ai uses postdates that cutoff, so these models are reachable only with your own key on an older project. *** ### DeepSeek | Field | Value | | - | - | | **Provider ID** | `deepseek` | | **Base URL** | `https://api.deepseek.com/v1` | | **Website** | [deepseek.com](https://www.deepseek.com/) | | **API surfaces** | Chat Completions, Responses, Messages | | **Provider key required** | No | [How to use DeepSeek →](/docs/ai-gateway/providers/deepseek) #### DeepSeek models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `deepseek-flash` | 1M | 384K | Text | No | | `deepseek-v4-flash` | 1M | 384K | Text | No | | `deepseek-v4-pro` | 1M | 384K | Text | No | *** ### xAI | Field | Value | | - | - | | **Provider ID** | `xai` | | **Base URL** | `https://api.x.ai/v1` | | **Website** | [x.ai](https://x.ai/) | | **API surfaces** | Chat Completions, Responses | | **Provider key required** | No | [How to use xAI →](/docs/ai-gateway/providers/xai) #### xAI models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `grok-4.7` | 500K | — | Text Image | No | | `grok-4.6` | 500K | — | Text Image | No | | `grok-4.5` | 500K | — | Text Image | No | | `grok-build-0.1` | 256K | — | Text Image | No | | `grok-4.3` | 1M | — | Text Image | No | *** ### Groq | Field | Value | | - | - | | **Provider ID** | `groq` | | **Base URL** | `https://api.groq.com/openai/v1` | | **Website** | [groq.com](https://groq.com/) | | **API surfaces** | Chat Completions, Responses | | **Provider key required** | Yes | [How to use Groq →](/docs/ai-gateway/providers/groq) Groq provides AI inference powered by their custom LPU (Language Processing Unit) hardware. #### Groq models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `openai/gpt-oss-safeguard-20b` | 128K | 64K | Text | Yes | | `openai/gpt-oss-120b` | 128K | 64K | Text | Yes | | `openai/gpt-oss-20b` | 128K | 64K | Text | Yes | | `meta-llama/llama-prompt-guard-2-22m` | 512 | 512 | Text | Yes | | `meta-llama/llama-prompt-guard-2-86m` | 512 | 512 | Text | Yes | | `qwen/qwen3-32b` | 128K | 40K | Text | Yes | | `meta-llama/llama-4-scout-17b-16e-instruct` | 128K | 8K | Text Image | Yes | *** ### Mistral AI | Field | Value | | - | - | | **Provider ID** | `mistral` | | **Base URL** | `https://api.mistral.ai/v1` | | **Website** | [mistral.ai](https://mistral.ai) | | **API surfaces** | Chat Completions | | **Provider key required** | Yes | [How to use Mistral AI →](/docs/ai-gateway/providers/mistral) #### Mistral AI models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `z-ai/glm-5.3` | 1M | 128K | Text | Yes | *** ### Moonshot AI | Field | Value | | - | - | | **Provider ID** | `moonshotai` | | **Base URL** | `https://api.moonshot.ai/v1` | | **Website** | [moonshot.ai](https://www.moonshot.ai/) | | **API surfaces** | Chat Completions | | **Provider key required** | No | [How to use Moonshot AI →](/docs/ai-gateway/providers/moonshotai) #### Moonshot AI models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `kimi-k3` | 1M | 1M | Text Image Video | No | | `kimi-k2.7-code` | 256K | 256K | Text Image Video | No | | `kimi-k2.7-code-highspeed` | 256K | 256K | Text Image Video | No | | `kimi-k2.6` | 256K | 256K | Text Image Video | No | *** ### Z.ai | Field | Value | | - | - | | **Provider ID** | `z-ai` | | **Base URL** | `https://api.z.ai/api/paas/v4` | | **Website** | [z.ai/model-api](https://z.ai/model-api) | | **API surfaces** | Chat Completions | | **Provider key required** | No | [How to use Z.ai →](/docs/ai-gateway/providers/z-ai) #### Z.ai models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `glm-5.3-flash` | 1M | 128K | Text Image Video File | No | | `glm-5.3` | 1M | 128K | Text | No | | `glm-5.2` | 1M | 128K | Text | No | | `glm-5.1` | 200K | 128K | Text | No | | `glm-5-turbo` | 200K | 128K | Text | No | | `glm-5` | 200K | 128K | Text | No | | `glm-4.7-flash` | 200K | 128K | Text | No | | `glm-4.7-flashx` | 200K | 128K | Text | No | | `glm-4.7` | 200K | 128K | Text | No | | `glm-4.6` | 200K | 128K | Text | No | | `glm-4.5` | 128K | 96K | Text | No | | `glm-4.5-air` | 128K | 96K | Text | No | | `glm-4.5-airx` | 128K | 96K | Text | No | | `glm-4.5-flash` | 128K | 96K | Text | No | | `glm-4.5-x` | 128K | 96K | Text | No | | `glm-4-32b-0414-128k` | 128K | 16K | Text | No | | `glm-5.3-flashx` | 1M | 128K | Text Image Video File | No | *** ### QwenCloud | Field | Value | | - | - | | **Provider ID** | `qwencloud` | | **Base URL** | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | | **Website** | [qwencloud.com](https://qwencloud.com/) | | **API surfaces** | Chat Completions, Messages, Responses | | **Provider key required** | No | [How to use QwenCloud →](/docs/ai-gateway/providers/qwencloud) #### QwenCloud models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `deepseek/deepseek-flash` | 1M | 384K | Text | No | | `deepseek/deepseek-v4.1-flash` | 1M | 384K | Text | No | | `qwen/qwen3.8-max` | 1M | 128K | Text Image Video | No | | `qwen/qwen3.8-flash` | 1M | 128K | Text Image Video | No | | `z-ai/glm-5.3` | 1M | 128K | Text | No | | `qwen/qwen3.8-2.4t-a95b` | 1M | 128K | Text | No | | `qwen/qwen3.7-flash` | 1M | 128K | Text Image Video | No | | `moonshotai/kimi-k3` | 1M | 1M | Text Image Video | No | | `z-ai/glm-5.2` | 1M | 128K | Text | No | | `z-ai/glm-5.2-fast-preview` | 1M | 128K | Text | No | | `qwen/qwen3.7-plus` | 1M | 128K | Text Image Video | No | | `qwen/qwen3.7-max` | 1M | 128K | Text | No | | `deepseek/deepseek-v4-pro` | 1M | 384K | Text | No | | `qwen/qwen3.6-plus` | 1M | 64K | Text Image Video | No | | `qwen/qwen3.5-flash` | 1M | 64K | Text Image Video | No | *** ### MiniMax | Field | Value | | - | - | | **Provider ID** | `minimax` | | **Base URL** | `https://api.minimax.io/v1` | | **Website** | [minimax.io](https://www.minimax.io) | | **API surfaces** | Chat Completions, Responses, Messages | | **Provider key required** | No | [How to use MiniMax →](/docs/ai-gateway/providers/minimax) #### MiniMax models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `minimax-m3` | 1M | 512K | Text Image Video | No | | `minimax-m2.7` | 200K | 200K | Text | No | | `minimax-m2.7-highspeed` | 200K | 200K | Text | No | | `minimax-m2.5` | 200K | 200K | Text | No | | `minimax-m2.5-highspeed` | 200K | 200K | Text | No | | `minimax-m2.1` | 200K | 200K | Text | No | | `minimax-m2.1-highspeed` | 200K | 200K | Text | No | | `minimax-m2` | 200K | 200K | Text | No | *** ### Tencent TokenHub | Field | Value | | - | - | | **Provider ID** | `tokenhub` | | **Base URL** | `https://tokenhub-intl.tencentcloudmaas.com/v1` | | **Website** | [tencentcloud.com/document/product/1300](https://www.tencentcloud.com/document/product/1300) | | **API surfaces** | Chat Completions, Responses, Messages | | **Provider key required** | No | [How to use Tencent TokenHub →](/docs/ai-gateway/providers/tokenhub) #### Tencent TokenHub models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `xiaomi/mimo-v2.6-flash` | 1M | 64K | Text | No | | `xiaomi/mimo-v2.6-pro` | 1M | 128K | Text | No | | `stepfun/step-5-preview` | 1M | 64K | Text | No | | `moonshotai/kimi-k2.8-preview` | 1M | — | Text Image Video | No | | `deepseek/deepseek-flash` | 1M | 384K | Text | No | | `tencent/hy4-preview` | 1M | 64K | Text | No | | `z-ai/glm-5.3-flash` | 1M | 128K | Text Image Video File | No | | `z-ai/glm-5.3` | 1M | 128K | Text | No | | `deepseek/deepseek-v4-flash-0731` | 1M | 384K | Text | No | | `deepseek/deepseek-v4-flash-202605` | 1M | 384K | Text | No | | `moonshotai/kimi-k3` | 1M | 1M | Text Image Video | No | | `tencent/hy3` | 256K | 128K | Text | No | | `z-ai/glm-5.2` | 1M | 128K | Text | No | | `moonshotai/kimi-k2.7-code` | 256K | 256K | Text Image Video | No | | `moonshotai/kimi-k2.7-code-highspeed` | 256K | 256K | Text Image Video | No | | `tencent/hy-mt2-lite` | 8K | 4K | Text | No | | `tencent/hy-mt2-plus` | 8K | 4K | Text | No | | `minimax/minimax-m3` | 1M | 512K | Text Image Video | No | | `tencent/hy-mt2-pro` | 8K | 4K | Text | No | | `xiaomi/mimo-v2.5-pro` | 1M | 128K | Text | No | | `deepseek/deepseek-v4-flash` | 1M | 384K | Text | No | | `deepseek/deepseek-v4-pro` | 1M | 384K | Text | No | | `moonshotai/kimi-k2.6` | 256K | 256K | Text Image Video | No | | `minimax/minimax-m2.7` | 200K | 200K | Text | No | | `z-ai/glm-5.3-flashx` | 1M | 128K | Text Image Video File | No | *** ### Typesafe AI | Field | Value | | - | - | | **Provider ID** | `typesafeai` | | **Base URL** | `https://api.typesafe.ai/v1` | | **Website** | [typesafe.ai](https://typesafe.ai) | | **API surfaces** | System One | | **Provider key required** | No | [How to use Typesafe AI →](/docs/ai-gateway/providers/typesafeai) #### Typesafe AI models | Model ID | Context | Max Output | Modalities | Needs BYOK? | | - | - | - | - | - | | `jev-1.13.0` | 64K | — | Text | No | | `jev-latest` | 64K | — | Text | No | | `jev-preview` | 64K | — | Text | No | *** ### OpenRouter | Field | Value | | - | - | | **Provider ID** | `openrouter` | | **Base URL** | `https://openrouter.ai/api/v1` | | **Website** | [openrouter.ai](https://openrouter.ai/) | | **API surfaces** | Responses, Chat Completions, Messages | | **Provider key required** | Yes | [How to use OpenRouter →](/docs/ai-gateway/providers/openrouter) OpenRouter is a unified API that provides access to multiple AI models from various providers through a single endpoint. The catalog lists no models for OpenRouter. Send any model ID the provider serves with the `openrouter:` prefix, and the gateway passes it through. *** ### Hyperbolic | Field | Value | | - | - | | **Provider ID** | `hyperbolic` | | **Base URL** | `https://api.hyperbolic.xyz/v1` | | **Website** | [hyperbolic.ai](https://www.hyperbolic.ai/) | | **API surfaces** | Chat Completions | | **Provider key required** | Yes | [How to use Hyperbolic →](/docs/ai-gateway/providers/hyperbolic) Hyperbolic provides high-performance inference for open-source models. The catalog lists no models for Hyperbolic. Send any model ID the provider serves with the `hyperbolic:` prefix, and the gateway passes it through. *** ### InceptionLabs | Field | Value | | - | - | | **Provider ID** | `inceptionlabs` | | **Base URL** | `https://api.inceptionlabs.ai/v1` | | **Website** | [inceptionlabs.ai](https://www.inceptionlabs.ai/) | | **API surfaces** | Chat Completions | | **Provider key required** | Yes | [How to use InceptionLabs →](/docs/ai-gateway/providers/inceptionlabs) InceptionLabs develops diffusion-based language models for fast, efficient text generation. The catalog lists no models for InceptionLabs. Send any model ID the provider serves with the `inceptionlabs:` prefix, and the gateway passes it through. *** ### Inference.net | Field | Value | | - | - | | **Provider ID** | `inference.net` | | **Base URL** | `https://api.inference.net/v1` | | **Website** | [inference.net](https://inference.net/) | | **API surfaces** | Chat Completions | | **Provider key required** | Yes | [How to use Inference.net →](/docs/ai-gateway/providers/inference-net) Inference.net provides a distributed inference network for running AI models at scale. The catalog lists no models for Inference.net. Send any model ID the provider serves with the `inference.net:` prefix, and the gateway passes it through. *** ## Using models from the catalog ### Simple model reference Reference models directly by their ID. A bare ID such as `gpt-4o` resolves to every provider that serves the model. The full catalog ID, such as `openai/gpt-4o`, works too: ```json theme={null} { "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}] } ``` ### Explicit provider Use the `provider:model` format for explicit routing: ```json theme={null} { "model": "openai:gpt-4o", "messages": [{"role": "user", "content": "Hello"}] } ``` ## Custom providers Built-in providers cover major public APIs. For self-hosted models (Ollama, vLLM, LM Studio) or other endpoints, create a **custom provider** in the **Custom** tab in app.ngrok.ai. Custom providers always require a provider key when the upstream needs authentication. See [Custom providers](/docs/ai-gateway/concepts/custom-providers) to add one. ## Catalog updates The model catalog is updated periodically to include new models and providers. To use a model that isn't in the catalog yet, send it with its provider prefix, such as `groq:new-model-id`. The gateway passes it through to that provider. Uncataloged models need your own provider key, because ngrok.ai can't price them, so you can't pay for them with credits. # Anthropic SDK Source: https://ngrok.com/docs/ai-gateway/sdks/anthropic Use the ngrok AI Gateway with Anthropic's official SDKs. **Prerequisite**: Complete the [Quickstart](/docs/ai-gateway/quickstart) to get an [access key](/docs/ai-gateway/concepts/access-keys). All requests go to `https://gateway.ngrok.ai`. The AI Gateway is compatible with Anthropic's official SDKs. Set the base URL to `https://gateway.ngrok.ai` and authenticate with your [access key](/docs/ai-gateway/concepts/access-keys). Anthropic models also work through OpenAI-compatible SDKs (OpenAI SDK, Vercel AI SDK, LangChain, etc.)—just use the model name directly, for example `model: "claude-opus-4-6"`. You don't need the Anthropic SDK to access Anthropic models. ## Installation ```bash Python theme={null} pip install anthropic ``` ```bash TypeScript theme={null} npm install @anthropic-ai/sdk ``` ## Basic usage Point the SDK at your AI Gateway endpoint: ```python Python highlight={4} theme={null} import anthropic client = anthropic.Anthropic( base_url="https://gateway.ngrok.ai", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) message = client.messages.create( model="claude-opus-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) print(message.content) ``` ```typescript TypeScript highlight={4} theme={null} import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://gateway.ngrok.ai", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const message = await client.messages.create({ model: "claude-opus-4-6", max_tokens: 1024, messages: [{ role: "user", content: "Hello!" }], }); console.log(message.content); ``` State your exact model name and provider. The Anthropic SDK sends the API key via the `x-api-key` header. Access keys work with both `x-api-key` and `Authorization: Bearer` headers. ## Streaming The AI Gateway supports streaming responses with the Anthropic SDK: ```python Python highlight={8} theme={null} import anthropic client = anthropic.Anthropic( base_url="https://gateway.ngrok.ai", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) with client.messages.stream( model="claude-opus-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Write a haiku about APIs"}] ) as stream: for text in stream.text_stream: print(text, end="", flush=True) ``` ```typescript TypeScript highlight={12} theme={null} import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://gateway.ngrok.ai", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const stream = await client.messages.create({ max_tokens: 1024, messages: [{ role: 'user', content: 'Hello, Claude' }], model: 'claude-opus-4-6', stream: true, }); for await (const messageStreamEvent of stream) { console.log(messageStreamEvent.type); } ``` Write a haiku about APIs ## Model failover Specify fallback models in the request body. See [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models). When using Anthropic SDKs with the gateway, model selection is limited to providers that support the Anthropic Claude API. Currently in the ngrok model catalog Anthropic is the only provider that supports this format. ## Tool use Tool calling works as documented by Anthropic: ```python Python highlight={12-22} theme={null} import anthropic client = anthropic.Anthropic( base_url="https://gateway.ngrok.ai", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) message = client.messages.create( model="claude-opus-4-6", max_tokens=1024, messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=[{ "name": "get_weather", "description": "Get weather for a location", "input_schema": { "type": "object", "properties": { "location": {"type": "string"} }, "required": ["location"] } }] ) print(message) ``` ```typescript TypeScript highlight={12-22} theme={null} import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://gateway.ngrok.ai", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const message = await client.messages.create({ model: "claude-opus-4-6", max_tokens: 1024, messages: [{ role: "user", content: "What's the weather in Paris?" }], tools: [ { name: "get_weather", description: "Get weather for a location", input_schema: { type: "object", properties: { location: { type: "string" }, }, required: ["location"], }, }, ], }); console.log(message); ``` ## Error handling The gateway handles many errors automatically through failover. For errors that reach your app: ```python Python theme={null} import anthropic client = anthropic.Anthropic( base_url="https://gateway.ngrok.ai", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) try: message = client.messages.create( model="claude-opus-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) except anthropic.RateLimitError as e: print("Rate limited across all providers") except anthropic.APIError as e: print(f"API error: {e}") ``` ```typescript TypeScript theme={null} import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://gateway.ngrok.ai", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); try { const message = await client.messages.create({ model: "claude-opus-4-6", max_tokens: 1024, messages: [{ role: "user", content: "Hello!" }], }); } catch (error) { if (error instanceof Anthropic.RateLimitError) { console.log("Rate limited across all providers"); } else if (error instanceof Anthropic.APIError) { console.log(`API error: ${error.message}`); } } ``` ## Supported endpoints The AI Gateway supports these Anthropic Claude API endpoints: | Endpoint | Description | | - | - | | `/v1/messages` | Messages | | `/v1/files` | Files. See [Upload and use files](/docs/ai-gateway/guides/files) | ## Next steps * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Model IDs and provider prefixes * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Choose how to reach providers](/docs/ai-gateway/guides/configuring-providers): Credits, provider keys, and self-run models # Google Gen AI SDK Source: https://ngrok.com/docs/ai-gateway/sdks/google Connect Google's Gen AI SDK to the ngrok AI Gateway with Python and TypeScript examples for GenerateContent, streaming, and stateless Interactions. **Prerequisite**: Complete the [quickstart](/docs/ai-gateway/quickstart) to create an [access key](/docs/ai-gateway/concepts/access-keys). See [Google provider setup](/docs/ai-gateway/providers/google#setup) to configure Google routing or bring your own Google key. Use Google's Gen AI SDK for [Python](https://googleapis.github.io/python-genai/) or [TypeScript](https://googleapis.github.io/js-genai/) with the AI Gateway. Set the base URL to `https://gateway.ngrok.ai`, use API version `v1beta`, and authenticate with your ngrok access key. Google models also work through OpenAI-compatible Chat Completions. See the [OpenAI SDK guide](/docs/ai-gateway/sdks/openai) to use your existing OpenAI client with a Google model name. The OpenAI Responses API (`/v1/responses`) doesn't reach Google models. ## Installation ```bash Python theme={null} pip install --upgrade google-genai ``` ```bash TypeScript theme={null} npm install @google/genai ``` ## Basic usage Set `NGROK_AI_GATEWAY_KEY` to your ngrok access key, then send a GenerateContent request: ```python Python highlight={7-11} theme={null} import os from google import genai from google.genai import types client = genai.Client( api_key=os.environ["NGROK_AI_GATEWAY_KEY"], http_options=types.HttpOptions( base_url="https://gateway.ngrok.ai", api_version="v1beta", ), ) response = client.models.generate_content( model="gemini-3.6-flash", contents="Hello", ) print(response.text) ``` ```typescript TypeScript highlight={4-8} theme={null} import { GoogleGenAI } from "@google/genai"; const client = new GoogleGenAI({ apiKey: process.env.NGROK_AI_GATEWAY_KEY, httpOptions: { baseUrl: "https://gateway.ngrok.ai", apiVersion: "v1beta", }, }); const response = await client.models.generateContent({ model: "gemini-3.6-flash", contents: "Hello", }); console.log(response.text); ``` ## Streaming Use the SDK's streaming method to print text as it arrives: ```python Python highlight={14-19} theme={null} import os from google import genai from google.genai import types client = genai.Client( api_key=os.environ["NGROK_AI_GATEWAY_KEY"], http_options=types.HttpOptions( base_url="https://gateway.ngrok.ai", api_version="v1beta", ), ) for chunk in client.models.generate_content_stream( model="gemini-3.6-flash", contents="Write a haiku about APIs", ): if chunk.text: print(chunk.text, end="", flush=True) ``` ```typescript TypeScript highlight={11-18} theme={null} import { GoogleGenAI } from "@google/genai"; const client = new GoogleGenAI({ apiKey: process.env.NGROK_AI_GATEWAY_KEY, httpOptions: { baseUrl: "https://gateway.ngrok.ai", apiVersion: "v1beta", }, }); const stream = await client.models.generateContentStream({ model: "gemini-3.6-flash", contents: "Write a haiku about APIs", }); for await (const chunk of stream) { process.stdout.write(chunk.text ?? ""); } ``` ## Interactions Send `model` and `input` to create a stateless interaction. Set `store=false` explicitly: ```python Python highlight={14-18} theme={null} import os from google import genai from google.genai import types client = genai.Client( api_key=os.environ["NGROK_AI_GATEWAY_KEY"], http_options=types.HttpOptions( base_url="https://gateway.ngrok.ai", api_version="v1beta", ), ) response = client.interactions.create( model="gemini-3.6-flash", input="Hello", store=False, ) print(response.output_text) ``` ```typescript TypeScript highlight={11-15} theme={null} import { GoogleGenAI } from "@google/genai"; const client = new GoogleGenAI({ apiKey: process.env.NGROK_AI_GATEWAY_KEY, httpOptions: { baseUrl: "https://gateway.ngrok.ai", apiVersion: "v1beta", }, }); const response = await client.interactions.create({ model: "gemini-3.6-flash", input: "Hello", store: false, }); console.log(response.output_text); ``` Interactions through the gateway are stateless. The gateway rejects `store=true` and sends every valid request to Google with `store=false`, even if you omit `store` or set it to `null`. Requests paid with credits and bring your own key (BYOK) requests both have these restrictions: * Stored conversation chaining (`previous_interaction_id`) and background execution (`background=true`) are unsupported. * Interaction retrieval, deletion, cancellation, and stream resumption are unsupported. * Agents, environments, webhooks, and triggers are unsupported. Interactions requests paid with credits support text input and text or structured JSON output only. Use the Standard tier: omit `service_tier` or set it to `standard`. The `gemini-3.1-pro-preview` and `gemini-3.1-pro-preview-customtools` models require BYOK for Interactions because they use context-band pricing. With BYOK, supported input and output formats and service tiers depend on Google and the selected model. ## Authentication Use your ngrok access key to authenticate requests to the gateway. The gateway checks credentials in this order: 1. `Authorization: Bearer ` 2. `X-Api-Key: ` 3. `X-Goog-Api-Key: `, which the Google SDK sets from `api_key` in Python or `apiKey` in TypeScript. The URL query parameter `key` fills an empty `X-Goog-Api-Key` header. It doesn't override a header credential. Prefer headers to keep credentials out of URLs. Your access key identifies your gateway configuration. A Google provider key is a separate credential stored in the gateway. For BYOK, attach the stored Google key to your configuration; continue to send your ngrok access key from the client. The gateway replaces client credentials with the selected Google provider credential before forwarding the request. ## Supported endpoints The gateway supports these native Gemini endpoints: | Endpoint | Description | | - | - | | `/v1beta/models/{model}:generateContent` | GenerateContent with a JSON response | | `/v1beta/models/{model}:streamGenerateContent?alt=sse` | Streaming GenerateContent | | `/v1beta/interactions` | Stateless Interactions with a JSON or streaming response | ### Generate content requests Point a Gemini client at `https://gateway.ngrok.ai` and send your access key as described in [Authentication](#authentication). Use `POST /v1beta/models/{model}:generateContent` for a JSON response. For streaming, use `POST /v1beta/models/{model}:streamGenerateContent?alt=sse`. The `alt=sse` query parameter is required. ```bash theme={null} curl "https://gateway.ngrok.ai/v1beta/models/gemini-3.6-flash:generateContent" \ -H "X-Goog-Api-Key: ng-xxxxx-g1-xxxxx" \ -H "Content-Type: application/json" \ -d '{"contents":[{"parts":[{"text":"Hello"}]}]}' ``` Use Google's native GenerateContent payload format. The gateway filters request parameters for the selected backend and applies output-token limits. Responses include the gateway response headers: `Ngrok-AIG-Request-Id` on every response, `Ngrok-AIG-Attempts` once the upstream attempt loop runs, and `Ngrok-AIG-Model` on success. See [Response headers](/docs/ai-gateway/how-it-works#response-headers). ### Interactions requests Send `POST /v1beta/interactions` with `model` and `input` in the request body. The REST base URL is `https://gateway.ngrok.ai/v1beta/`. Omit `stream` or set it to `false` for a JSON response. Set `stream=true` for server-sent events (SSE). Responses use Google's native Interactions format and include the [gateway response headers](/docs/ai-gateway/how-it-works#response-headers). ```bash theme={null} curl -X POST \ "https://gateway.ngrok.ai/v1beta/interactions" \ -H "Content-Type: application/json" \ -H "X-Goog-Api-Key: ng-xxxxx-g1-xxxxx" \ -d '{ "model": "gemini-3.6-flash", "input": "Hello!", "store": false }' ``` ## Next steps * [Configure Google as a provider](/docs/ai-gateway/providers/google) * [Browse Google models](/docs/ai-gateway/reference/model-catalog#google) * [Configure access keys](/docs/ai-gateway/guides/access-key-configurations) # SDK Integration Source: https://ngrok.com/docs/ai-gateway/sdks/index Connect your SDK to the ngrok AI Gateway. **Prerequisite**: Complete the [Quickstart](/docs/ai-gateway/quickstart) to create an access key on [app.ngrok.ai](https://app.ngrok.ai). The AI Gateway works with official and third-party SDKs. Point the base URL at `https://gateway.ngrok.ai` and use your [access key](/docs/ai-gateway/concepts/access-keys). ## Supported SDKs Official SDKs for Python, TypeScript, Go, Java, and .NET Official SDKs for Python, TypeScript, Java, Go, Ruby, C#, and PHP Official SDK examples for Python and TypeScript Build AI apps with React, Next.js, and streaming Type-safe AI library for React and Solid Framework for chains, agents, and RAG cURL, HTTP clients ## Quick start The pattern is the same for any SDK—set the gateway URL and your [access key](/docs/ai-gateway/concepts/access-keys). ```python Python (OpenAI) theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) ``` ```python Python (Anthropic) theme={null} import anthropic client = anthropic.Anthropic( base_url="https://gateway.ngrok.ai", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) ``` ```typescript TypeScript (OpenAI) theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); ``` ```typescript TypeScript (Anthropic) theme={null} import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://gateway.ngrok.ai", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); ``` ## What works through the gateway | Feature | Supported | | - | - | | Chat Completions API | ✅ | | Messages API | ✅ (Anthropic SDKs, on providers that support it) | | Responses API | ✅ (providers that support it, see the [model catalog](/docs/ai-gateway/reference/model-catalog)) | | GenerateContent API | ✅ (Google Gen AI SDK) | | Interactions API | ✅ ([Stateless requests](/docs/ai-gateway/sdks/google#interactions)) | | Streaming | ✅ | | Function/tool calling | ✅ | | Async clients | ✅ | ## Gateway benefits Routing your SDK through the gateway gives you: * Automatic failover that retries with the next key or model on failure. * Provider switching by changing the model, with no integration code changes. * One access key for multiple upstream providers. ## Using different providers Use model prefixes or catalog names: ```python theme={null} client.chat.completions.create(model="gpt-4o", ...) # OpenAI client.chat.completions.create(model="claude-opus-4-6", ...) # Anthropic client.chat.completions.create(model="openai:gpt-4o", ...) # Explicit provider ``` ## Next steps * [OpenAI SDK](/docs/ai-gateway/sdks/openai) * [Anthropic SDK](/docs/ai-gateway/sdks/anthropic) * [Google Gen AI SDK](/docs/ai-gateway/sdks/google) * [Vercel AI SDK](/docs/ai-gateway/sdks/vercel-ai-sdk) * [TanStack AI](/docs/ai-gateway/sdks/tanstack-ai) * [LangChain](/docs/ai-gateway/sdks/langchain) * [Other SDKs](/docs/ai-gateway/sdks/other) # LangChain Source: https://ngrok.com/docs/ai-gateway/sdks/langchain Use the ngrok AI Gateway with LangChain. **Prerequisite**: Complete the [Quickstart](/docs/ai-gateway/quickstart) to get an [access key](/docs/ai-gateway/concepts/access-keys). All requests go to `https://gateway.ngrok.ai`. [LangChain](https://langchain.com/) is a framework for building applications with LLMs. The ngrok AI Gateway works with LangChain's OpenAI-compatible integrations, adding automatic failover and key rotation. ## Installation ```bash Python theme={null} pip install langchain langchain-openai ``` ```bash TypeScript theme={null} npm install langchain @langchain/openai ``` ## Basic usage Configure LangChain to use your AI Gateway. Pass your [access key](/docs/ai-gateway/concepts/access-keys) (`ng-xxxxx-g1-xxxxx`) as the API key—the AI Gateway injects upstream provider credentials from your [access key configuration](/docs/ai-gateway/guides/access-key-configurations). ```python Python highlight={4} theme={null} from langchain_openai import ChatOpenAI llm = ChatOpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", # Your access key model="gpt-4o" ) response = llm.invoke("What is the capital of France?") print(response.content) ``` ```typescript TypeScript highlight={4} theme={null} import { ChatOpenAI } from "@langchain/openai"; const llm = new ChatOpenAI({ configuration: { baseURL: "https://gateway.ngrok.ai/v1", }, apiKey: "ng-xxxxx-g1-xxxxx", // Your access key model: "gpt-4o", }); const response = await llm.invoke("What is the capital of France?"); console.log(response.content); ``` ## Streaming Stream responses for real-time output: ```python highlight={9} theme={null} from langchain_openai import ChatOpenAI llm = ChatOpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", # Your access key model="gpt-4o" ) for chunk in llm.stream("Write a poem about AI"): print(chunk.content, end="", flush=True) ``` ## Using different providers Route to different providers with model prefixes: ```python highlight={7-8} theme={null} from langchain_openai import ChatOpenAI llm = ChatOpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", # Your access key model="anthropic:claude-sonnet-4-6" # Use Anthropic through gateway ) response = llm.invoke("Hello!") ``` ## Chains Build chains that route through the gateway: ```python highlight={4} theme={null} from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate llm = ChatOpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", # Your access key model="gpt-4o" ) prompt = ChatPromptTemplate.from_messages([ ("system", "You are a helpful assistant that translates {input_language} to {output_language}."), ("human", "{input}") ]) chain = prompt | llm response = chain.invoke({ "input_language": "English", "output_language": "French", "input": "Hello, how are you?" }) print(response.content) ``` ## RAG applications Build RAG applications with automatic failover. The AI Gateway doesn't serve embeddings, so create embeddings with your provider directly: ```python highlight={5} theme={null} from langchain_openai import ChatOpenAI, OpenAIEmbeddings from langchain_community.vectorstores import FAISS from langchain_core.prompts import ChatPromptTemplate llm = ChatOpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", # Your access key model="gpt-4o" ) # Embeddings go to OpenAI directly, not through the gateway embeddings = OpenAIEmbeddings(model="text-embedding-3-small") # Create vector store texts = ["Document 1 content", "Document 2 content"] vectorstore = FAISS.from_texts(texts, embeddings) # Query with RAG retriever = vectorstore.as_retriever() docs = retriever.invoke("query") prompt = ChatPromptTemplate.from_template( "Answer based on context: {context}\n\nQuestion: {question}" ) chain = prompt | llm response = chain.invoke({"context": docs, "question": "Your question"}) ``` ## Agents Build agents that use the gateway: ```python highlight={5} theme={null} from langchain_openai import ChatOpenAI from langchain.agents import create_react_agent, AgentExecutor from langchain_core.tools import tool llm = ChatOpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx", # Your access key model="gpt-4o" ) @tool def search(query: str) -> str: """Search for information.""" return f"Results for: {query}" # Create and run agent # ... ``` ## Next steps * [LangChain Documentation](https://python.langchain.com/docs/) - Full LangChain reference * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Model IDs and provider prefixes * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Choose how to reach providers](/docs/ai-gateway/guides/configuring-providers): Credits, provider keys, and self-run models # OpenAI SDK Source: https://ngrok.com/docs/ai-gateway/sdks/openai Use the ngrok AI Gateway with OpenAI's official SDKs. **Prerequisite**: Complete the [Quickstart](/docs/ai-gateway/quickstart) to create an access key on app.ngrok.ai before continuing. The AI Gateway is compatible with OpenAI's official SDKs. Set the base URL to `https://gateway.ngrok.ai/v1` and use your [access key](/docs/ai-gateway/concepts/access-keys). ## Installation ```bash Python theme={null} pip install openai ``` ```bash TypeScript theme={null} npm install openai ``` ## Basic usage Point the SDK at your AI Gateway endpoint and pass your [access key](/docs/ai-gateway/concepts/access-keys) as `api_key`. See [Access keys vs provider keys](/docs/ai-gateway/concepts/access-keys#access-keys-vs-provider-keys) for how credentials flow through the gateway. ```python Python highlight={4} theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```typescript TypeScript highlight={4} theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.choices[0].message.content); ``` State your exact model name and provider. ## Streaming The AI Gateway supports streaming responses: ```python Python highlight={8} theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Write a haiku about APIs"}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ```typescript TypeScript highlight={8} theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Write a haiku about APIs" }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content || ""); } ``` Write a haiku about APIs ## Using different providers Route to different providers using model prefixes: ```python Python highlight={8-9} theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) # OpenAI response = client.chat.completions.create(model="openai:gpt-4o", messages=[...]) # Anthropic (through the gateway) response = client.chat.completions.create(model="anthropic:claude-sonnet-4-6", messages=[...]) # Your self-hosted Ollama response = client.chat.completions.create(model="ollama:llama3.2", messages=[...]) ``` ```typescript TypeScript highlight={8-9} theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); // OpenAI const openaiRes = await client.chat.completions.create({ model: "openai:gpt-4o", messages: [...] }); // Anthropic (through the gateway) const anthropicRes = await client.chat.completions.create({ model: "anthropic:claude-sonnet-4-6", messages: [...] }); // Your self-hosted Ollama const ollamaRes = await client.chat.completions.create({ model: "ollama:llama3.2", messages: [...] }); ``` ## Model failover List fallback models in the request: ```python theme={null} response = client.chat.completions.create( model="gpt-4o", extra_body={"models": ["gpt-4o-mini"]}, messages=[{"role": "user", "content": "Hello!"}], ) ``` To try another model when the first one fails, see [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models). ## Function calling Tool/function calling works exactly as documented by OpenAI: ```python highlight={11-23} theme={null} from openai import OpenAI client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=[{ "type": "function", "function": { "name": "get_weather", "description": "Get weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string"} } } } }] ) ``` What is the current weather in Paris? ## Async usage Use async clients for better performance: ```python Python theme={null} import asyncio from openai import AsyncOpenAI client = AsyncOpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) async def main(): response = await client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) asyncio.run(main()) ``` ```typescript TypeScript theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); // TypeScript client is async by default const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); ``` ## Error handling The gateway handles many errors automatically through failover. For errors that reach your app: ```python Python theme={null} from openai import OpenAI, APIError, RateLimitError client = OpenAI( base_url="https://gateway.ngrok.ai/v1", api_key="ng-xxxxx-g1-xxxxx" # Your access key ) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) except RateLimitError: # All configured keys exhausted print("Rate limited across all providers") except APIError as e: print(f"API error: {e}") ``` ```typescript TypeScript theme={null} import OpenAI, { APIError, RateLimitError } from "openai"; const client = new OpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); try { const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); } catch (e) { if (e instanceof RateLimitError) { // All configured keys exhausted console.log("Rate limited across all providers"); } else if (e instanceof APIError) { console.log(`API error: ${e.message}`); } } ``` ## Supported endpoints The AI Gateway supports these OpenAI API endpoints: | Endpoint | Description | | - | - | | `/v1/chat/completions` | Chat completions | | `/v1/responses` | Responses | | `/v1/decisions` | Decisions. See [Typed-question APIs](/docs/ai-gateway/guides/decisions-and-system-one) | | `/v1/files` | Files. See [Upload and use files](/docs/ai-gateway/guides/files) | ## Next steps * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies) * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # Other SDKs Source: https://ngrok.com/docs/ai-gateway/sdks/other Use the ngrok AI Gateway with any SDK that supports OpenAI API or Anthropic Claude API format. **Prerequisite**: Complete the [Quickstart](/docs/ai-gateway/quickstart) to get an [access key](/docs/ai-gateway/concepts/access-keys). All requests go to `https://gateway.ngrok.ai`. The AI Gateway works with any SDK or HTTP client that supports OpenAI API or Anthropic Claude API format. If your language or framework isn't covered in the dedicated guides, you can still use the gateway. ## Requirements For any SDK or client to work with the AI Gateway, it must: 1. Support the OpenAI API or Anthropic Claude API format 2. Allow customizing the base URL / endpoint 3. Send requests with proper authorization headers (for OpenAI API, `Authorization`; for Anthropic Claude API, `x-api-key`) ## Authentication Pass your [access key](/docs/ai-gateway/concepts/access-keys) (`ng-xxxxx-g1-xxxxx`) as the API key. The AI Gateway injects upstream provider credentials from your [access key configuration](/docs/ai-gateway/guides/access-key-configurations). ## cURL For testing or shell scripts: ```bash OpenAI API theme={null} curl https://gateway.ngrok.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ng-xxxxx-g1-xxxxx" \ # Your access key -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}] }' ``` ```bash Anthropic Claude API theme={null} curl https://gateway.ngrok.ai/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: ng-xxxxx-g1-xxxxx" \ # Your access key -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello!"}] }' ``` ## HTTP request format Any HTTP client can call the AI Gateway: ```http OpenAI API theme={null} POST /v1/chat/completions HTTP/1.1 Host: gateway.ngrok.ai Content-Type: application/json Authorization: Bearer ng-xxxxx-g1-xxxxx { "model": "gpt-4o", "messages": [ {"role": "user", "content": "Hello!"} ] } ``` ```http Anthropic Claude API theme={null} POST /v1/messages HTTP/1.1 Host: gateway.ngrok.ai Content-Type: application/json x-api-key: ng-xxxxx-g1-xxxxx anthropic-version: 2023-06-01 { "model": "claude-sonnet-4-20250514", "max_tokens": 1024, "messages": [ {"role": "user", "content": "Hello!"} ] } ``` ## Using provider prefixes All clients support routing to specific providers using model prefixes: ``` # OpenAI model: "openai:gpt-4o" # Anthropic model: "anthropic:claude-sonnet-4-6" # Self-hosted model: "ollama:llama3.2" ``` ## Supported endpoints The AI Gateway supports these endpoints: | Endpoint | API Format | Description | | - | - | - | | `/v1/chat/completions` | OpenAI | Chat completions | | `/v1/responses` | OpenAI | Responses | | `/v1/models` | OpenAI | List available models | | `/v1/messages` | Anthropic | Messages | | `/v1/files` | OpenAI or Anthropic | Files. See [Upload and use files](/docs/ai-gateway/guides/files) | | `/v1/decisions` | OpenAI | Decisions. See [Typed-question APIs](/docs/ai-gateway/guides/decisions-and-system-one) | | `/v1/systemone` | Typesafe AI | System One. See [Typed-question APIs](/docs/ai-gateway/guides/decisions-and-system-one) | ## Next steps * [OpenAI SDK](/docs/ai-gateway/sdks/openai) - Official Python, TypeScript, Go, Java, and .NET guide * [Vercel AI SDK](/docs/ai-gateway/sdks/vercel-ai-sdk) - For Next.js and React applications * [LangChain](/docs/ai-gateway/sdks/langchain) - For LangChain applications * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies) * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models) # TanStack AI Source: https://ngrok.com/docs/ai-gateway/sdks/tanstack-ai Use the ngrok AI Gateway with TanStack AI. **Prerequisite**: Complete the [Quickstart](/docs/ai-gateway/quickstart) to get an [access key](/docs/ai-gateway/concepts/access-keys). All requests go to `https://gateway.ngrok.ai`. [TanStack AI](https://tanstack.com/ai) is a type-safe library for building AI applications with React, Solid, and other frameworks. It works with the ngrok AI Gateway through its OpenAI adapter. ## Installation ```bash theme={null} npm install @tanstack/ai @tanstack/ai-openai ``` ## Basic usage Configure the OpenAI adapter to use your AI Gateway endpoint: ```typescript highlight={5} theme={null} import { chat } from "@tanstack/ai"; import { openai } from "@tanstack/ai-openai"; const adapter = openai({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const stream = chat({ adapter, messages: [{ role: "user", content: "Hello!" }], model: "gpt-4o", }); ``` ## Streaming responses TanStack AI is built for streaming. Process chunks as they arrive: ```typescript highlight={10-12} theme={null} import { chat } from "@tanstack/ai"; import { openai } from "@tanstack/ai-openai"; const adapter = openai({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const stream = chat({ adapter, messages: [{ role: "user", content: "Write a poem about AI gateways." }], model: "gpt-4o", }); for await (const chunk of stream) { // Process each chunk as it arrives console.log(chunk); } ``` ## API route handler Build API endpoints that stream responses: ```typescript title="app/api/chat/route.ts" highlight={5} theme={null} import { chat, toStreamResponse } from "@tanstack/ai"; import { openai } from "@tanstack/ai-openai"; const adapter = openai({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); export async function POST(request: Request) { const { messages } = await request.json(); const stream = chat({ adapter, messages, model: "gpt-4o", }); return toStreamResponse(stream); } ``` ## Using different providers The AI Gateway routes based on the model name. Use provider prefixes to target specific providers: ```typescript highlight={10-11} theme={null} import { chat } from "@tanstack/ai"; import { openai } from "@tanstack/ai-openai"; const gateway = openai({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key // Gateway handles auth }); // Use different providers through the same gateway const openaiStream = chat({ adapter: gateway, model: "openai:gpt-4o", messages }); const anthropicStream = chat({ adapter: gateway, model: "anthropic:claude-sonnet-4-6", messages }); ``` ## Model failover To try another model when the first one fails, see [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models). ## Tool calling The AI Gateway supports function/tool calling: ```typescript highlight={10-20} theme={null} import { chat, toolDefinition } from "@tanstack/ai"; import { openai } from "@tanstack/ai-openai"; import { z } from "zod"; const adapter = openai({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const getWeatherDef = toolDefinition({ name: "get_weather", description: "Get the current weather", inputSchema: z.object({ location: z.string().describe("The location to get weather for"), }), }); const getWeather = getWeatherDef.server(async ({ location }) => { return { temperature: 72, conditions: "sunny" }; }); const stream = chat({ adapter, messages: [{ role: "user", content: "What's the weather in San Francisco?" }], model: "gpt-4o", tools: [getWeather], }); ``` ## Provider options Pass provider-specific options for fine-grained control: ```typescript highlight={13-19} theme={null} import { chat } from "@tanstack/ai"; import { openai } from "@tanstack/ai-openai"; const adapter = openai({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const stream = chat({ adapter, messages, model: "gpt-4o", providerOptions: { temperature: 0.7, maxTokens: 1000, topP: 0.9, frequencyPenalty: 0.5, presencePenalty: 0.5, }, }); ``` ## React integration Use TanStack AI with React for building chat interfaces: ```tsx title="app/page.tsx" theme={null} "use client"; import { useChat } from "@tanstack/ai-react"; export default function Chat() { const { messages, input, handleInputChange, handleSubmit } = useChat({ api: "/api/chat", }); return (
{messages.map((m) => (
{m.role}: {m.content}
))}
); } ``` ## Environment variables Set up your environment: ```bash title=".env.local" theme={null} AI_GATEWAY_URL=https://gateway.ngrok.ai/v1 AI_GATEWAY_ACCESS_KEY=ng-xxxxx-g1-xxxxx ``` ```typescript theme={null} import { openai } from "@tanstack/ai-openai"; const adapter = openai({ baseURL: process.env.AI_GATEWAY_URL, apiKey: process.env.AI_GATEWAY_ACCESS_KEY!, }); ``` ## Next steps * [TanStack AI Documentation](https://tanstack.com/ai/latest/docs) * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies) * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models) * [Access key configurations](/docs/ai-gateway/guides/access-key-configurations) # Vercel AI SDK Source: https://ngrok.com/docs/ai-gateway/sdks/vercel-ai-sdk Use the ngrok AI Gateway with Vercel's AI SDK. **Prerequisite**: Complete the [Quickstart](/docs/ai-gateway/quickstart) to get an [access key](/docs/ai-gateway/concepts/access-keys). All requests go to `https://gateway.ngrok.ai`. The [Vercel AI SDK](https://sdk.vercel.ai/) provides a unified interface for building AI applications. It works with the ngrok AI Gateway, adding provider failover, key rotation, and observability to Vercel's streaming and UI components. ## Installation ```bash theme={null} npm install ai @ai-sdk/openai ``` ## Basic usage Point the OpenAI provider at your AI Gateway endpoint: ```typescript Next.js App Router highlight={5} theme={null} import { generateText } from "ai"; import { createOpenAI } from "@ai-sdk/openai"; const openai = createOpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const { text } = await generateText({ model: openai("gpt-4o"), prompt: "What is the meaning of life?", }); ``` ```typescript Edge Runtime highlight={5} theme={null} import { generateText } from "ai"; import { createOpenAI } from "@ai-sdk/openai"; const openai = createOpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); export const runtime = "edge"; export async function POST(req: Request) { const { prompt } = await req.json(); const { text } = await generateText({ model: openai("gpt-4o"), prompt, }); return Response.json({ text }); } ``` ## Streaming responses The AI Gateway supports streaming. Use `streamText` for real-time responses: ```typescript highlight={9-12} theme={null} import { streamText } from "ai"; import { createOpenAI } from "@ai-sdk/openai"; const openai = createOpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const result = streamText({ model: openai("gpt-4o"), prompt: "Write a poem about AI gateways.", }); for await (const textPart of result.textStream) { process.stdout.write(textPart); } ``` ## Chat interface Build chat applications with the `useChat` hook: ```tsx title="app/page.tsx" highlight={7} theme={null} "use client"; import { useChat } from "ai/react"; export default function Chat() { const { messages, input, handleInputChange, handleSubmit } = useChat({ api: "/api/chat", }); return (
{messages.map((m) => (
{m.role}: {m.content}
))}
); } ``` ```typescript title="app/api/chat/route.ts" highlight={5} theme={null} import { streamText } from "ai"; import { createOpenAI } from "@ai-sdk/openai"; const openai = createOpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); export async function POST(req: Request) { const { messages } = await req.json(); const result = streamText({ model: openai("gpt-4o"), messages, }); return result.toDataStreamResponse(); } ``` ## Using different providers The AI Gateway routes based on the model name. Use provider prefixes to be explicit: ```typescript highlight={10-11} theme={null} import { generateText } from "ai"; import { createOpenAI } from "@ai-sdk/openai"; const gateway = createOpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); // Use different providers through the same gateway const openaiResult = await generateText({ model: gateway("openai:gpt-4o"), prompt: "Hello" }); const anthropicResult = await generateText({ model: gateway("anthropic:claude-sonnet-4-6"), prompt: "Hello" }); ``` ## Model failover List fallback models in the request. See [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models). ## Environment variables Set up your environment: ```bash title=".env.local" theme={null} AI_GATEWAY_URL=https://gateway.ngrok.ai/v1 AI_GATEWAY_ACCESS_KEY=ng-xxxxx-g1-xxxxx ``` ```typescript theme={null} const openai = createOpenAI({ baseURL: process.env.AI_GATEWAY_URL, apiKey: process.env.AI_GATEWAY_ACCESS_KEY!, }); ``` ## Tool calling The AI Gateway supports function/tool calling: ```typescript highlight={12-22} theme={null} import { generateText, tool } from "ai"; import { createOpenAI } from "@ai-sdk/openai"; import { z } from "zod"; const openai = createOpenAI({ baseURL: "https://gateway.ngrok.ai/v1", apiKey: "ng-xxxxx-g1-xxxxx", // Your access key }); const { text, toolCalls } = await generateText({ model: openai("gpt-4o"), tools: { weather: tool({ description: "Get the weather in a location", parameters: z.object({ location: z.string().describe("The location to get weather for"), }), execute: async ({ location }) => { return { temperature: 72, condition: "sunny" }; }, }), }, prompt: "What's the weather in San Francisco?", }); ``` ## Error handling Handle errors gracefully: ```typescript theme={null} import { generateText, APICallError } from "ai"; try { const { text } = await generateText({ model: openai("gpt-4o"), prompt: "Hello", }); } catch (error) { if (error instanceof APICallError) { console.error("API Error:", error.message); console.error("Status:", error.statusCode); } } ``` With the AI Gateway's failover, many errors are handled automatically by retrying with different providers or keys. ## Next steps * [Vercel AI SDK Documentation](https://sdk.vercel.ai/docs) - Full SDK reference * [Choose a model](/docs/ai-gateway/guides/model-selection-strategies): Model IDs and provider prefixes * [Configure fallback models](/docs/ai-gateway/guides/configure-fallback-models): Try another model when the first one fails * [Choose how to reach providers](/docs/ai-gateway/guides/configuring-providers): Credits, provider keys, and self-run models # Create Source: https://ngrok.com/docs/api-reference/abusereports/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /abuse_reports Creates a new abuse report which will be reviewed by our system and abuse response team. This API is only available to authorized accounts. Contact abuse@ngrok.com to request access # Get Source: https://ngrok.com/docs/api-reference/abusereports/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /abuse_reports/{id} Get the detailed status of abuse report by ID. # Create Source: https://ngrok.com/docs/api-reference/agentingresses/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /agent_ingresses Create a new Agent Ingress. The ngrok agent can be configured to connect to ngrok via the new set of addresses on the returned Agent Ingress. # Delete Source: https://ngrok.com/docs/api-reference/agentingresses/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /agent_ingresses/{id} Delete an Agent Ingress by ID # Get Source: https://ngrok.com/docs/api-reference/agentingresses/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /agent_ingresses/{id} Get the details of an Agent Ingress by ID. # List Source: https://ngrok.com/docs/api-reference/agentingresses/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /agent_ingresses List all Agent Ingresses owned by this account # Update Source: https://ngrok.com/docs/api-reference/agentingresses/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /agent_ingresses/{id} Update attributes of an Agent Ingress by ID. # Create Source: https://ngrok.com/docs/api-reference/apikeys/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /api_keys Create a new API key. The generated API key can be used to authenticate to the ngrok API. # Delete Source: https://ngrok.com/docs/api-reference/apikeys/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /api_keys/{id} Delete an API key by ID # Get Source: https://ngrok.com/docs/api-reference/apikeys/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /api_keys/{id} Get the details of an API key by ID. # List Source: https://ngrok.com/docs/api-reference/apikeys/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /api_keys List all API keys owned by this account # Update Source: https://ngrok.com/docs/api-reference/apikeys/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /api_keys/{id} Update attributes of an API key by ID. # Delete Source: https://ngrok.com/docs/api-reference/applicationsessions/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /app/sessions/{id} Delete an application session by ID. # Get Source: https://ngrok.com/docs/api-reference/applicationsessions/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /app/sessions/{id} Get an application session by ID. # List Source: https://ngrok.com/docs/api-reference/applicationsessions/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /app/sessions List all application sessions for this account. # Delete Source: https://ngrok.com/docs/api-reference/applicationusers/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /app/users/{id} Delete an application user by ID. # Get Source: https://ngrok.com/docs/api-reference/applicationusers/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /app/users/{id} Get an application user by ID. # List Source: https://ngrok.com/docs/api-reference/applicationusers/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /app/users List all application users for this account. # Create Source: https://ngrok.com/docs/api-reference/botusers/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /bot_users Create a new bot user # Delete Source: https://ngrok.com/docs/api-reference/botusers/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /bot_users/{id} Delete a bot user by ID # Get Source: https://ngrok.com/docs/api-reference/botusers/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /bot_users/{id} Get the details of a Bot User by ID. # List Source: https://ngrok.com/docs/api-reference/botusers/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /bot_users List all bot users in this account. # Update Source: https://ngrok.com/docs/api-reference/botusers/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /bot_users/{id} Update attributes of a bot user by ID. # Create Source: https://ngrok.com/docs/api-reference/certificateauthorities/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /certificate_authorities Upload a new Certificate Authority # Delete Source: https://ngrok.com/docs/api-reference/certificateauthorities/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /certificate_authorities/{id} Delete a Certificate Authority # Get Source: https://ngrok.com/docs/api-reference/certificateauthorities/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /certificate_authorities/{id} Get detailed information about a certificate authority # List Source: https://ngrok.com/docs/api-reference/certificateauthorities/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /certificate_authorities List all Certificate Authority on this account # Update Source: https://ngrok.com/docs/api-reference/certificateauthorities/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /certificate_authorities/{id} Update attributes of a Certificate Authority by ID # Create Source: https://ngrok.com/docs/api-reference/credentials/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /credentials Create a new tunnel authtoken credential. This authtoken credential can be used to start a new tunnel session. The response to this API call is the only time the generated token is available. If you need it for future use, you must save it securely yourself. # Delete Source: https://ngrok.com/docs/api-reference/credentials/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /credentials/{id} Delete a tunnel authtoken credential by ID # Get Source: https://ngrok.com/docs/api-reference/credentials/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /credentials/{id} Get detailed information about a tunnel authtoken credential # List Source: https://ngrok.com/docs/api-reference/credentials/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /credentials List all tunnel authtoken credentials on this account # Update Source: https://ngrok.com/docs/api-reference/credentials/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /credentials/{id} Update attributes of an tunnel authtoken credential by ID # Delete Source: https://ngrok.com/docs/api-reference/edgeroutebackendmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/backend # Get Source: https://ngrok.com/docs/api-reference/edgeroutebackendmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/backend # Replace Source: https://ngrok.com/docs/api-reference/edgeroutebackendmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/backend # Delete Source: https://ngrok.com/docs/api-reference/edgeroutecircuitbreakermodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/circuit_breaker # Get Source: https://ngrok.com/docs/api-reference/edgeroutecircuitbreakermodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/circuit_breaker # Replace Source: https://ngrok.com/docs/api-reference/edgeroutecircuitbreakermodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/circuit_breaker # Delete Source: https://ngrok.com/docs/api-reference/edgeroutecompressionmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/compression # Get Source: https://ngrok.com/docs/api-reference/edgeroutecompressionmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/compression # Replace Source: https://ngrok.com/docs/api-reference/edgeroutecompressionmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/compression # Delete Source: https://ngrok.com/docs/api-reference/edgerouteiprestrictionmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/ip_restriction # Get Source: https://ngrok.com/docs/api-reference/edgerouteiprestrictionmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/ip_restriction # Replace Source: https://ngrok.com/docs/api-reference/edgerouteiprestrictionmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/ip_restriction # Delete Source: https://ngrok.com/docs/api-reference/edgerouteoauthmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/oauth # Get Source: https://ngrok.com/docs/api-reference/edgerouteoauthmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/oauth # Replace Source: https://ngrok.com/docs/api-reference/edgerouteoauthmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/oauth # Delete Source: https://ngrok.com/docs/api-reference/edgerouteoidcmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/oidc # Get Source: https://ngrok.com/docs/api-reference/edgerouteoidcmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/oidc # Replace Source: https://ngrok.com/docs/api-reference/edgerouteoidcmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/oidc # Delete Source: https://ngrok.com/docs/api-reference/edgerouterequestheadersmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/request_headers # Get Source: https://ngrok.com/docs/api-reference/edgerouterequestheadersmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/request_headers # Replace Source: https://ngrok.com/docs/api-reference/edgerouterequestheadersmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/request_headers # Delete Source: https://ngrok.com/docs/api-reference/edgerouteresponseheadersmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/response_headers # Get Source: https://ngrok.com/docs/api-reference/edgerouteresponseheadersmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/response_headers # Replace Source: https://ngrok.com/docs/api-reference/edgerouteresponseheadersmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/response_headers # Delete Source: https://ngrok.com/docs/api-reference/edgeroutesamlmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/saml # Get Source: https://ngrok.com/docs/api-reference/edgeroutesamlmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/saml # Replace Source: https://ngrok.com/docs/api-reference/edgeroutesamlmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/saml # Delete Source: https://ngrok.com/docs/api-reference/edgeroutetrafficpolicymodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/traffic_policy # Get Source: https://ngrok.com/docs/api-reference/edgeroutetrafficpolicymodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/traffic_policy # Replace Source: https://ngrok.com/docs/api-reference/edgeroutetrafficpolicymodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/traffic_policy # Delete Source: https://ngrok.com/docs/api-reference/edgerouteuseragentfiltermodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/user_agent_filter # Get Source: https://ngrok.com/docs/api-reference/edgerouteuseragentfiltermodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/user_agent_filter # Replace Source: https://ngrok.com/docs/api-reference/edgerouteuseragentfiltermodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/user_agent_filter # Delete Source: https://ngrok.com/docs/api-reference/edgeroutewebhookverificationmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/webhook_verification # Get Source: https://ngrok.com/docs/api-reference/edgeroutewebhookverificationmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/webhook_verification # Replace Source: https://ngrok.com/docs/api-reference/edgeroutewebhookverificationmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/webhook_verification # Delete Source: https://ngrok.com/docs/api-reference/edgeroutewebsockettcpconvertermodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id}/websocket_tcp_converter # Get Source: https://ngrok.com/docs/api-reference/edgeroutewebsockettcpconvertermodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id}/websocket_tcp_converter # Replace Source: https://ngrok.com/docs/api-reference/edgeroutewebsockettcpconvertermodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{edge_id}/routes/{id}/websocket_tcp_converter # Create Source: https://ngrok.com/docs/api-reference/edgeshttps/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /edges/https Create an HTTPS Edge # Delete Source: https://ngrok.com/docs/api-reference/edgeshttps/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{id} Delete an HTTPS Edge by ID # Get Source: https://ngrok.com/docs/api-reference/edgeshttps/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{id} Get an HTTPS Edge by ID # List Source: https://ngrok.com/docs/api-reference/edgeshttps/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https Returns a list of all HTTPS Edges on this account # Update Source: https://ngrok.com/docs/api-reference/edgeshttps/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /edges/https/{id} Updates an HTTPS Edge by ID. If a module is not specified in the update, it will not be modified. However, each module configuration that is specified will completely replace the existing value. There is no way to delete an existing module via this API, instead use the delete module API. # Create Source: https://ngrok.com/docs/api-reference/edgeshttpsroutes/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /edges/https/{edge_id}/routes Create an HTTPS Edge Route # Delete Source: https://ngrok.com/docs/api-reference/edgeshttpsroutes/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{edge_id}/routes/{id} Delete an HTTPS Edge Route by ID # Get Source: https://ngrok.com/docs/api-reference/edgeshttpsroutes/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{edge_id}/routes/{id} Get an HTTPS Edge Route by ID # Update Source: https://ngrok.com/docs/api-reference/edgeshttpsroutes/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /edges/https/{edge_id}/routes/{id} Updates an HTTPS Edge Route by ID. If a module is not specified in the update, it will not be modified. However, each module configuration that is specified will completely replace the existing value. There is no way to delete an existing module via this API, instead use the delete module API. # Create Source: https://ngrok.com/docs/api-reference/edgestcp/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /edges/tcp Create a TCP Edge # Delete Source: https://ngrok.com/docs/api-reference/edgestcp/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/tcp/{id} Delete a TCP Edge by ID # Get Source: https://ngrok.com/docs/api-reference/edgestcp/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tcp/{id} Get a TCP Edge by ID # List Source: https://ngrok.com/docs/api-reference/edgestcp/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tcp Returns a list of all TCP Edges on this account # Update Source: https://ngrok.com/docs/api-reference/edgestcp/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /edges/tcp/{id} Updates a TCP Edge by ID. If a module is not specified in the update, it will not be modified. However, each module configuration that is specified will completely replace the existing value. There is no way to delete an existing module via this API, instead use the delete module API. # Create Source: https://ngrok.com/docs/api-reference/edgestls/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /edges/tls Create a TLS Edge # Delete Source: https://ngrok.com/docs/api-reference/edgestls/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/tls/{id} Delete a TLS Edge by ID # Get Source: https://ngrok.com/docs/api-reference/edgestls/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tls/{id} Get a TLS Edge by ID # List Source: https://ngrok.com/docs/api-reference/edgestls/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tls Returns a list of all TLS Edges on this account # Update Source: https://ngrok.com/docs/api-reference/edgestls/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /edges/tls/{id} Updates a TLS Edge by ID. If a module is not specified in the update, it will not be modified. However, each module configuration that is specified will completely replace the existing value. There is no way to delete an existing module via this API, instead use the delete module API. # Create Source: https://ngrok.com/docs/api-reference/endpoints/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /endpoints Create an endpoint, currently available only for cloud endpoints # Delete Source: https://ngrok.com/docs/api-reference/endpoints/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /endpoints/{id} Delete an Endpoint by ID, currently available only for cloud endpoints # Get Source: https://ngrok.com/docs/api-reference/endpoints/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /endpoints/{id} Get the status of an endpoint by ID # List Source: https://ngrok.com/docs/api-reference/endpoints/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /endpoints List all active endpoints on the account # Update Source: https://ngrok.com/docs/api-reference/endpoints/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /endpoints/{id} Update an Endpoint by ID, currently available only for cloud endpoints # Create Source: https://ngrok.com/docs/api-reference/eventdestinations/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /event_destinations Create a new Event Destination. It will not apply to anything until it is associated with an Event Subscription. # Delete Source: https://ngrok.com/docs/api-reference/eventdestinations/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /event_destinations/{id} Delete an Event Destination. If the Event Destination is still referenced by an Event Subscription. # Get Source: https://ngrok.com/docs/api-reference/eventdestinations/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /event_destinations/{id} Get detailed information about an Event Destination by ID. # List Source: https://ngrok.com/docs/api-reference/eventdestinations/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /event_destinations List all Event Destinations on this account. # Update Source: https://ngrok.com/docs/api-reference/eventdestinations/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /event_destinations/{id} Update attributes of an Event Destination. # Create Source: https://ngrok.com/docs/api-reference/eventsources/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /event_subscriptions/{subscription_id}/sources Add an additional type for which this event subscription will trigger # Delete Source: https://ngrok.com/docs/api-reference/eventsources/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /event_subscriptions/{subscription_id}/sources/{type} Remove a type for which this event subscription will trigger # Get Source: https://ngrok.com/docs/api-reference/eventsources/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /event_subscriptions/{subscription_id}/sources/{type} Get the details for a given type that triggers for the given event subscription # List Source: https://ngrok.com/docs/api-reference/eventsources/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /event_subscriptions/{subscription_id}/sources List the types for which this event subscription will trigger # Update Source: https://ngrok.com/docs/api-reference/eventsources/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /event_subscriptions/{subscription_id}/sources/{type} Update the type for which this event subscription will trigger # Create Source: https://ngrok.com/docs/api-reference/eventsubscriptions/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /event_subscriptions Create an Event Subscription. # Delete Source: https://ngrok.com/docs/api-reference/eventsubscriptions/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /event_subscriptions/{id} Delete an Event Subscription. # Get Source: https://ngrok.com/docs/api-reference/eventsubscriptions/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /event_subscriptions/{id} Get an Event Subscription by ID. # List Source: https://ngrok.com/docs/api-reference/eventsubscriptions/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /event_subscriptions List this Account's Event Subscriptions. # Update Source: https://ngrok.com/docs/api-reference/eventsubscriptions/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /event_subscriptions/{id} Update an Event Subscription. # Create Source: https://ngrok.com/docs/api-reference/failoverbackends/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /backends/failover Create a new Failover backend # Delete Source: https://ngrok.com/docs/api-reference/failoverbackends/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /backends/failover/{id} Delete a Failover backend by ID. # Get Source: https://ngrok.com/docs/api-reference/failoverbackends/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /backends/failover/{id} Get detailed information about a Failover backend by ID # List Source: https://ngrok.com/docs/api-reference/failoverbackends/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /backends/failover List all Failover backends on this account # Update Source: https://ngrok.com/docs/api-reference/failoverbackends/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /backends/failover/{id} Update Failover backend by ID # Create Source: https://ngrok.com/docs/api-reference/httpresponsebackends/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /backends/http_response # Delete Source: https://ngrok.com/docs/api-reference/httpresponsebackends/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /backends/http_response/{id} # Get Source: https://ngrok.com/docs/api-reference/httpresponsebackends/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /backends/http_response/{id} # List Source: https://ngrok.com/docs/api-reference/httpresponsebackends/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /backends/http_response # Update Source: https://ngrok.com/docs/api-reference/httpresponsebackends/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /backends/http_response/{id} # Delete Source: https://ngrok.com/docs/api-reference/httpsedgemutualtlsmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{id}/mutual_tls # Get Source: https://ngrok.com/docs/api-reference/httpsedgemutualtlsmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{id}/mutual_tls # Replace Source: https://ngrok.com/docs/api-reference/httpsedgemutualtlsmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{id}/mutual_tls # Delete Source: https://ngrok.com/docs/api-reference/httpsedgetlsterminationmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/https/{id}/tls_termination # Get Source: https://ngrok.com/docs/api-reference/httpsedgetlsterminationmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/https/{id}/tls_termination # Replace Source: https://ngrok.com/docs/api-reference/httpsedgetlsterminationmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/https/{id}/tls_termination # Create Source: https://ngrok.com/docs/api-reference/ippolicies/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /ip_policies Create a new IP policy. It will not apply to any traffic until you associate to a traffic source via an endpoint configuration or IP restriction. # Delete Source: https://ngrok.com/docs/api-reference/ippolicies/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /ip_policies/{id} Delete an IP policy. If the IP policy is referenced by another object for the purposes of traffic restriction it will be treated as if the IP policy remains but has zero rules. # Get Source: https://ngrok.com/docs/api-reference/ippolicies/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ip_policies/{id} Get detailed information about an IP policy by ID. # List Source: https://ngrok.com/docs/api-reference/ippolicies/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ip_policies List all IP policies on this account # Update Source: https://ngrok.com/docs/api-reference/ippolicies/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /ip_policies/{id} Update attributes of an IP policy by ID # Create Source: https://ngrok.com/docs/api-reference/ippolicyrules/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /ip_policy_rules Create a new IP policy rule attached to an IP Policy. # Delete Source: https://ngrok.com/docs/api-reference/ippolicyrules/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /ip_policy_rules/{id} Delete an IP policy rule. # Get Source: https://ngrok.com/docs/api-reference/ippolicyrules/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ip_policy_rules/{id} Get detailed information about an IP policy rule by ID. # List Source: https://ngrok.com/docs/api-reference/ippolicyrules/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ip_policy_rules List all IP policy rules on this account # Update Source: https://ngrok.com/docs/api-reference/ippolicyrules/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /ip_policy_rules/{id} Update attributes of an IP policy rule by ID # Create Source: https://ngrok.com/docs/api-reference/iprestrictions/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /ip_restrictions Create a new IP restriction # Delete Source: https://ngrok.com/docs/api-reference/iprestrictions/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /ip_restrictions/{id} Delete an IP restriction # Get Source: https://ngrok.com/docs/api-reference/iprestrictions/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ip_restrictions/{id} Get detailed information about an IP restriction # List Source: https://ngrok.com/docs/api-reference/iprestrictions/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ip_restrictions List all IP restrictions on this account # Update Source: https://ngrok.com/docs/api-reference/iprestrictions/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /ip_restrictions/{id} Update attributes of an IP restriction by ID # Kubernetes Operators Source: https://ngrok.com/docs/api-reference/kubernetes-operators API reference documentation for Kubernetes Operator endpoints used by the ngrok Kubernetes Operator to manage operator state. These endpoints are not intended to be accessed directly by users. They exist to be accessed by the [Kubernetes Operator](/docs/k8s). They are documented here for completeness. ## Create Kubernetes Operator Create a new Kubernetes Operator ### Request POST /kubernetes\_operators #### Example Request ```bash theme={null} curl \ -X POST \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"deployment":{"name":"ngrok-operator","namespace":"ngrok-operator"},"description":"Example Kubernetes Operator","enabled_features":["Ingress"],"metadata":"{\"environment\": \"staging\"}","region":"global"}' \ https://api.ngrok.com/kubernetes_operators ``` #### Parameters | Name | Type | Description | | - | - | - | | `description` | string | human-readable description of this Kubernetes Operator. optional, max 255 bytes. | | `metadata` | string | arbitrary user-defined machine-readable data of this Kubernetes Operator. optional, max 4096 bytes. | | `enabled_features` | List\ | features enabled for this Kubernetes Operator. a subset of "bindings", "ingress", and "gateway" | | `region` | string | the ngrok region in which the ingress for this Operator is served. defaults to "global" | | `deployment` | [KubernetesOperatorDeployment](#kubernetesoperatordeployment-parameters) | information about the deployment of this Kubernetes Operator | | `binding` | [KubernetesOperatorBindingCreate](#kubernetesoperatorbindingcreate-parameters) | configuration for the Bindings feature of this Kubernetes Operator. set only if enabling the "bindings" feature | #### KubernetesOperatorDeployment parameters | Name | Type | Description | | - | - | - | | `name` | string | the deployment name | | `namespace` | string | the namespace this Kubernetes Operator is deployed to | | `version` | string | the version of this Kubernetes Operator | | `cluster_name` | string | user-given name for the cluster the Kubernetes Operator is deployed to | #### KubernetesOperatorBindingCreate parameters | Name | Type | Description | | - | - | - | | `endpoint_selectors` | List\ | the list of cel expressions that filter the k8s bound endpoints for this Operator | | `csr` | string | CSR is supplied during initial creation to enable creating a mutual TLS secured connection between ngrok and the Operator. This is an internal implementation detail and subject to change. | | `ingress_endpoint` | string | the public ingress endpoint for this Kubernetes Operator | ### Response Returns a 201 response on success #### Example Response ```json theme={null} { "binding": { "cert": { "cert": "", "not_after": "0001-01-01T00:00:00Z", "not_before": "0001-01-01T00:00:00Z" }, "endpoint_selectors": null }, "created_at": "2025-09-04T10:11:43Z", "deployment": { "cluster_name": "", "name": "ngrok-operator", "namespace": "ngrok-operator", "version": "" }, "description": "Example Kubernetes Operator", "enabled_features": [ "ingress" ], "id": "k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT", "metadata": "{\"environment\": \"staging\"}", "principal": { "id": "usr_32ELAUA7XkJHRP2DgH2JjDRLp81", "uri": "" }, "region": "global", "updated_at": "2025-09-04T10:11:43Z", "uri": "https://api.ngrok.com/kubernetes_operators/k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT" } ``` #### Fields | Name | Type | Description | | - | - | - | | `id` | string | unique identifier for this Kubernetes Operator | | `uri` | string | URI of this Kubernetes Operator API resource | | `created_at` | string | timestamp when the Kubernetes Operator was created. RFC 3339 format | | `updated_at` | string | timestamp when the Kubernetes Operator was last updated. RFC 3339 format | | `description` | string | human-readable description of this Kubernetes Operator. optional, max 255 bytes. | | `metadata` | string | arbitrary user-defined machine-readable data of this Kubernetes Operator. optional, max 4096 bytes. | | `principal` | [Ref](#ref-fields) | the principal who created this Kubernetes Operator | | `enabled_features` | List\ | features enabled for this Kubernetes Operator. a subset of "bindings", "ingress", and "gateway" | | `region` | string | the ngrok region in which the ingress for this Operator is served. defaults to "global" | | `deployment` | [KubernetesOperatorDeployment](#kubernetesoperatordeployment-fields) | information about the deployment of this Kubernetes Operator | | `binding` | [KubernetesOperatorBinding](#kubernetesoperatorbinding-fields) | information about the Bindings feature of this Kubernetes Operator, if enabled | #### Ref fields | Name | Type | Description | | - | - | - | | `id` | string | a resource identifier | | `uri` | string | a uri for locating a resource | #### KubernetesOperatorDeployment fields | Name | Type | Description | | - | - | - | | `name` | string | the deployment name | | `namespace` | string | the namespace this Kubernetes Operator is deployed to | | `version` | string | the version of this Kubernetes Operator | | `cluster_name` | string | user-given name for the cluster the Kubernetes Operator is deployed to | #### KubernetesOperatorBinding fields | Name | Type | Description | | - | - | - | | `endpoint_selectors` | List\ | the list of cel expressions that filter the k8s bound endpoints for this Operator | | `cert` | [KubernetesOperatorCert](#kubernetesoperatorcert-fields) | the binding certificate information | | `ingress_endpoint` | string | the public ingress endpoint for this Kubernetes Operator | #### KubernetesOperatorCert fields | Name | Type | Description | | - | - | - | | `cert` | string | the public client certificate generated for this Kubernetes Operator from the CSR supplied when enabling the Bindings feature | | `not_before` | string | timestamp when the certificate becomes valid. RFC 3339 format | | `not_after` | string | timestamp when the certificate becomes invalid. RFC 3339 format | ## Update Kubernetes Operator Update an existing Kubernetes Operator by ID. ### Request PATCH /kubernetes\_operators/\{id} #### Example Request ```bash theme={null} curl \ -X PATCH \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"metadata":"{\"environment\": \"production\"}"}' \ https://api.ngrok.com/kubernetes_operators/k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT ``` #### Parameters | Name | Type | Description | | - | - | - | | `id` | string | unique identifier for this Kubernetes Operator | | `description` | string | human-readable description of this Kubernetes Operator. optional, max 255 bytes. | | `metadata` | string | arbitrary user-defined machine-readable data of this Kubernetes Operator. optional, max 4096 bytes. | | `enabled_features` | List\ | features enabled for this Kubernetes Operator. a subset of "bindings", "ingress", and "gateway" | | `region` | string | the ngrok region in which the ingress for this Operator is served. defaults to "global" | | `binding` | [KubernetesOperatorBindingUpdate](#kubernetesoperatorbindingupdate-parameters) | configuration for the Bindings feature of this Kubernetes Operator. set only if enabling the "bindings" feature | | `deployment` | [KubernetesOperatorDeploymentUpdate](#kubernetesoperatordeploymentupdate-parameters) | configuration for the Deployment info | #### KubernetesOperatorBindingUpdate parameters | Name | Type | Description | | - | - | - | | `endpoint_selectors` | List\ | the list of cel expressions that filter the k8s bound endpoints for this Operator | | `csr` | string | CSR is supplied during initial creation to enable creating a mutual TLS secured connection between ngrok and the Operator. This is an internal implementation detail and subject to change. | | `ingress_endpoint` | string | the public ingress endpoint for this Kubernetes Operator | #### KubernetesOperatorDeploymentUpdate parameters | Name | Type | Description | | - | - | - | | `name` | string | the deployment name | | `version` | string | the version of this Kubernetes Operator | ### Response Returns a 200 response on success #### Example Response ```json theme={null} { "binding": { "cert": { "cert": "", "not_after": "0001-01-01T00:00:00Z", "not_before": "0001-01-01T00:00:00Z" }, "endpoint_selectors": null, "ingress_endpoint": "connect.ngrok-endpoints.com:443" }, "created_at": "2025-09-04T10:11:43Z", "deployment": { "cluster_name": "", "name": "ngrok-operator", "namespace": "ngrok-operator", "version": "" }, "description": "Example Kubernetes Operator", "enabled_features": [ "ingress" ], "id": "k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT", "metadata": "{\"environment\": \"production\"}", "principal": { "id": "usr_32ELAUA7XkJHRP2DgH2JjDRLp81", "uri": "" }, "region": "global", "updated_at": "2025-09-04T10:11:43Z", "uri": "https://api.ngrok.com/kubernetes_operators/k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT" } ``` #### Fields | Name | Type | Description | | - | - | - | | `id` | string | unique identifier for this Kubernetes Operator | | `uri` | string | URI of this Kubernetes Operator API resource | | `created_at` | string | timestamp when the Kubernetes Operator was created. RFC 3339 format | | `updated_at` | string | timestamp when the Kubernetes Operator was last updated. RFC 3339 format | | `description` | string | human-readable description of this Kubernetes Operator. optional, max 255 bytes. | | `metadata` | string | arbitrary user-defined machine-readable data of this Kubernetes Operator. optional, max 4096 bytes. | | `principal` | [Ref](#ref-fields) | the principal who created this Kubernetes Operator | | `enabled_features` | List\ | features enabled for this Kubernetes Operator. a subset of "bindings", "ingress", and "gateway" | | `region` | string | the ngrok region in which the ingress for this Operator is served. defaults to "global" | | `deployment` | [KubernetesOperatorDeployment](#kubernetesoperatordeployment-fields) | information about the deployment of this Kubernetes Operator | | `binding` | [KubernetesOperatorBinding](#kubernetesoperatorbinding-fields) | information about the Bindings feature of this Kubernetes Operator, if enabled | #### Ref fields | Name | Type | Description | | - | - | - | | `id` | string | a resource identifier | | `uri` | string | a uri for locating a resource | #### KubernetesOperatorDeployment fields | Name | Type | Description | | - | - | - | | `name` | string | the deployment name | | `namespace` | string | the namespace this Kubernetes Operator is deployed to | | `version` | string | the version of this Kubernetes Operator | | `cluster_name` | string | user-given name for the cluster the Kubernetes Operator is deployed to | #### KubernetesOperatorBinding fields | Name | Type | Description | | - | - | - | | `endpoint_selectors` | List\ | the list of cel expressions that filter the k8s bound endpoints for this Operator | | `cert` | [KubernetesOperatorCert](#kubernetesoperatorcert-fields) | the binding certificate information | | `ingress_endpoint` | string | the public ingress endpoint for this Kubernetes Operator | #### KubernetesOperatorCert fields | Name | Type | Description | | - | - | - | | `cert` | string | the public client certificate generated for this Kubernetes Operator from the CSR supplied when enabling the Bindings feature | | `not_before` | string | timestamp when the certificate becomes valid. RFC 3339 format | | `not_after` | string | timestamp when the certificate becomes invalid. RFC 3339 format | ## Delete Kubernetes Operator Delete a Kubernetes Operator ### Request DELETE /kubernetes\_operators/\{id} #### Example Request ```bash theme={null} curl \ -X DELETE \ -H "Authorization: Bearer {API_KEY}" \ -H "Ngrok-Version: 2" \ https://api.ngrok.com/kubernetes_operators/k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT ``` ### Response Returns a 204 response with no body on success ## Get Kubernetes Operator Get of a Kubernetes Operator ### Request GET /kubernetes\_operators/\{id} #### Example Request ```bash theme={null} curl \ -X GET \ -H "Authorization: Bearer {API_KEY}" \ -H "Ngrok-Version: 2" \ https://api.ngrok.com/kubernetes_operators/k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT ``` ### Response Returns a 200 response on success #### Example Response ```json theme={null} { "binding": { "cert": { "cert": "", "not_after": "0001-01-01T00:00:00Z", "not_before": "0001-01-01T00:00:00Z" }, "endpoint_selectors": null }, "created_at": "2025-09-04T10:11:43Z", "deployment": { "cluster_name": "", "name": "ngrok-operator", "namespace": "ngrok-operator", "version": "" }, "description": "Example Kubernetes Operator", "enabled_features": [ "ingress" ], "id": "k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT", "metadata": "{\"environment\": \"staging\"}", "principal": { "id": "usr_32ELAUA7XkJHRP2DgH2JjDRLp81", "uri": "" }, "region": "global", "updated_at": "2025-09-04T10:11:43Z", "uri": "https://api.ngrok.com/kubernetes_operators/k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT" } ``` #### Fields | Name | Type | Description | | - | - | - | | `id` | string | unique identifier for this Kubernetes Operator | | `uri` | string | URI of this Kubernetes Operator API resource | | `created_at` | string | timestamp when the Kubernetes Operator was created. RFC 3339 format | | `updated_at` | string | timestamp when the Kubernetes Operator was last updated. RFC 3339 format | | `description` | string | human-readable description of this Kubernetes Operator. optional, max 255 bytes. | | `metadata` | string | arbitrary user-defined machine-readable data of this Kubernetes Operator. optional, max 4096 bytes. | | `principal` | [Ref](#ref-fields) | the principal who created this Kubernetes Operator | | `enabled_features` | List\ | features enabled for this Kubernetes Operator. a subset of "bindings", "ingress", and "gateway" | | `region` | string | the ngrok region in which the ingress for this Operator is served. defaults to "global" | | `deployment` | [KubernetesOperatorDeployment](#kubernetesoperatordeployment-fields) | information about the deployment of this Kubernetes Operator | | `binding` | [KubernetesOperatorBinding](#kubernetesoperatorbinding-fields) | information about the Bindings feature of this Kubernetes Operator, if enabled | #### Ref fields | Name | Type | Description | | - | - | - | | `id` | string | a resource identifier | | `uri` | string | a uri for locating a resource | #### KubernetesOperatorDeployment fields | Name | Type | Description | | - | - | - | | `name` | string | the deployment name | | `namespace` | string | the namespace this Kubernetes Operator is deployed to | | `version` | string | the version of this Kubernetes Operator | | `cluster_name` | string | user-given name for the cluster the Kubernetes Operator is deployed to | #### KubernetesOperatorBinding fields | Name | Type | Description | | - | - | - | | `endpoint_selectors` | List\ | the list of cel expressions that filter the k8s bound endpoints for this Operator | | `cert` | [KubernetesOperatorCert](#kubernetesoperatorcert-fields) | the binding certificate information | | `ingress_endpoint` | string | the public ingress endpoint for this Kubernetes Operator | #### KubernetesOperatorCert fields | Name | Type | Description | | - | - | - | | `cert` | string | the public client certificate generated for this Kubernetes Operator from the CSR supplied when enabling the Bindings feature | | `not_before` | string | timestamp when the certificate becomes valid. RFC 3339 format | | `not_after` | string | timestamp when the certificate becomes invalid. RFC 3339 format | ## List Kubernetes operators List all Kubernetes Operators owned by this account ### Request GET /kubernetes\_operators #### Example Request ```bash theme={null} curl \ -X GET \ -H "Authorization: Bearer {API_KEY}" \ -H "Ngrok-Version: 2" \ https://api.ngrok.com/kubernetes_operators ``` ### Response Returns a 200 response on success #### Example Response ```json theme={null} { "next_page_uri": null, "operators": [ { "binding": { "cert": { "cert": "", "not_after": "0001-01-01T00:00:00Z", "not_before": "0001-01-01T00:00:00Z" }, "endpoint_selectors": null }, "created_at": "2025-09-04T10:11:43Z", "deployment": { "cluster_name": "", "name": "ngrok-operator", "namespace": "ngrok-operator", "version": "" }, "description": "Example Kubernetes Operator", "enabled_features": [ "ingress" ], "id": "k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT", "metadata": "{\"environment\": \"staging\"}", "principal": { "id": "usr_32ELAUA7XkJHRP2DgH2JjDRLp81", "uri": "" }, "region": "global", "updated_at": "2025-09-04T10:11:43Z", "uri": "https://api.ngrok.com/kubernetes_operators/k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT" } ], "uri": "https://api.ngrok.com/kubernetes_operators" } ``` #### Fields | Name | Type | Description | | - | - | - | | `operators` | [KubernetesOperator](#kubernetesoperator-fields) | the list of Kubernetes Operators for this account | | `uri` | string | | | `next_page_uri` | string | URI of the next page, or null if there is no next page | #### KubernetesOperator fields | Name | Type | Description | | - | - | - | | `id` | string | unique identifier for this Kubernetes Operator | | `uri` | string | URI of this Kubernetes Operator API resource | | `created_at` | string | timestamp when the Kubernetes Operator was created. RFC 3339 format | | `updated_at` | string | timestamp when the Kubernetes Operator was last updated. RFC 3339 format | | `description` | string | human-readable description of this Kubernetes Operator. optional, max 255 bytes. | | `metadata` | string | arbitrary user-defined machine-readable data of this Kubernetes Operator. optional, max 4096 bytes. | | `principal` | [Ref](#ref-fields) | the principal who created this Kubernetes Operator | | `enabled_features` | List\ | features enabled for this Kubernetes Operator. a subset of "bindings", "ingress", and "gateway" | | `region` | string | the ngrok region in which the ingress for this Operator is served. defaults to "global" | | `deployment` | [KubernetesOperatorDeployment](#kubernetesoperatordeployment-fields) | information about the deployment of this Kubernetes Operator | | `binding` | [KubernetesOperatorBinding](#kubernetesoperatorbinding-fields) | information about the Bindings feature of this Kubernetes Operator, if enabled | #### Ref fields | Name | Type | Description | | - | - | - | | `id` | string | a resource identifier | | `uri` | string | a uri for locating a resource | #### KubernetesOperatorDeployment fields | Name | Type | Description | | - | - | - | | `name` | string | the deployment name | | `namespace` | string | the namespace this Kubernetes Operator is deployed to | | `version` | string | the version of this Kubernetes Operator | | `cluster_name` | string | user-given name for the cluster the Kubernetes Operator is deployed to | #### KubernetesOperatorBinding fields | Name | Type | Description | | - | - | - | | `endpoint_selectors` | List\ | the list of cel expressions that filter the k8s bound endpoints for this Operator | | `cert` | [KubernetesOperatorCert](#kubernetesoperatorcert-fields) | the binding certificate information | | `ingress_endpoint` | string | the public ingress endpoint for this Kubernetes Operator | #### KubernetesOperatorCert fields | Name | Type | Description | | - | - | - | | `cert` | string | the public client certificate generated for this Kubernetes Operator from the CSR supplied when enabling the Bindings feature | | `not_before` | string | timestamp when the certificate becomes valid. RFC 3339 format | | `not_after` | string | timestamp when the certificate becomes invalid. RFC 3339 format | ## GetBoundEndpoints Kubernetes Operator List Endpoints bound to a Kubernetes Operator ### Request GET /kubernetes\_operators/\{id}/bound\_endpoints #### Example Request ```bash theme={null} curl \ -X GET \ -H "Authorization: Bearer {API_KEY}" \ -H "Ngrok-Version: 2" \ https://api.ngrok.com/kubernetes_operators/k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT/bound_endpoints ``` ### Response Returns a 200 response on success #### Example Response ```json theme={null} { "endpoints": [], "next_page_uri": null, "uri": "https://api.ngrok.com/kubernetes_operators/k8sop_32ELIMnXLu9Qkpj1OddUYoLIHfT/bound_endpoints" } ``` #### Fields | Name | Type | Description | | - | - | - | | `endpoints` | [Endpoint](#endpoint-fields) | the list of all active endpoints on this account | | `uri` | string | URI of the endpoints list API resource | | `next_page_uri` | string | URI of the next page, or null if there is no next page | #### Endpoint fields | Name | Type | Description | | - | - | - | | `id` | string | unique endpoint resource identifier | | `region` | string | identifier of the region this endpoint belongs to | | `created_at` | string | timestamp when the endpoint was created in RFC 3339 format | | `updated_at` | string | timestamp when the endpoint was updated in RFC 3339 format | | `public_url` | string | URL of the hostport served by this endpoint | | `proto` | string | protocol served by this endpoint. one of `http`, `https`, `tcp`, or `tls` | | `scheme` | string | | | `hostport` | string | hostport served by this endpoint (hostname:port) -> soon to be deprecated | | `host` | string | | | `port` | int64 | | | `type` | string | whether the endpoint is `ephemeral` (served directly by an agent-initiated tunnel) or `edge` (served by an edge) or `cloud (represents a Cloud Endpoint)` | | `metadata` | string | user-supplied metadata of the associated tunnel or edge object | | `description` | string | user-supplied description of the associated tunnel | | `domain` | [Ref](#ref-fields) | the domain reserved for this endpoint | | `tcp_addr` | [Ref](#ref-fields) | the address reserved for this endpoint | | `tunnel` | [Ref](#ref-fields) | the tunnel serving requests to this endpoint, if this is an ephemeral endpoint | | `edge` | [Ref](#ref-fields) | the edge serving requests to this endpoint, if this is an edge endpoint | | `upstream_url` | string | the local address the tunnel forwards to | | `upstream_protocol` | string | the protocol the agent uses to forward with | | `url` | string | the URL of the endpoint | | `principal` | [Ref](#ref-fields) | The ID of the owner (bot or user) that owns this endpoint | | `traffic_policy` | string | The Traffic Policy attached to this endpoint | | `bindings` | List\ | the bindings associated with this endpoint | | `tunnel_session` | [Ref](#ref-fields) | The tunnel session of the agent for this endpoint | | `uri` | string | URI of the clep API resource | | `name` | string | user supplied name for the endpoint | | `pooling_enabled` | boolean | whether the endpoint allows pooling | #### Ref fields | Name | Type | Description | | - | - | - | | `id` | string | a resource identifier | | `uri` | string | a uri for locating a resource | # Create Source: https://ngrok.com/docs/api-reference/reservedaddrs/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /reserved_addrs Create a new reserved address. # Delete Source: https://ngrok.com/docs/api-reference/reservedaddrs/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /reserved_addrs/{id} Delete a reserved address. # Get Source: https://ngrok.com/docs/api-reference/reservedaddrs/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /reserved_addrs/{id} Get the details of a reserved address. # List Source: https://ngrok.com/docs/api-reference/reservedaddrs/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /reserved_addrs List all reserved addresses on this account. # Update Source: https://ngrok.com/docs/api-reference/reservedaddrs/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /reserved_addrs/{id} Update the attributes of a reserved address. # Create Source: https://ngrok.com/docs/api-reference/reserveddomains/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /reserved_domains Create a new reserved domain. # Delete Source: https://ngrok.com/docs/api-reference/reserveddomains/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /reserved_domains/{id} Delete a reserved domain. # DeleteCertificate Source: https://ngrok.com/docs/api-reference/reserveddomains/deletecertificate https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /reserved_domains/{id}/certificate Detach the certificate attached to a reserved domain. # DeleteCertificateManagementPolicy Source: https://ngrok.com/docs/api-reference/reserveddomains/deletecertificatemanagementpolicy https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /reserved_domains/{id}/certificate_management_policy Detach the certificate management policy attached to a reserved domain. # Get Source: https://ngrok.com/docs/api-reference/reserveddomains/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /reserved_domains/{id} Get the details of a reserved domain. # List Source: https://ngrok.com/docs/api-reference/reserveddomains/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /reserved_domains List all reserved domains on this account. # Update Source: https://ngrok.com/docs/api-reference/reserveddomains/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /reserved_domains/{id} Update the attributes of a reserved domain. # Create Source: https://ngrok.com/docs/api-reference/secrets/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /vault_secrets Create a new Secret # Delete Source: https://ngrok.com/docs/api-reference/secrets/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /vault_secrets/{id} Delete a Secret # Get Source: https://ngrok.com/docs/api-reference/secrets/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /vault_secrets/{id} Get a Secret by ID # List Source: https://ngrok.com/docs/api-reference/secrets/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /vault_secrets List all Secrets owned by account # Update Source: https://ngrok.com/docs/api-reference/secrets/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /vault_secrets/{id} Update an existing Secret by ID # Service Users (Bot Users) Source: https://ngrok.com/docs/api-reference/service-users Service Users API reference. Service Users were previously called Bot Users. The Bot User API endpoint is still available but will be deprecated in the future. If you need the Bot Users API reference, you can find it [here](/docs/api-reference/botusers/get). ## Create Service User Create a new Service User. ### Request `POST /service_users` #### Example request ```bash theme={null} curl \ -X POST \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"name":"new service user from API"}' \ https://api.ngrok.com/service_users ``` #### Parameters | Name | Type | Description | | - | - | - | | `name` | string | Human-readable name used to identify the Service User. | | `active` | boolean | Whether or not the Service User is active. | ### Response Returns a 201 response on success. #### Example response ```json theme={null} { "active": true, "created_at": "2025-09-04T10:11:43Z", "id": "service_32ELIFubEAAGUeRtreNW6kr94Od", "name": "new service user from API", "uri": "https://api.ngrok.com/service_users/service_32ELIFubEAAGUeRtreNW6kr94Od" } ``` #### Fields | Name | Type | Description | | - | - | - | | `id` | string | Unique API key resource identifier. | | `uri` | string | URI to the API resource of this Service User | | `name` | string | Human-readable name used to identify the Service User. | | `active` | boolean | Whether or not the Service User is active. | | `created_at` | string | Timestamp when the API key was created, in RFC 3339 format. | ## Delete Service User Delete a Service User by ID. ### Request `DELETE /service_users/\{id\}` #### Example request ```bash theme={null} curl \ -X DELETE \ -H "Authorization: Bearer {API_KEY}" \ -H "Ngrok-Version: 2" \ https://api.ngrok.com/service_users/service_32ELIFubEAAGUeRtreNW6kr94Od ``` ### Response Returns a 204 response with no body on success. ## Get Service User Get the details of a Service User by ID. ### Request `GET /service_users/\{id\}` #### Example request ```bash theme={null} curl \ -X GET \ -H "Authorization: Bearer {API_KEY}" \ -H "Ngrok-Version: 2" \ https://api.ngrok.com/service_users/service_32ELIFubEAAGUeRtreNW6kr94Od ``` ### Response Returns a 200 response on success. #### Example response ```json theme={null} { "active": true, "created_at": "2025-09-04T10:11:43Z", "id": "service_32ELIFubEAAGUeRtreNW6kr94Od", "name": "new service user from API", "uri": "https://api.ngrok.com/service_users/service_32ELIFubEAAGUeRtreNW6kr94Od" } ``` #### Fields | Name | Type | Description | | - | - | - | | `id` | string | Unique API key resource identifier. | | `uri` | string | URI to the API resource of this Service User. | | `name` | string | Human-readable name used to identify the Service User. | | `active` | boolean | Whether or not the Service User is active. | | `created_at` | string | Timestamp when the API key was created, in RFC 3339 format. | ## List Service Users List all Service Users in this account. ### Request `GET /service_users` #### Example request ```bash theme={null} curl \ -X GET \ -H "Authorization: Bearer {API_KEY}" \ -H "Ngrok-Version: 2" \ https://api.ngrok.com/service_users?limit=1 ``` ### Response Returns a 200 response on success. #### Example response ```json theme={null} { "service_users": [ { "active": true, "created_at": "2025-09-04T10:11:43Z", "id": "service_32ELIKFMeMYDQm0tERqc5MmICl4", "name": "API example service user", "uri": "https://api.ngrok.com/service_users/service_32ELIKFMeMYDQm0tERqc5MmICl4" } ], "next_page_uri": "https://api.ngrok.com/service_users?before_id=service_32ELIKFMeMYDQm0tERqc5MmICl4&limit=1", "uri": "https://api.ngrok.com/service_users" } ``` #### Fields | Name | Type | Description | | - | - | - | | `service_users` | [ServiceUser](#serviceuser-fields) | List of all Service Users on this account. | | `uri` | string | URI of the Service Users list API resource. | | `next_page_uri` | string | URI of the next page, or null if there is no next page. | #### ServiceUser fields | Name | Type | Description | | - | - | - | | `id` | string | Unique API key resource identifier. | | `uri` | string | URI to the API resource of this Service User. | | `name` | string | Human-readable name used to identify the Service User. | | `active` | boolean | Whether or not the Service User is active. | | `created_at` | string | Timestamp when the API key was created, in RFC 3339 format. | ## Update Service User Update attributes of a Service User by ID. ### Request `PATCH /service_users/\{id\}` #### Example request ```bash theme={null} curl \ -X PATCH \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"active":false,"name":"inactive service user from API"}' \ https://api.ngrok.com/service_users/service_32ELIFubEAAGUeRtreNW6kr94Od ``` #### Parameters | Name | Type | Description | | - | - | - | | `id` | string | | | `name` | string | Human-readable name used to identify the Service User. | | `active` | boolean | Whether or not the Service User is active. | ### Response Returns a 200 response and a copy of the updated entity on success. #### Example response ```json theme={null} { "active": false, "created_at": "2025-09-04T10:11:43Z", "id": "service_32ELIFubEAAGUeRtreNW6kr94Od", "name": "inactive service user from API", "uri": "https://api.ngrok.com/service_users/service_32ELIFubEAAGUeRtreNW6kr94Od" } ``` #### Fields | Name | Type | Description | | - | - | - | | `id` | string | Unique API key resource identifier. | | `uri` | string | URI to the API resource of this Service User. | | `name` | string | Human-readable name used to identify the Service User. | | `active` | boolean | Whether or not the Service User is active. | | `created_at` | string | Timestamp when the API key was created, in RFC 3339 format. | # Create Source: https://ngrok.com/docs/api-reference/sshcertificateauthorities/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /ssh_certificate_authorities Create a new SSH Certificate Authority # Delete Source: https://ngrok.com/docs/api-reference/sshcertificateauthorities/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /ssh_certificate_authorities/{id} Delete an SSH Certificate Authority # Get Source: https://ngrok.com/docs/api-reference/sshcertificateauthorities/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ssh_certificate_authorities/{id} Get detailed information about an SSH Certificate Authority # List Source: https://ngrok.com/docs/api-reference/sshcertificateauthorities/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ssh_certificate_authorities List all SSH Certificate Authorities on this account # Update Source: https://ngrok.com/docs/api-reference/sshcertificateauthorities/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /ssh_certificate_authorities/{id} Update an SSH Certificate Authority # Create Source: https://ngrok.com/docs/api-reference/sshcredentials/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /ssh_credentials Create a new ssh_credential from an uploaded public SSH key. This ssh credential can be used to start new tunnels via ngrok's SSH gateway. # Delete Source: https://ngrok.com/docs/api-reference/sshcredentials/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /ssh_credentials/{id} Delete an ssh_credential by ID # Get Source: https://ngrok.com/docs/api-reference/sshcredentials/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ssh_credentials/{id} Get detailed information about an ssh_credential # List Source: https://ngrok.com/docs/api-reference/sshcredentials/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ssh_credentials List all ssh credentials on this account # Update Source: https://ngrok.com/docs/api-reference/sshcredentials/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /ssh_credentials/{id} Update attributes of an ssh_credential by ID # Create Source: https://ngrok.com/docs/api-reference/sshhostcertificates/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /ssh_host_certificates Create a new SSH Host Certificate # Delete Source: https://ngrok.com/docs/api-reference/sshhostcertificates/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /ssh_host_certificates/{id} Delete an SSH Host Certificate # Get Source: https://ngrok.com/docs/api-reference/sshhostcertificates/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ssh_host_certificates/{id} Get detailed information about an SSH Host Certificate # List Source: https://ngrok.com/docs/api-reference/sshhostcertificates/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ssh_host_certificates List all SSH Host Certificates issued on this account # Update Source: https://ngrok.com/docs/api-reference/sshhostcertificates/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /ssh_host_certificates/{id} Update an SSH Host Certificate # Create Source: https://ngrok.com/docs/api-reference/sshusercertificates/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /ssh_user_certificates Create a new SSH User Certificate # Delete Source: https://ngrok.com/docs/api-reference/sshusercertificates/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /ssh_user_certificates/{id} Delete an SSH User Certificate # Get Source: https://ngrok.com/docs/api-reference/sshusercertificates/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ssh_user_certificates/{id} Get detailed information about an SSH User Certificate # List Source: https://ngrok.com/docs/api-reference/sshusercertificates/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /ssh_user_certificates List all SSH User Certificates issued on this account # Update Source: https://ngrok.com/docs/api-reference/sshusercertificates/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /ssh_user_certificates/{id} Update an SSH User Certificate # Create Source: https://ngrok.com/docs/api-reference/staticbackends/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /backends/static Create a new static backend # Delete Source: https://ngrok.com/docs/api-reference/staticbackends/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /backends/static/{id} Delete a static backend by ID. # Get Source: https://ngrok.com/docs/api-reference/staticbackends/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /backends/static/{id} Get detailed information about a static backend by ID # List Source: https://ngrok.com/docs/api-reference/staticbackends/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /backends/static List all static backends on this account # Update Source: https://ngrok.com/docs/api-reference/staticbackends/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /backends/static/{id} Update static backend by ID # Delete Source: https://ngrok.com/docs/api-reference/tcpedgebackendmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/tcp/{id}/backend # Get Source: https://ngrok.com/docs/api-reference/tcpedgebackendmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tcp/{id}/backend # Replace Source: https://ngrok.com/docs/api-reference/tcpedgebackendmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/tcp/{id}/backend # Delete Source: https://ngrok.com/docs/api-reference/tcpedgeiprestrictionmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/tcp/{id}/ip_restriction # Get Source: https://ngrok.com/docs/api-reference/tcpedgeiprestrictionmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tcp/{id}/ip_restriction # Replace Source: https://ngrok.com/docs/api-reference/tcpedgeiprestrictionmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/tcp/{id}/ip_restriction # Delete Source: https://ngrok.com/docs/api-reference/tcpedgetrafficpolicymodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/tcp/{id}/traffic_policy # Get Source: https://ngrok.com/docs/api-reference/tcpedgetrafficpolicymodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tcp/{id}/traffic_policy # Replace Source: https://ngrok.com/docs/api-reference/tcpedgetrafficpolicymodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/tcp/{id}/traffic_policy # Create Source: https://ngrok.com/docs/api-reference/tlscertificates/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /tls_certificates Upload a new TLS certificate # Delete Source: https://ngrok.com/docs/api-reference/tlscertificates/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /tls_certificates/{id} Delete a TLS certificate # Get Source: https://ngrok.com/docs/api-reference/tlscertificates/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /tls_certificates/{id} Get detailed information about a TLS certificate # List Source: https://ngrok.com/docs/api-reference/tlscertificates/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /tls_certificates List all TLS certificates on this account # Update Source: https://ngrok.com/docs/api-reference/tlscertificates/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /tls_certificates/{id} Update attributes of a TLS Certificate by ID # Delete Source: https://ngrok.com/docs/api-reference/tlsedgebackendmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/tls/{id}/backend # Get Source: https://ngrok.com/docs/api-reference/tlsedgebackendmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tls/{id}/backend # Replace Source: https://ngrok.com/docs/api-reference/tlsedgebackendmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/tls/{id}/backend # Delete Source: https://ngrok.com/docs/api-reference/tlsedgeiprestrictionmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/tls/{id}/ip_restriction # Get Source: https://ngrok.com/docs/api-reference/tlsedgeiprestrictionmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tls/{id}/ip_restriction # Replace Source: https://ngrok.com/docs/api-reference/tlsedgeiprestrictionmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/tls/{id}/ip_restriction # Delete Source: https://ngrok.com/docs/api-reference/tlsedgemutualtlsmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/tls/{id}/mutual_tls # Get Source: https://ngrok.com/docs/api-reference/tlsedgemutualtlsmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tls/{id}/mutual_tls # Replace Source: https://ngrok.com/docs/api-reference/tlsedgemutualtlsmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/tls/{id}/mutual_tls # Delete Source: https://ngrok.com/docs/api-reference/tlsedgetlsterminationmodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/tls/{id}/tls_termination # Get Source: https://ngrok.com/docs/api-reference/tlsedgetlsterminationmodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tls/{id}/tls_termination # Replace Source: https://ngrok.com/docs/api-reference/tlsedgetlsterminationmodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/tls/{id}/tls_termination # Delete Source: https://ngrok.com/docs/api-reference/tlsedgetrafficpolicymodule/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /edges/tls/{id}/traffic_policy # Get Source: https://ngrok.com/docs/api-reference/tlsedgetrafficpolicymodule/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /edges/tls/{id}/traffic_policy # Replace Source: https://ngrok.com/docs/api-reference/tlsedgetrafficpolicymodule/replace https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml put /edges/tls/{id}/traffic_policy # Create Source: https://ngrok.com/docs/api-reference/tunnelgroupbackends/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /backends/tunnel_group Create a new TunnelGroup backend # Delete Source: https://ngrok.com/docs/api-reference/tunnelgroupbackends/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /backends/tunnel_group/{id} Delete a TunnelGroup backend by ID. # Get Source: https://ngrok.com/docs/api-reference/tunnelgroupbackends/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /backends/tunnel_group/{id} Get detailed information about a TunnelGroup backend by ID # List Source: https://ngrok.com/docs/api-reference/tunnelgroupbackends/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /backends/tunnel_group List all TunnelGroup backends on this account # Update Source: https://ngrok.com/docs/api-reference/tunnelgroupbackends/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /backends/tunnel_group/{id} Update TunnelGroup backend by ID # Get Source: https://ngrok.com/docs/api-reference/tunnels/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /tunnels/{id} Get the status of a tunnel by ID # List Source: https://ngrok.com/docs/api-reference/tunnels/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /tunnels List all online tunnels currently running on the account. # Get Source: https://ngrok.com/docs/api-reference/tunnelsessions/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /tunnel_sessions/{id} Get the detailed status of a tunnel session by ID # List Source: https://ngrok.com/docs/api-reference/tunnelsessions/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /tunnel_sessions List all online tunnel sessions running on this account. # Restart Source: https://ngrok.com/docs/api-reference/tunnelsessions/restart https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /tunnel_sessions/{id}/restart Issues a command instructing the ngrok agent to restart. The agent restarts itself by calling exec() on platforms that support it. This operation is notably not supported on Windows. When an agent restarts, it reconnects with a new tunnel session ID. # Stop Source: https://ngrok.com/docs/api-reference/tunnelsessions/stop https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /tunnel_sessions/{id}/stop Issues a command instructing the ngrok agent that started this tunnel session to exit. # Update Source: https://ngrok.com/docs/api-reference/tunnelsessions/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /tunnel_sessions/{id}/update Issues a command instructing the ngrok agent to update itself to the latest version. After this call completes successfully, the ngrok agent will be in the update process. A caller should wait some amount of time to allow the update to complete (at least 10 seconds) before making a call to the Restart endpoint to request that the agent restart itself to start using the new code. This call will never update an ngrok agent to a new major version which could cause breaking compatibility issues. If you wish to update to a new major version, that must be done manually. Still, please be aware that updating your ngrok agent could break your integration. This call will fail in any of the following circumstances: there is no update available the ngrok agent's configuration disabled update checks the agent is currently in process of updating the agent has already successfully updated but has not yet been restarted # Create Source: https://ngrok.com/docs/api-reference/vaults/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /vaults Create a new Vault # Delete Source: https://ngrok.com/docs/api-reference/vaults/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /vaults/{id} Delete a Vault # Get Source: https://ngrok.com/docs/api-reference/vaults/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /vaults/{id} Get a Vault by ID # List Source: https://ngrok.com/docs/api-reference/vaults/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /vaults List all Vaults owned by account # Update Source: https://ngrok.com/docs/api-reference/vaults/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /vaults/{id} Update an existing Vault by ID # Create Source: https://ngrok.com/docs/api-reference/weightedbackends/create https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml post /backends/weighted Create a new Weighted backend # Delete Source: https://ngrok.com/docs/api-reference/weightedbackends/delete https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml delete /backends/weighted/{id} Delete a Weighted backend by ID. # Get Source: https://ngrok.com/docs/api-reference/weightedbackends/get https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /backends/weighted/{id} Get detailed information about a Weighted backend by ID # List Source: https://ngrok.com/docs/api-reference/weightedbackends/list https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml get /backends/weighted List all Weighted backends on this account # Update Source: https://ngrok.com/docs/api-reference/weightedbackends/update https://raw.githubusercontent.com/ngrok/ngrok-openapi/refs/heads/main/ngrok.yaml patch /backends/weighted/{id} Update Weighted backend by ID # API Filtering Source: https://ngrok.com/docs/api/api-filtering Learn how to use API Filtering to make operational tooling faster and more precise. When using ngrok's API, you can add the `filter` query parameter to `GET` requests to return only those results which match a provided criteria. This makes automated management of resources easier while eliminating the need to download large collections and filter client-side. To use API Filtering, you pass a subset of CEL expressions to the `filter` query parameter, as demonstrated in the following example. This example request fetches a list of all your Cloud and Agent endpoints. ```http theme={null} GET /endpoints?filter=obj.type == "cloud" || obj.type == "agent" ``` ## Request shape ```http theme={null} GET /{resource}?filter={CEL_EXPRESSION} ``` ### cURL usage ``` curl --location 'https://api.ngrok.com/endpoints' \ --get \ --data-urlencode '' \ --header 'Ngrok-Version: 2' \ --header 'Authorization: Bearer ' ``` ## Supported CEL (subset) These core operators and helpers are supported: * Logical operators: `!`, `&&`, `||` * Comparative operators: `<`, `<=`, `==`, `!=`, `>=`, `>` * Parentheses for grouping * List membership using the `in` keyword * String substring checks: `startsWith()`, `contains()`, `endsWith()` * Length / emptiness checks: `size()`, `== ""`, `== null` * Date and time helpers: `timestamp(RFC-3339)`, `timestamp(time.now)`, `timestamp(time.now).subtract()`, `timestamp(time.now).add()` ### Instance inspection (versus list comprehension) Expressions are evaluated against a single **resource instance** exposed as `obj`. Compare fields **on the instance** rather than attempting list-wise checks on fields. ✅ Valid ```http theme={null} GET /endpoints?filter=obj.type == "cloud" || obj.type == "agent" GET /endpoints?filter=obj.type in ["agent", "cloud"] GET /endpoints?filter="public" in obj.bindings || "internal" in obj.bindings ``` ❌ Not valid ```http theme={null} GET /endpoints?filter=["agent","cloud"] in obj.types GET /endpoints?filter=obj.bindings in ["public", "internal"] ``` ## Dates and time helpers * **Treat timestamps as numerics** by using `<`, `<=`, `==`, `>=`, `>` directly on `timestamp()` fields, for example: ```http theme={null} GET /vaults?filter=obj.created_at < timestamp("2025-10-31T09:23:45-07:00") ``` * **Relative helpers based on the current time:** Use `timestamp(time.now)` for the current time, then chain `.subtract()` or `.add()` with a duration string such as `"7d"`, `"24h"`, or `"15m"`. For example: ```http theme={null} # resources created in the last 7 days GET /endpoints?filter=obj.created_at >= timestamp(time.now).subtract("7d") # resources that will expire within the next 24 hours GET /tls_certificates?filter=obj.not_after <= timestamp(time.now).add("24h") ``` ## Query restrictions and limitations ### Unsupported CEL features To keep filter evaluation small and predictable, the following CEL features are not supported. * **No index access** (for example, `a[0]`) * **No arithmetic** (for example, `a + b`) * **No ternary** (for example, `cond ? x : y`) * **No type checks** (for example, `type(a) == string`) * **No regexes** * **No fuzzy matching** These exclusions intentionally keep evaluation small and predictable. ### High-entropy fields and substring checks **High entropy** fields are fields with values that are effectively random, usually because they're generated. The `id` field on a response object, such as `obj.id`, is a common example. [Substring functions](https://github.com/google/cel-spec/blob/master/doc/langdef.md#string-functions), such as `startsWith()`, `contains()`, and `endsWith()`, are **disallowed** on high entropy fields. Check for equality on these fields instead. For example: ``` obj.id == "ep_123" ``` ### Query complexity (budgeting/limits) Very large expressions can stress the query engine. The service may enforce **limits on the number of conditions per query** or similar throttles in the future. ## Filterable resources and fields The initial release prioritizes the resource types and fields below. CEL filtering is not supported on deprecated endpoints. Field coverage is evolving and may change before GA. | Resource Type | Filterable Fields | | - | - | | **Endpoints** | - `id`
- `created_at`
- `description`
- `metadata`
- `principal.id`
- `type`
- `binding`
- `url`
- `pooling_enabled`
- `scheme`
- `region`
- `name` | | **Reserved Addresses** | - `id`
- `created_at`
- `description`
- `metadata`
- `addr`
- `region` | | **Reserved Domains** | - `id`
- `created_at`
- `description`
- `metadata`
- `domain`
- `region`
- `cname_target`
- `certificate.id`
- `acme_challenge_cname_target` | | **TLS Certificates** | - `id`
- `created_at`
- `description`
- `metadata`
- `subject_common_name`
- `not_after`
- `not_before`
- `serial_number` | | **Certificate Authorities** | - `id`
- `created_at`
- `description`
- `metadata`
- `subject_common_name`
- `not_before`
- `not_after` | | **IP Policies** | - `id`
- `created_at`
- `description`
- `metadata` | | **IP Policy Rules** | - `id`
- `created_at`
- `description`
- `metadata`
- `ip_policy`
- `cidr`
- `action` | | **Agent Ingress** | - `id`
- `created_at`
- `description`
- `metadata`
- `domain` | | **Tunnel Sessions** | - `id`
- `metadata`
- `agent_version`
- `ip`
- `os`
- `region`
- `started_at`
- `credential` | | **Event Destinations** | - `id`
- `created_at`
- `description`
- `metadata` | | **Event Subscriptions** | - `id`
- `created_at`
- `description`
- `metadata` | | **IP Restrictions** | - `id`
- `created_at`
- `description`
- `metadata` | | **API Keys** | - `id`
- `created_at`
- `description`
- `metadata`
- `owner_id` | | **SSH Credentials** | - `id`
- `created_at`
- `description`
- `metadata`
- `owner_id`
- `acl` | | **Credentials** | - `id`
- `created_at`
- `description`
- `metadata`
- `owner_id`
- `acl` | | **Service Users** | - `id`
- `created_at` | | **SSH Certificate Authorities** | - `id`
- `created_at`
- `description`
- `metadata` | | **Vaults** | - `id`
- `created_at`
- `description`
- `metadata`
- `name` | | **Secrets** | - `id`
- `created_at`
- `description`
- `metadata`
- `name` | ## Usage examples ### **Filter endpoints by type and creation time** ```http theme={null} GET /endpoints?filter=obj.type == "cloud" && obj.created_at < timestamp("2025-10-31T09:23:45-07:00") # or using helpers GET /endpoints?filter=obj.type == "cloud" && obj.created_at >= timestamp(time.now).subtract("6d") ``` Reference: * [`LIST /endpoints`](/docs/api-reference/endpoints/list) ### **Reserved domains by prefix** ```http theme={null} GET /reserved_domains?filter=obj.domain.startsWith("myapi.ngrok") ``` Reference: * [`LIST /reserved_domains`](/docs/api-reference/reserveddomains/list) ### **IP policy rules by CIDR and action** ```http theme={null} GET /ip_policy_rules?filter=obj.cidr.contains("1.1.0.0/16") && obj.action == "deny" ``` Reference: * [`LIST /ip_policy_rules`](/docs/api-reference/ippolicyrules/list) ### **Credentials by owner with optional empty ACL** ```http theme={null} GET /credentials?filter=obj.owner_id == "usr_2tEpN0yrxDI4j8jVnhVRoTNN2Tx" && (obj.acl == null || obj.acl == "") ``` Reference: * [`LIST /credentials`](/docs/api-reference/credentials/list) ### **Complex nesting** ```http theme={null} GET /agent_ingresses?filter=obj.domain in ["foo.com","bar.com","baz.com"] || (obj.created_at < timestamp("2025-05-10Z") && obj.description.contains("cowbell")) ``` Reference: * [`LIST /agent_ingresses`](/docs/api-reference/agentingresses/list) ## Error handling Invalid filters return **HTTP 400** with a structured error body (`category`, `status_code`, `message`, `details`). Example: ```http theme={null} HTTP/1.1 400 Bad Request Content-Type: application/json; charset=utf-8 Cache-Control: no-store { "error_code": "invalid_cel_expression", "status_code": 400, "msg": "Invalid CEL query: unsupported field: endpoint.idk (must be endpoint.url, endpoint.id, endpoint.type, or endpoint.bindings).", "details": { "operation_id": "op_k23j45n134jkasdfk34jkjnlkjuhasdf" } } ``` # ngrok API Overview Source: https://ngrok.com/docs/api/index Learn about the ngrok REST API for programmatic access to all ngrok resources including endpoints, domains, and configurations. ## Overview The ngrok API provides programmatic access to all of ngrok's resources. The API is REST-ish. It follows most of the conventions of a REST API but diverges slightly when the REST model does not fit well. The API is available at `https://api.ngrok.com`. The API listens only on port 443 to help avoid any accidental unencrypted requests. The API is designed to be simple enough to be called with `curl` and HTTP libraries in your programming language of choice. Other ways to interact with the API include native API client libraries, a native CLI and a Terraform Provider. #### Other ways to interact with the API * **[Agent API CLI](/docs/gateway/agent/cli-api/)**: Explore and script the API via the `ngrok api` commands included in the ngrok agent. * **[API Client Libraries](#client-libraries)**: Idiomatic API client libraries are published for all major programming languages * **[Terraform Provider](#terraform-provider)**: Use ngrok's Terraform provider to manage ngrok resources #### You may be looking for other programmatic ways to work with ngrok * **[Agent API](/docs/gateway/agent/api)**: The ngrok agent has its own API if you're trying to dynamically start and stop endpoints and tunnels * **[Agent SDKs](/docs/agent-sdks/)**: If you're looking to embed ngrok agent functionality as a library in your own software ## Quickstart Start by [generating a new API key](https://dashboard.ngrok.com/api-keys) in the ngrok Dashboard. ##### Access the API with curl ``` curl https://api.ngrok.com -H "authorization: Bearer {API Key}" -H "ngrok-version: 2" ``` ##### Access the API with the Agent CLI ``` ngrok config add-api-key "{API Key}" ngrok api endpoints list ``` ## Client libraries Open-source API client libraries are published to make working with the ngrok API feel native and fluent in your favorite programming language. Library repositories are published in the [ngrok github organization](https://github.com/ngrok). | Language | Installation | Documentation | Repository | | - | - | - | - | | Go | `go get github.com/ngrok/ngrok-api-go/v4` | [Documentation](https://pkg.go.dev/github.com/ngrok/ngrok-api-go/v4) | [GitHub](https://github.com/ngrok/ngrok-api-go) | | .NET | `dotnet add package NgrokApi` | [Documentation](https://github.com/ngrok/ngrok-api-dotnet) | [GitHub](https://github.com/ngrok/ngrok-api-dotnet) | | Ruby | `gem install ngrok-api` | [Documentation](https://ruby-api.docs.ngrok.com) | [GitHub](https://github.com/ngrok/ngrok-api-ruby) | | Python | `pip install ngrok-api` | [Documentation](https://python-api.docs.ngrok.com) | [GitHub](https://github.com/ngrok/ngrok-api-python) | | Java (unstable) | See the README for installing with Maven | [Documentation](https://java-api.docs.ngrok.com) | [GitHub](https://github.com/ngrok/ngrok-api-java) | | Scala (unstable) | See the README for installing with Maven | [Documentation](https://python-api.docs.ngrok.com) | [GitHub](https://github.com/ngrok/ngrok-api-scala) | ## Terraform provider When you use ngrok resources as part of production infrastructure, it is an industry best practice to define them using an infrastructure-as-code tool like [Terraform](https://www.terraform.io/). An official Terraform provider is published that consumes the ngrok API to manage your resources in this way. Consult the documentation for the [ngrok Terraform provider on Hashicorp's Terraform Registry.](https://registry.terraform.io/providers/ngrok/ngrok/latest/docs) #### example.tf ```hcl theme={null} # Configure the ngrok provider provider "ngrok" { api_key = "{API_KEY}" } # Provision an ngrok domain resource "ngrok_reserved_domain" "my_domain" { name = "my-domain.example.com" region = "us" certificate_management_policy { authority = "letsencrypt" private_key_type = "ecdsa" } } ``` ## Authentication | | | | - | - | | Base URL | `https://api.ngrok.com` | | Authentication Header | **`authorization: Bearer {API Key}`** | Requests to the API must include an API key as a bearer token in the `Authorization` header. * **[API Keys on the ngrok dashboard](https://dashboard.ngrok.com/api-keys)**: Provision your first API key from your ngrok dashboard. * **[API keys API Resource](/docs/api-reference/apikeys/list)**: Once you have an API key, you may provision additional API keys programmatically. ## Content types Requests to the API with a content body must set a `content-type` header of `application/json`. All responses returned by the API will use the `application/json` content type. ## Versioning and API stability Requests to the API must set the `ngrok-version` header. The latest version is `2`. All other versions are deprecated. The ngrok API guarantees that breaking changes to the API will never be made unless the caller explicitly opts in to a newer version. The following non-breaking changes to the API may be made to existing versions: * The addition of new resources * The addition of new methods to existing resources * The addition of new fields on existing resource representations * Bug fixes which change the API to match documented behavior ## Pagination List endpoints can be paginated using the query parameters `limit` and `before_id`. Results are returned ordered from newest to oldest. The maximum value of `limit` is 100. If a limit is not specified, it will default to 100. If `before_id` is not specified, the first page of results will be returned. You can provide an explicit value for `before_id` to retrieve items created before the given ID. Each response to a list request will include a `next_page_uri` field, which will be the full URL you can request to retrieve the next page of results. If there are no more results, `next_page_uri` will be `null`. ## Timestamps and timezones Timestamp fields in the ngrok API are represented as strings that are returned in the [RFC 3339 format](https://www.ietf.org/rfc/rfc3339.txt). Timestamps returned by the API are in UTC time which is indicated by a `Z` at the end. ## Rate limits The ngrok API enforces a limit on all accounts of 120 requests over a rolling 60 second window. If you exceed the rate limit, ngrok will return [ERR\_NGROK\_226](/docs/errors/err_ngrok_226) and an HTTP Status Code of 429. ## IP restrictions All API access requires an API key, but you may also further restrict which IPs are permitted to make requests to the API. You may define one or more IP Policies to configure which CIDR blocks may manipulate the API for your account. If you use SCIM with an IdP, you may need to add the IPs of your IdP as well because SCIM requests to the ngrok API are subject to these IP restrictions as well. IP Restrictions can be configured manually on the ngrok dashboard or programmatically via API with a `type` of `api`. * **[IP Restrictions on your ngrok dashboard](https://dashboard.ngrok.com/ip-restrictions)** * **[IP Restrictions API Resource](/docs/api-reference/iprestrictions/list/)** ## Errors It is important to provide a well-defined interface to errors you encounter when working with the ngrok API. Well-defined error surfaces and codes are what allow your software to appropriately handle error conditions. Errors returned by the ngrok API are serialized with the following example format: ``` { "error_code": "ERR_NGROK_218", "status_code": 400, "msg": "Your request has not specified an API version. Please include the version you wish to use in the Ngrok-Version header. Supported versions: 2.", "details": { "operation_id": "op_2RfSBcv0nsy71XCHWOegjx1OoKh" } } ``` Every error returned by the ngrok API includes a globally unique error code in the `error_code` property field. This error code is distinct for every possible error condition that can be encountered so your software can properly handle different error conditions. Consult the **[Error Reference Documentation](/docs/errors)** for the complete list of all error codes returned by ngrok. ## CORS The ngrok API supports CORS ([Cross Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)) so that you may initiate requests to it from Javascript running in a web browser on other domains. The response to CORS requests include the `Access-Control-Allow-Origin` and `Access-Control-Allow-Credentials` headers. The ngrok API identifies any request with an `OPTIONS` method and the headers `Origin` and `Access-Control-Request-Method` as a CORS preflight request. The API responds to CORS preflight requests as follows: * The API sets the `Access-Control-Allow-Origin` to match the requested `Origin`. * The API sets `Access-Control-Allow-Methods` to all supported methods for the requested resource. * The API echoes back all requested header from `Access-Control-Request-Headers` in `Access-Control-Allow-Headers` * The API responds with `Access-Control-Allow-Credentials: true` because the `Authorization` header is required for accessing API resources. * The API sets the `Access-Control-Max-Age` header to enable the preflight response to be cached for up to 10 minutes. ## API pricing The API is available to all ngrok users at no additional charge. You only incur costs if the resources provisioned by the API incur a cost. [API IP Restrictions](#ip-restrictions) is available on the Pay-as-you-go plan. # Errors Source: https://ngrok.com/docs/errors/index ## Philosophy When you encounter an error while using ngrok, we report a globally unique error code. Each error code corresponds to a specific location in ngrok's code which helps us quickly identify where something went wrong and help you know exactly how to fix it. When your report a bug or email us about a support issue, please include the unique error code if you have encountered one. It will help us diagnose and solve your issue much more quickly. ngrok's error codes were also designed so that you can paste them into your favorite search engine and immediately find a dedicated page with instructions on how to fix your problem. ## Programmatic Error Handling When working with ngrok's programmatically, we return these unique error codes in a structured way so that you can write software which appropriately handles different error conditions. * [API Error Handling](/docs/api/#errors) ## Common Errors You're more likely to run into some errors than others. The most common are listed here to help make your troubleshooting easier. | Code | Message | | - | - | | [ERR\_NGROK\_100](/docs/errors/err_ngrok_100) | Invalid metadata length | | [ERR\_NGROK\_108](/docs/errors/err_ngrok_108) | You've hit your account limit for simultaneous ngrok agent sessions. Try stopping an existing agent or upgrading your account. | | [ERR\_NGROK\_120](/docs/errors/err_ngrok_120) | Your ngrok agent version `VERSION` is no longer supported. Only the most recent version of the ngrok agent is supported without an account. Update to a newer version with `ngrok update` or by downloading from [https://download.ngrok.com](https://download.ngrok.com). Sign up for an account to avoid forced version upgrades: [https://ngrok.com/signup](https://ngrok.com/signup). | | [ERR\_NGROK\_1205](/docs/errors/err_ngrok_1205) | You failed to solve the catpcha, please try again. | | [ERR\_NGROK\_1226](/docs/errors/err_ngrok_1226) | You are disallowed from creating an ngrok account due to violation of the terms of service. | | [ERR\_NGROK\_3004](/docs/errors/err_ngrok_3004) | ngrok gateway error. The server returned an invalid or incomplete HTTP response. Try starting ngrok with the full upstream service URL (for example, ngrok http [https://localhost:8081](https://localhost:8081)) | | [ERR\_NGROK\_3200](/docs/errors/err_ngrok_3200) | Tunnel not found. This could be because your agent is not online or your tunnel has been flagged by our automated moderation system. | | [ERR\_NGROK\_3208](/docs/errors/err_ngrok_3208) | The account associated with this hostname has been banned.We've determined this account to be in violation of ngrok's terms of service.If you are the account owner and believe this is a mistake, please contact [support@ngrok.com](mailto:support@ngrok.com). | | [ERR\_NGROK\_4011](/docs/errors/err_ngrok_4011) | Your password must be at least 10 characters. | | [ERR\_NGROK\_4013](/docs/errors/err_ngrok_4013) | You may not create a new account because you are already a member of a free account. Upgrade or delete that account first before creating a new account. | | [ERR\_NGROK\_4100](/docs/errors/err_ngrok_4100) | The email or password you entered is not valid. | | [ERR\_NGROK\_4101](/docs/errors/err_ngrok_4101) | A user with the email address already exists. | | [ERR\_NGROK\_4108](/docs/errors/err_ngrok_4108) | Sign ups are disallowed for the email provider. Please sign up with a different email provider. | | [ERR\_NGROK\_6022](/docs/errors/err_ngrok_6022) | Before you can serve HTML content, you must sign up for an ngrok account and install your authtoken. | | [ERR\_NGROK\_6024](/docs/errors/err_ngrok_6024) | You are about to visit `HOSTPORT`, served by `SERVINGIP`. This website is served for free through ngrok.com. You should only visit this website if you trust whoever sent the link to you. | | [ERR\_NGROK\_8012](/docs/errors/err_ngrok_8012) | Traffic was successfully tunneled to the ngrok agent, but the agent failed to establish a connection to the upstream web service | ## All Errors Consult the [Errors Reference Documentation](/docs/errors/reference) for a complete list of all of ngrok's error codes. ## Customize Error Pages for your Traffic To access and provide feedback on a development preview of custom error pages [Fill out the form](https://ngrok.com/new-features/custom-error-pages?ref=errordoc). # Errors Reference Source: https://ngrok.com/docs/errors/reference Complete reference for all ngrok error codes. Find explanations, causes, and fixes for every ngrok tunnel, agent, and API error in one place. | Code | Message | | - | - | | [ERR\_NGROK\_100](/docs/errors/err_ngrok_100) | Invalid metadata length: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_102](/docs/errors/err_ngrok_102) | The last payment for the `ACCOUNT` account failed. | | [ERR\_NGROK\_103](/docs/errors/err_ngrok_103) | The account `ACCOUNT` has been suspended. | | [ERR\_NGROK\_105](/docs/errors/err_ngrok_105) | The authtoken you specified does not look like a proper ngrok authtoken. | | [ERR\_NGROK\_106](/docs/errors/err_ngrok_106) | The authtoken you specified is an ngrok v1 authtoken, but you're using ngrok v2. | | [ERR\_NGROK\_107](/docs/errors/err_ngrok_107) | The authtoken you specified is properly formed, but it is invalid. | | [ERR\_NGROK\_108](/docs/errors/err_ngrok_108) | Your account is limited to `MAX` simultaneous ngrok agent sessions. | | [ERR\_NGROK\_109](/docs/errors/err_ngrok_109) | The authentication payload you specified is not valid. | | [ERR\_NGROK\_110](/docs/errors/err_ngrok_110) | The session cookie you specified is not valid. | | [ERR\_NGROK\_111](/docs/errors/err_ngrok_111) | The heartbeat interval you specified is not valid. | | [ERR\_NGROK\_112](/docs/errors/err_ngrok_112) | The heartbeat tolerance you specified is not valid. | | [ERR\_NGROK\_115](/docs/errors/err_ngrok_115) | Your IP `IP` does not match the IP Policies specified by the Agent IP Restriction for this account. Authenticate from an IP in the correct range or update your Agent IP Restrictions from the ngrok dashboard. [https://dashboard.ngrok.com/ip-restrictions](https://dashboard.ngrok.com/ip-restrictions) | | [ERR\_NGROK\_116](/docs/errors/err_ngrok_116) | Your IP `IP` does not match the IP ACL policy specified for your credentials. Authenticate from an IP in the correct range or make sure you are using the correct credentials | | [ERR\_NGROK\_118](/docs/errors/err_ngrok_118) | Your account `ACCOUNTNAME` is not permitted to use the ngrok enterprise tunnel ingress. | | [ERR\_NGROK\_119](/docs/errors/err_ngrok_119) | The agent specified an invalid semantic version string: `VERSION` | | [ERR\_NGROK\_121](/docs/errors/err_ngrok_121) | Your `PRODUCT` version "`VERSION`" is too old. The minimum supported agent version for your account is "`MINVERSION`". Update to a newer version with 'ngrok update', by downloading from [https://ngrok.com/download](https://ngrok.com/download), or by updating your SDK version. Paid accounts are currently excluded from minimum agent version requirements. To begin handling traffic immediately without updating your agent, upgrade to a paid plan: [https://dashboard.ngrok.com/billing/choose-a-plan](https://dashboard.ngrok.com/billing/choose-a-plan). | | [ERR\_NGROK\_122](/docs/errors/err_ngrok_122) | Your account is not permitted to use the agent ingress "`DOMAIN`". | | [ERR\_NGROK\_123](/docs/errors/err_ngrok_123) | The account "`NAME`" may not start an ngrok agent session until the admin's email address is verified. Verify your email at [https://dashboard.ngrok.com/user/settings](https://dashboard.ngrok.com/user/settings) | | [ERR\_NGROK\_124](/docs/errors/err_ngrok_124) | Your agent provided an invalid UserAgent: `ERR` | | [ERR\_NGROK\_125](/docs/errors/err_ngrok_125) | The analytics payload you specified is not valid. | | [ERR\_NGROK\_126](/docs/errors/err_ngrok_126) | The agent ingress "`DOMAIN`" is not available via "`IP`". | | [ERR\_NGROK\_127](/docs/errors/err_ngrok_127) | Your account is not permitted to use the agent ingress via "`IP`". | | [ERR\_NGROK\_128](/docs/errors/err_ngrok_128) | Something went wrong. Please try your request again later. | | [ERR\_NGROK\_200](/docs/errors/err_ngrok_200) | The ngrok API requires that you set the Authorization header for authentication. Your API keys and instructions are available on your dashboard: [https://dashboard.ngrok.com/api-keys](https://dashboard.ngrok.com/api-keys) | | [ERR\_NGROK\_201](/docs/errors/err_ngrok_201) | The ngrok API requires that you use Basic or Bearer authentication via the Authorization header. It could not parse the header in this request. API keys and instructions are available on your dashboard: [https://dashboard.ngrok.com/api-keys](https://dashboard.ngrok.com/api-keys) | | [ERR\_NGROK\_202](/docs/errors/err_ngrok_202) | The API authentication you specified does not look like a valid credential. API keys and instructions are available on your dashboard: [https://dashboard.ngrok.com/api-keys](https://dashboard.ngrok.com/api-keys) | | [ERR\_NGROK\_203](/docs/errors/err_ngrok_203) | The API authentication you specified is properly formed, but it is invalid. API keys and instructions are available on your dashboard: [https://dashboard.ngrok.com/api-keys](https://dashboard.ngrok.com/api-keys) | | [ERR\_NGROK\_204](/docs/errors/err_ngrok_204) | This is a valid API token, but the account `ACCOUNT` is not allowed to use the ngrok API. | | [ERR\_NGROK\_205](/docs/errors/err_ngrok_205) | The account `ACCOUNT` has been suspended. This is usually the result of violating the ngrok Terms of Service. Email [support@ngrok.com](mailto:support@ngrok.com) if you think your suspension is an error. | | [ERR\_NGROK\_206](/docs/errors/err_ngrok_206) | The authentication you specified is actually an authtoken. Check your records for an API key. API keys and instructions are available on your dashboard: [https://dashboard.ngrok.com/api-keys](https://dashboard.ngrok.com/api-keys) | | [ERR\_NGROK\_207](/docs/errors/err_ngrok_207) | The authentication you specified is actually an authtoken, not an API key token. Your key: `TOKEN`. Check your records for an API key with the form FIRSTPART\_SECONDPART. API keys and instructions are available on your dashboard: [https://dashboard.ngrok.com/api-keys](https://dashboard.ngrok.com/api-keys) | | [ERR\_NGROK\_208](/docs/errors/err_ngrok_208) | The authentication you specified is actually an API key ID, not an API key token. Your credential: `TOKEN`. Check your records for an API key with the form FIRSTPART\_SECONDPART. API keys and instructions are available on your dashboard: [https://dashboard.ngrok.com/api-keys](https://dashboard.ngrok.com/api-keys) | | [ERR\_NGROK\_210](/docs/errors/err_ngrok_210) | The content type you specified `CTYPE` is not supported by the API. Check your API client implementation and see the list of supported content types: [https://ngrok.com/docs/api#content-types](https://ngrok.com/docs/api#content-types) | | [ERR\_NGROK\_211](/docs/errors/err_ngrok_211) | The `METHOD` request you made does not expect a body, but you provided one. Check your API client implementation and review the API documentation: [https://ngrok.com/docs/api-reference/](https://ngrok.com/docs/api-reference/). | | [ERR\_NGROK\_212](/docs/errors/err_ngrok_212) | The server was unable to read the complete request body. Check your API client implementation and review the API documentation: [https://ngrok.com/docs/api-reference/](https://ngrok.com/docs/api-reference/). | | [ERR\_NGROK\_213](/docs/errors/err_ngrok_213) | The `CTYPE` request body could not be parsed. Check your API client implementation and review the API documentation: [https://ngrok.com/docs/api-reference/](https://ngrok.com/docs/api-reference/). | | [ERR\_NGROK\_214](/docs/errors/err_ngrok_214) | The request parameter `NAME` is unknown and not expected. The supported fields are: `SUPPORTED`. Check your API client implementation and review the API documentation: [https://ngrok.com/docs/api-reference/](https://ngrok.com/docs/api-reference/). | | [ERR\_NGROK\_215](/docs/errors/err_ngrok_215) | The value provided for the request parameter `NAME` is invalid. Check your request and review the API documentation: [https://ngrok.com/docs/api-reference/](https://ngrok.com/docs/api-reference/). | | [ERR\_NGROK\_216](/docs/errors/err_ngrok_216) | You did not provide a Content-Type with your request. Check your API client implementation and use one of the supported content types: [https://ngrok.com/docs/api#content-types](https://ngrok.com/docs/api#content-types) | | [ERR\_NGROK\_217](/docs/errors/err_ngrok_217) | The provided API version `VERSION` is invalid or unsupported. Supported versions: `SUPPORTED`. Check your API client implementation. | | [ERR\_NGROK\_218](/docs/errors/err_ngrok_218) | Your request has not specified an API version. Include the version you wish to use in the Ngrok-Version header. Supported versions: `SUPPORTED`. | | [ERR\_NGROK\_219](/docs/errors/err_ngrok_219) | `TLSVERSION` is not a supported TLS version. Supported versions: `SUPPORTED`. | | [ERR\_NGROK\_220](/docs/errors/err_ngrok_220) | Your IP does not match the IP Policy for this Account. Authenticate from an IP in the correct range or update your IP Restrictions from the ngrok dashboard. [https://dashboard.ngrok.com/ip-restrictions](https://dashboard.ngrok.com/ip-restrictions) | | [ERR\_NGROK\_221](/docs/errors/err_ngrok_221) | This is a valid API token, but the account `ACCOUNT` is not authorized to submit or view abuse reports. Email [support@ngrok.com](mailto:support@ngrok.com) if you believe this is an error. | | [ERR\_NGROK\_223](/docs/errors/err_ngrok_223) | `ID` is not a valid resource reference identifier. | | [ERR\_NGROK\_224](/docs/errors/err_ngrok_224) | The value `VAL` is not valid for the terminate\_at property of the TLS Termination endpoint configuration module. Must be either 'edge', 'agent' or 'upstream'. | | [ERR\_NGROK\_225](/docs/errors/err_ngrok_225) | Your account is limited to `MAX` simultaneous API requests. | | [ERR\_NGROK\_226](/docs/errors/err_ngrok_226) | Your account is rate limited to `MAX` API requests per `INTERVAL`. | | [ERR\_NGROK\_227](/docs/errors/err_ngrok_227) | The ID `VAL` is invalid. Expected an ID with a prefix of `PREFIX`. | | [ERR\_NGROK\_228](/docs/errors/err_ngrok_228) | The ID `VAL` is not a valid Certificate Authority ID. | | [ERR\_NGROK\_229](/docs/errors/err_ngrok_229) | The ID `VAL` is not a valid IP Policy ID. | | [ERR\_NGROK\_230](/docs/errors/err_ngrok_230) | The query part of the requested URI is invalid: `ERR`. Check your API client implementation and review the API documentation: [https://ngrok.com/docs/api-reference/](https://ngrok.com/docs/api-reference/). | | [ERR\_NGROK\_231](/docs/errors/err_ngrok_231) | The query part of the requested URI includes unknown parameters: `UNKNOWN`. The supported parameters are: `SUPPORTED`. Check your API client implementation and review the API documentation: [https://ngrok.com/docs/api-reference/](https://ngrok.com/docs/api-reference/). | | [ERR\_NGROK\_232](/docs/errors/err_ngrok_232) | The request must specify only a single value for the `NAME` parameter but got `COUNT`: `VALUES`. Check your API client implementation and review the API documentation: [https://ngrok.com/docs/api-reference/](https://ngrok.com/docs/api-reference/). | | [ERR\_NGROK\_233](/docs/errors/err_ngrok_233) | The page size limit must be between 1 and `MAX` but `VAL` was provided. | | [ERR\_NGROK\_235](/docs/errors/err_ngrok_235) | The Format `VAL` is missing or is not a valid Event Destination Format. Supported formats are: `SUPPORTED`. | | [ERR\_NGROK\_236](/docs/errors/err_ngrok_236) | The destination target is invalid. Expected fields include one of the following: `TARGETS` | | [ERR\_NGROK\_238](/docs/errors/err_ngrok_238) | The provided event type: `TYP` is not valid. Valid event types are `ID` | | [ERR\_NGROK\_239](/docs/errors/err_ngrok_239) | The provided field: `TYP` is not valid. | | [ERR\_NGROK\_240](/docs/errors/err_ngrok_240) | The provided event destination auth is invalid. | | [ERR\_NGROK\_241](/docs/errors/err_ngrok_241) | The value "`TS`" is not a valid RFC3339 timestamp. Error while parsing: "`ERR`". | | [ERR\_NGROK\_242](/docs/errors/err_ngrok_242) | The ID `VAL` is not a valid Backend ID. | | [ERR\_NGROK\_243](/docs/errors/err_ngrok_243) | The ID `VAL` is invalid. Expected an ID with prefix one of `PREFIXES`. | | [ERR\_NGROK\_244](/docs/errors/err_ngrok_244) | The page size limit must be a number between 1 and `MAX` but `VAL` was provided. | | [ERR\_NGROK\_247](/docs/errors/err_ngrok_247) | The account `ACCOUNT` has been suspended for non-payment. Update your payment method to pay your outstanding bill and resume service: [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) | | [ERR\_NGROK\_248](/docs/errors/err_ngrok_248) | The endpoint create request passed an invalid type of `TYPE`. Currently we only support creation of cloud endpoints on this route. | | [ERR\_NGROK\_249](/docs/errors/err_ngrok_249) | The endpoint URL provided is not a valid URL: `URL`. Ensure the URL has a proper scheme, domain, and if a TCP url, port. | | [ERR\_NGROK\_250](/docs/errors/err_ngrok_250) | Invalid CEL query: `DETAIL` | | [ERR\_NGROK\_251](/docs/errors/err_ngrok_251) | `DETAIL` | | [ERR\_NGROK\_252](/docs/errors/err_ngrok_252) | `REASON` | | [ERR\_NGROK\_253](/docs/errors/err_ngrok_253) | `REASON` | | [ERR\_NGROK\_254](/docs/errors/err_ngrok_254) | `REASON` | | [ERR\_NGROK\_255](/docs/errors/err_ngrok_255) | `REASON` | | [ERR\_NGROK\_256](/docs/errors/err_ngrok_256) | Only the access token's owner or an account admin can change it. | | [ERR\_NGROK\_300](/docs/errors/err_ngrok_300) | The authtoken credential `ID` has been revoked | | [ERR\_NGROK\_301](/docs/errors/err_ngrok_301) | The bind payload you specified is not valid. | | [ERR\_NGROK\_307](/docs/errors/err_ngrok_307) | You must reserve an address for your account before it can be used. | | [ERR\_NGROK\_308](/docs/errors/err_ngrok_308) | The credential ACL policy does not permit binding this address. | | [ERR\_NGROK\_309](/docs/errors/err_ngrok_309) | This address is reserved for another account. | | [ERR\_NGROK\_310](/docs/errors/err_ngrok_310) | Only Hobbyist and Pay-as-you-go plans may create endpoints with reserved addresses. | | [ERR\_NGROK\_311](/docs/errors/err_ngrok_311) | This address is allocated for a different region. | | [ERR\_NGROK\_312](/docs/errors/err_ngrok_312) | Failed to create a TLS endpoint for the account `ACCOUNT`. | | [ERR\_NGROK\_313](/docs/errors/err_ngrok_313) | Only paid plans may create endpoints with custom subdomains. | | [ERR\_NGROK\_314](/docs/errors/err_ngrok_314) | Only paid plans may create endpoints with custom hostnames. | | [ERR\_NGROK\_315](/docs/errors/err_ngrok_315) | Wildcard domains are a feature reserved for ngrok's Pay-as-you-go plan. | | [ERR\_NGROK\_316](/docs/errors/err_ngrok_316) | The credential ACL policy does not permit binding this name. | | [ERR\_NGROK\_317](/docs/errors/err_ngrok_317) | You cannot create an endpoint with the domain `DOMAIN` in region | | [ERR\_NGROK\_318](/docs/errors/err_ngrok_318) | You must reserve a wildcard domain for your account before you can use it to create endpoints. | | [ERR\_NGROK\_319](/docs/errors/err_ngrok_319) | You must reserve a custom hostname for your account before it can be used to create endpoints. | | [ERR\_NGROK\_320](/docs/errors/err_ngrok_320) | This domain is reserved for another account. | | [ERR\_NGROK\_321](/docs/errors/err_ngrok_321) | Wildcard names must be reserved exactly. | | [ERR\_NGROK\_322](/docs/errors/err_ngrok_322) | This name is reserved in a different region. | | [ERR\_NGROK\_324](/docs/errors/err_ngrok_324) | Your account may not run more than `MAX` endpoints over a single ngrok agent session. | | [ERR\_NGROK\_326](/docs/errors/err_ngrok_326) | Invalid character `CHAR` in domain name. Valid domains may contain only a-z, A-Z, 0-9, . \* and - characters. Failed to create an endpoint with the domain `DOMAIN`. | | [ERR\_NGROK\_327](/docs/errors/err_ngrok_327) | Domain has an invalid character sequence. Valid domains may not contain repeated periods '..'. Failed to create an endpoint with the domain `DOMAIN`. | | [ERR\_NGROK\_328](/docs/errors/err_ngrok_328) | Domain part `PART` has an invalid first character. Valid domain parts must begin with a-z, A-Z, 0-9 or \*. Failed to create an endpoint with the domain `DOMAIN`. | | [ERR\_NGROK\_329](/docs/errors/err_ngrok_329) | Domain part `PART` has an invalid last character. Valid domains must end with a-z A-Z or 0-9. Failed to create an endpoint with the domain `DOMAIN`. | | [ERR\_NGROK\_330](/docs/errors/err_ngrok_330) | Domain has an invalid character sequence. Valid domains may not contain '--' in positions 3 and 4 unless the domain has a punycode prefix of 'xn--'. Failed to create an endpoint with the domain `DOMAIN`. | | [ERR\_NGROK\_331](/docs/errors/err_ngrok_331) | You may not create an endpoint using a domain with the TLD `SUFFIX`. This TLD is not reachable over the public internet. Failed to create an endpoint with the domain `DOMAIN`. | | [ERR\_NGROK\_332](/docs/errors/err_ngrok_332) | `PROTO` is not a supported tunnel protocol | | [ERR\_NGROK\_333](/docs/errors/err_ngrok_333) | No tunnel found with the ID `ID` | | [ERR\_NGROK\_334](/docs/errors/err_ngrok_334) | The endpoint `URL` is already online. Either | | [ERR\_NGROK\_335](/docs/errors/err_ngrok_335) | The bind cookie you specified is malformed. This usually indicates a bug in the client's protocol implementation. | | [ERR\_NGROK\_336](/docs/errors/err_ngrok_336) | Could not find an account while creating this endpoint. This is either an internal server error or you deleted your account. | | [ERR\_NGROK\_337](/docs/errors/err_ngrok_337) | The last payment for the `ACCOUNT` account failed. | | [ERR\_NGROK\_338](/docs/errors/err_ngrok_338) | The account `ACCOUNT` has been suspended. | | [ERR\_NGROK\_341](/docs/errors/err_ngrok_341) | IP Policy with ID `ID` not found | | [ERR\_NGROK\_343](/docs/errors/err_ngrok_343) | Endpoint configuration `ID` does not allow the agent to set basic authentication. | | [ERR\_NGROK\_344](/docs/errors/err_ngrok_344) | Endpoint configuration `ID` does not allow clients to set an IP policy. | | [ERR\_NGROK\_345](/docs/errors/err_ngrok_345) | Endpoint configuration `ID`s HTTPS module terminates TLS which is incompatible with a 'tls' tunnel. You may either: | | [ERR\_NGROK\_346](/docs/errors/err_ngrok_346) | Endpoint configuration `ID`s HTTPS module does not terminate TLS which is incompatible with a 'https' tunnel. You may either: | | [ERR\_NGROK\_347](/docs/errors/err_ngrok_347) | Domain has invalid punycode: `IDNA_ERROR`. Failed to create an endpoint with the domain `DOMAIN`. | | [ERR\_NGROK\_348](/docs/errors/err_ngrok_348) | Your account is limited to `MAX` sessions. | | [ERR\_NGROK\_349](/docs/errors/err_ngrok_349) | Your account is rate limited for adding `MAX` sessions per `INTERVAL`. | | [ERR\_NGROK\_350](/docs/errors/err_ngrok_350) | Your account is limited to `MAX` endpoints in a session. | | [ERR\_NGROK\_351](/docs/errors/err_ngrok_351) | Your account is rate limited for adding `MAX` endpoints per `INTERVAL`. | | [ERR\_NGROK\_354](/docs/errors/err_ngrok_354) | Failed to create an endpoint with the domain `DOMAIN`. Nested subdomains of an ngrok base domain `ENDPOINT_DOMAIN` must be reserved first. Alternatively, try `SUGGESTION` instead. | | [ERR\_NGROK\_355](/docs/errors/err_ngrok_355) | Invalid '\_' in domain name. Valid domains may not contain underscores. Failed to create an endpoint with the domain `DOMAIN`. | | [ERR\_NGROK\_356](/docs/errors/err_ngrok_356) | The credential ACL policy does not permit binding random TCP addresses. | | [ERR\_NGROK\_358](/docs/errors/err_ngrok_358) | Your account doesn't have permission to create labeled endpoints. | | [ERR\_NGROK\_359](/docs/errors/err_ngrok_359) | Invalid basic auth credential. Username must not be zero length. | | [ERR\_NGROK\_360](/docs/errors/err_ngrok_360) | Invalid basic auth credential. Password must be between `MINLENGTH` and `MAXLENGTH` characters, got `GOTLENGTH`. | | [ERR\_NGROK\_361](/docs/errors/err_ngrok_361) | Invalid circuit breaker configuration, error threshold percentage must be between 0.0 and 1.0 inclusive, was `VAL`. | | [ERR\_NGROK\_362](/docs/errors/err_ngrok_362) | Invalid IP restriction configuration, invalid CIDR: `CIDR` | | [ERR\_NGROK\_363](/docs/errors/err_ngrok_363) | You must specify a supported provider name. Supported providers: \[`SUPPORTED`] | | [ERR\_NGROK\_364](/docs/errors/err_ngrok_364) | Webhook provider `PROVIDER` is not supported. Supported providers: \[`SUPPORTED`] | | [ERR\_NGROK\_365](/docs/errors/err_ngrok_365) | OAuth provider `PROVIDER` is not supported. Supported providers: \[`SUPPORTED`] | | [ERR\_NGROK\_366](/docs/errors/err_ngrok_366) | You may not authorize more than `MAX` emails. Got `GOT`. | | [ERR\_NGROK\_367](/docs/errors/err_ngrok_367) | You may not authorize more than `MAX` email domains. Got `GOT`. | | [ERR\_NGROK\_368](/docs/errors/err_ngrok_368) | You may not request more than `MAX` oauth scopes. Got `GOT`. | | [ERR\_NGROK\_370](/docs/errors/err_ngrok_370) | Added request header should be in key:value format, got `VAL` | | [ERR\_NGROK\_371](/docs/errors/err_ngrok_371) | Added response header should be in key:value format, got `VAL` | | [ERR\_NGROK\_372](/docs/errors/err_ngrok_372) | `KEY` is not a valid HTTP header name because it contains at least one invalid character. | | [ERR\_NGROK\_373](/docs/errors/err_ngrok_373) | `KEY` is not a valid HTTP header name because it contains at least one invalid character. | | [ERR\_NGROK\_374](/docs/errors/err_ngrok_374) | `VAL` is not a valid HTTP header value: `REASON`. | | [ERR\_NGROK\_375](/docs/errors/err_ngrok_375) | Duplicate 'add' header, `HEADER` was provided twice with different casings: `CASE_ONE` and `CASE_TWO`. | | [ERR\_NGROK\_376](/docs/errors/err_ngrok_376) | Exceeded the maximum number of added headers. You specified `VAL`, the maximum is `MAX`. | | [ERR\_NGROK\_377](/docs/errors/err_ngrok_377) | The header beginning with `PREFIX`...' exceeds the maximum header name length of `BYTES` bytes. | | [ERR\_NGROK\_378](/docs/errors/err_ngrok_378) | The header value for `HEADERKEY` beginning with `PREFIX`...' exceeds the maximum header value length of `BYTES` bytes. | | [ERR\_NGROK\_379](/docs/errors/err_ngrok_379) | The sendgrid verification key is not a base64 encoded ecdsa public key. The following error was encountered while trying to parse it: "`ERROR`" | | [ERR\_NGROK\_380](/docs/errors/err_ngrok_380) | The resource `ID` was referenced, but not found. Retry, and contact support if this error persists. | | [ERR\_NGROK\_381](/docs/errors/err_ngrok_381) | The host `HOST` and port `PORT` you requested is already reserved by an edge. | | [ERR\_NGROK\_382](/docs/errors/err_ngrok_382) | The account "`NAME`" may not start an endpoint until the admin's email address is verified. Verify your email at [https://dashboard.ngrok.com/user/settings](https://dashboard.ngrok.com/user/settings) | | [ERR\_NGROK\_383](/docs/errors/err_ngrok_383) | You may not specify an OAuth Client ID without also specifying a Client Secret. Specify both or neither. | | [ERR\_NGROK\_384](/docs/errors/err_ngrok_384) | You may not specify an OAuth Client Secret without also specifying a Client ID. Specify both or neither. | | [ERR\_NGROK\_385](/docs/errors/err_ngrok_385) | You must specify an OIDC Client ID. | | [ERR\_NGROK\_386](/docs/errors/err_ngrok_386) | You must specify an OIDC Client Secret. | | [ERR\_NGROK\_387](/docs/errors/err_ngrok_387) | You must specify the OIDC Issuer URL. | | [ERR\_NGROK\_388](/docs/errors/err_ngrok_388) | You may not request more than `MAX` OIDC scopes. Got `GOT`. | | [ERR\_NGROK\_389](/docs/errors/err_ngrok_389) | You must specify an OAuth client ID and secret for a custom application or neither client ID nor a secret for the ngrok-managed OAuth application. | | [ERR\_NGROK\_390](/docs/errors/err_ngrok_390) | You must use your own OAuth application, specifying its client ID and secret, in order to specify scopes. | | [ERR\_NGROK\_391](/docs/errors/err_ngrok_391) | You may not request an individual OAuth scope with length greater than `MAX`. Got scope `SCOPE` with length `GOT`. | | [ERR\_NGROK\_392](/docs/errors/err_ngrok_392) | You may not request an individual OIDC scope with length greater than `MAX`. Got scope `SCOPE` with length `GOT`. | | [ERR\_NGROK\_393](/docs/errors/err_ngrok_393) | When specifying CIDRs to deny, you must specify at least one CIDR to allow. Otherwise all traffic will be denied. Use 0.0.0.0/0 and ::0/0 to accept all traffic not explicitly blocked by your deny rules. | | [ERR\_NGROK\_394](/docs/errors/err_ngrok_394) | Each tunnel must specify either a protocol or labels, but neither was set. | | [ERR\_NGROK\_395](/docs/errors/err_ngrok_395) | Bind rules in authtoken ACLs are not supported with labeled endpoints at this time. | | [ERR\_NGROK\_396](/docs/errors/err_ngrok_396) | Domain `DOMAIN` is not a valid DNS name. No domain part may be more than 63 characters long. | | [ERR\_NGROK\_397](/docs/errors/err_ngrok_397) | Domain `DOMAIN` is not a valid DNS name. Domains may not be more than 253 characters long. | | [ERR\_NGROK\_398](/docs/errors/err_ngrok_398) | The `ID` header cannot be removed without specifying a replacement. | | [ERR\_NGROK\_399](/docs/errors/err_ngrok_399) | The credential ACL policy does not permit binding this label. | | [ERR\_NGROK\_400](/docs/errors/err_ngrok_400) | The region you specified, `REGION` is invalid. Use one of the following acceptable values: `VALID` | | [ERR\_NGROK\_401](/docs/errors/err_ngrok_401) | Only accounts on paid plans can reserve domains. | | [ERR\_NGROK\_402](/docs/errors/err_ngrok_402) | Only the first part of a reserved domain may be a '\*' wildcard. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_403](/docs/errors/err_ngrok_403) | The first subdomain of a wildcard domain must be a single '\*' character. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_404](/docs/errors/err_ngrok_404) | You may not reserve names on ngrok.com. Use an ngrok.io suffix. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_405](/docs/errors/err_ngrok_405) | You may not reserve a domain with the suffix `SUFFIX` in the `WANT` region because names with this suffix must be reserved for the `HAVE` region. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_406](/docs/errors/err_ngrok_406) | Invalid character `CHAR` in domain name. Valid domains may contain only a-z, A-Z, 0-9, . \* and - characters. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_407](/docs/errors/err_ngrok_407) | Domain part `PART` has an invalid first character. Valid domain parts must begin with a-z, A-Z, 0-9 or \*. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_408](/docs/errors/err_ngrok_408) | Domain part `PART` has an invalid last character. Valid domains must end with a-z A-Z or 0-9. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_409](/docs/errors/err_ngrok_409) | Domain has an invalid character sequence. Valid domains may not contain repeated periods '..'. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_410](/docs/errors/err_ngrok_410) | Domain has an invalid character sequence. Valid domains may not contain '--' in positions 3 and 4 unless the domain has a punycode prefix of 'xn--'. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_411](/docs/errors/err_ngrok_411) | You may not reserve a name on the TLD `SUFFIX`. This TLD is not reachable over the public internet. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_412](/docs/errors/err_ngrok_412) | Only accounts on paid plans can reserve endpoints on custom domains. | | [ERR\_NGROK\_413](/docs/errors/err_ngrok_413) | This domain is already reserved for your account. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_414](/docs/errors/err_ngrok_414) | This domain is already reserved for another account. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_415](/docs/errors/err_ngrok_415) | Your account is limited to `MAX` reserved domains. If you need more domains, upgrade to the Pay-as-you-go plan at: [https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo](https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo). | | [ERR\_NGROK\_416](/docs/errors/err_ngrok_416) | You may not register a wildcard for all `SUFFIX` domains. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_417](/docs/errors/err_ngrok_417) | This wildcard domain would conflict with a domain reserved for another account. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_418](/docs/errors/err_ngrok_418) | Your account is limited to `MAX` wildcard domains. Email [sales@ngrok.com](mailto:sales@ngrok.com) to purchase additional wildcard domains. | | [ERR\_NGROK\_419](/docs/errors/err_ngrok_419) | Only Pay-as-you-go accounts can reserve wildcard domains. | | [ERR\_NGROK\_420](/docs/errors/err_ngrok_420) | A description is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_421](/docs/errors/err_ngrok_421) | Metadata is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_422](/docs/errors/err_ngrok_422) | The reserved domain name update failed because no values were provided. Specify at least one value. | | [ERR\_NGROK\_423](/docs/errors/err_ngrok_423) | The reserved domain name is limited to `MAX` characters; you are using `VAL` characters. | | [ERR\_NGROK\_424](/docs/errors/err_ngrok_424) | Domain `DOMAIN` does not require a CNAME record | | [ERR\_NGROK\_425](/docs/errors/err_ngrok_425) | Domain `DOMAIN` CNAME record not found | | [ERR\_NGROK\_426](/docs/errors/err_ngrok_426) | Domain `DOMAIN` CNAME record not found, A record `IPS` found | | [ERR\_NGROK\_427](/docs/errors/err_ngrok_427) | Domain `DOMAIN` CNAME record resolved to `HAVE`, should have been `WANT` | | [ERR\_NGROK\_428](/docs/errors/err_ngrok_428) | You may not assign endpoint configuration `ID` because it is type `TYPE`. You must assign a configuration with type `EXPECTEDTYPE`. | | [ERR\_NGROK\_429](/docs/errors/err_ngrok_429) | Domain has invalid punycode: `IDNA_ERROR`. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_430](/docs/errors/err_ngrok_430) | Another account is running an active tunnel on the domain `DOMAIN`. You may not reserve domains that are actively in use. | | [ERR\_NGROK\_431](/docs/errors/err_ngrok_431) | Your account is limited to `MAX` reserved domains. If you need more domains, you can upgrade to a Pay-as-you-go plan at: [https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo](https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo). | | [ERR\_NGROK\_432](/docs/errors/err_ngrok_432) | Your account is rate limited for adding `MAX` reserved domains per `INTERVAL`. | | [ERR\_NGROK\_433](/docs/errors/err_ngrok_433) | Your account is limited to `MAX` reserved wildcard domains. If you need more domains, you can upgrade to a Pay-as-you-go plan at: [https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo](https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo). | | [ERR\_NGROK\_434](/docs/errors/err_ngrok_434) | Either a certificate or a certificate management policy may be specified, not both | | [ERR\_NGROK\_435](/docs/errors/err_ngrok_435) | Invalid authority specified in managed certificate policy. | | [ERR\_NGROK\_436](/docs/errors/err_ngrok_436) | The certificate of a reserved domain with a managed certificate policy can not be detached directly, instead the managed certificate policy itself should be detached. | | [ERR\_NGROK\_438](/docs/errors/err_ngrok_438) | Invalid '\_' in domain name. Valid domains may not contain underscores. Failed to reserve `DOMAIN`. | | [ERR\_NGROK\_439](/docs/errors/err_ngrok_439) | The certificate\_management\_policy field must be null for domains owned by ngrok. | | [ERR\_NGROK\_440](/docs/errors/err_ngrok_440) | Uploaded certificates are not allowed for domains owned by ngrok. | | [ERR\_NGROK\_441](/docs/errors/err_ngrok_441) | Domain `DOMAIN` does not require an ACME Challenge CNAME record | | [ERR\_NGROK\_442](/docs/errors/err_ngrok_442) | Domain `RECORD` ACME Challenge CNAME record not found | | [ERR\_NGROK\_443](/docs/errors/err_ngrok_443) | ACME Challenge `RECORD` CNAME record not found, A record `IPS` found | | [ERR\_NGROK\_444](/docs/errors/err_ngrok_444) | ACME Challenge `RECORD` CNAME record resolved to `HAVE`, should have been `WANT` | | [ERR\_NGROK\_445](/docs/errors/err_ngrok_445) | There was an error looking up the DNS `RTYPE` record for domain `DOMAIN` (status: `STATUS`): `MSG` | | [ERR\_NGROK\_446](/docs/errors/err_ngrok_446) | The domain `DOMAIN` could not be deleted because it is still referenced by an edge or cloud endpoint. | | [ERR\_NGROK\_447](/docs/errors/err_ngrok_447) | Managed certificates are not allowed on direct subdomains of `DOMAIN`. | | [ERR\_NGROK\_448](/docs/errors/err_ngrok_448) | The domain `DOMAIN` is not a fully qualified domain name. | | [ERR\_NGROK\_449](/docs/errors/err_ngrok_449) | You may only specify one of 'name' or 'domain'. | | [ERR\_NGROK\_450](/docs/errors/err_ngrok_450) | You are not allowed to reserve subdomains of `DOMAIN`. Failed to reserve domain `SUBDOMAIN`. | | [ERR\_NGROK\_451](/docs/errors/err_ngrok_451) | You are not allowed to change the region of `DOMAIN`. | | [ERR\_NGROK\_455](/docs/errors/err_ngrok_455) | This subdomain is covered by `WILDCARD_DOMAIN` however the wildcard's CNAME record resolved to `HAVE`, should have been `WANT`. | | [ERR\_NGROK\_456](/docs/errors/err_ngrok_456) | You are not allowed to specify duplicate regions. Region `DUPLICATE` was specified multiple times. | | [ERR\_NGROK\_457](/docs/errors/err_ngrok_457) | Custom error template URLs are limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_458](/docs/errors/err_ngrok_458) | You are not allowed to delete your dev domain. | | [ERR\_NGROK\_459](/docs/errors/err_ngrok_459) | You may not change Region and IP resolution for this domain. | | [ERR\_NGROK\_460](/docs/errors/err_ngrok_460) | You may not change Region and IP resolution for domains in this account. To access this functionality, upgrade to the Pay-as-you-go plan at: [https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo](https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo) | | [ERR\_NGROK\_461](/docs/errors/err_ngrok_461) | Your account is limited to `MAX` domain(s) with custom Region and IP resolution. Your account has `COUNT` domain(s) with custom Region and IP resolution. | | [ERR\_NGROK\_462](/docs/errors/err_ngrok_462) | Region and IP resolution is not available for this domain. The DNS name `NAME` has records that ngrok does not manage. | | [ERR\_NGROK\_500](/docs/errors/err_ngrok_500) | The region you specified, `REGION` is invalid. Use one of the following acceptable values: `VALID` | | [ERR\_NGROK\_501](/docs/errors/err_ngrok_501) | Only paid accounts can reserve addresses. | | [ERR\_NGROK\_502](/docs/errors/err_ngrok_502) | Your account is limited to `MAX` reserved addresses. If you need more addresses, you can upgrade to a Pay-as-you-go plan at: [https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo](https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo). | | [ERR\_NGROK\_503](/docs/errors/err_ngrok_503) | No available reserved addresses in region `REGION` | | [ERR\_NGROK\_504](/docs/errors/err_ngrok_504) | A description is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_505](/docs/errors/err_ngrok_505) | Metadata is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_506](/docs/errors/err_ngrok_506) | The reserved address update failed because no values were provided. Specify at least one value. | | [ERR\_NGROK\_507](/docs/errors/err_ngrok_507) | You may not assign endpoint configuration `ID` because it is type `TYPE`. You must assign a configuration with type 'tcp'. | | [ERR\_NGROK\_508](/docs/errors/err_ngrok_508) | Your account is limited to `MAX` reserved addresses. If you need more addresses, you can upgrade to a Pay-as-you-go plan at: [https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo](https://dashboard.ngrok.com/billing/choose-a-plan?plan=paygo). | | [ERR\_NGROK\_509](/docs/errors/err_ngrok_509) | Your account is rate limited for adding `MAX` reserved addresses per `INTERVAL`. | | [ERR\_NGROK\_511](/docs/errors/err_ngrok_511) | The domain `DOMAIN` has a dangling CNAME record. The CNAME must be deleted before this domain can be reserved. | | [ERR\_NGROK\_512](/docs/errors/err_ngrok_512) | The domain `DOMAIN` has a dangling A, AAAA, ALIAS or other record pointing to ngrok. The record must be deleted before this domain can be reserved. | | [ERR\_NGROK\_513](/docs/errors/err_ngrok_513) | The domain `DOMAIN` is not a valid DNS name. No domain part may be more than 63 characters long. | | [ERR\_NGROK\_514](/docs/errors/err_ngrok_514) | The domain `DOMAIN` is not a valid DNS name. Domains may not be more than 253 characters long. | | [ERR\_NGROK\_515](/docs/errors/err_ngrok_515) | The domain `DOMAIN` is proxied by Cloudflare. While the record correctly resolves to Cloudflare's edge and may be working correctly, we cannot verify it. If you're experiencing issues with this domain, try disabling the Cloudflare proxy and verify again. | | [ERR\_NGROK\_516](/docs/errors/err_ngrok_516) | The wildcard `DOMAIN` is proxied by Cloudflare. While the wildcard's covered subdomains resolve to Cloudflare's edge and may be working correctly, we cannot verify the wildcard CNAME. If you're experiencing issues with this wildcard domain, try disabling the Cloudflare proxy on the `DOMAIN` record and verify again. | | [ERR\_NGROK\_517](/docs/errors/err_ngrok_517) | The ACME Challenge `RECORD` is proxied by Cloudflare. Proxying hides the CNAME needed for validation, so we cannot verify the ACME Challenge even though it may be working correctly. If you're experiencing issues with this domain, try disabling the Cloudflare proxy and verify again. | | [ERR\_NGROK\_600](/docs/errors/err_ngrok_600) | Invalid ACL rule. An ACL rule must be '\*' or start with 'bind:'. The bind value must match the format of a domain, address, or label. You specified: `RULE`. | | [ERR\_NGROK\_601](/docs/errors/err_ngrok_601) | This credential is your tunnel token and cannot be deleted, only rotated. | | [ERR\_NGROK\_602](/docs/errors/err_ngrok_602) | Your account is limited to `MAX` authtokens. Upgrade your account at [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing). | | [ERR\_NGROK\_603](/docs/errors/err_ngrok_603) | Your account is limited to `MAX` API keys. Upgrade your account at [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing). | | [ERR\_NGROK\_604](/docs/errors/err_ngrok_604) | A description is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_605](/docs/errors/err_ngrok_605) | Metadata is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_606](/docs/errors/err_ngrok_606) | The credentials update failed because no values were provided. Specify at least one value. | | [ERR\_NGROK\_607](/docs/errors/err_ngrok_607) | SSH tunnel feature is not enabled on your account. | | [ERR\_NGROK\_608](/docs/errors/err_ngrok_608) | Your account is limited to `MAX` SSH keys. Upgrade your account at [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing). | | [ERR\_NGROK\_609](/docs/errors/err_ngrok_609) | The public key `PUBKEY` already exists on your account. | | [ERR\_NGROK\_610](/docs/errors/err_ngrok_610) | The public key `PUBKEY` is already used in a different account. Remove it from the other ngrok account or generate a new SSH key pair (hint: use ssh-keygen). | | [ERR\_NGROK\_611](/docs/errors/err_ngrok_611) | Public key is missing or empty. | | [ERR\_NGROK\_612](/docs/errors/err_ngrok_612) | Invalid public key `PUBKEY`: `ERR` | | [ERR\_NGROK\_613](/docs/errors/err_ngrok_613) | Your account is limited to `MAX` API keys. | | [ERR\_NGROK\_614](/docs/errors/err_ngrok_614) | Your account is rate limited for adding `MAX` API keys per `INTERVAL`. | | [ERR\_NGROK\_615](/docs/errors/err_ngrok_615) | Your account is limited to `MAX` authtokens. | | [ERR\_NGROK\_617](/docs/errors/err_ngrok_617) | Your account is limited to `MAX` SSH keys. | | [ERR\_NGROK\_619](/docs/errors/err_ngrok_619) | Your account is not authorized to use this ACL rule type. You specified: `RULE`. | | [ERR\_NGROK\_620](/docs/errors/err_ngrok_620) | Only Pay-as-you-go plans can use tunnel ACL rules. | | [ERR\_NGROK\_622](/docs/errors/err_ngrok_622) | Only Pay-as-you-go plans can use SSH ACL rules. | | [ERR\_NGROK\_627](/docs/errors/err_ngrok_627) | The auth token is already owned by a user and cannot be re-assigned ownership. | | [ERR\_NGROK\_628](/docs/errors/err_ngrok_628) | The API key is already owned by a user and cannot be re-assigned ownership. | | [ERR\_NGROK\_629](/docs/errors/err_ngrok_629) | The SSH public key is already owned by a user and cannot be re-assigned ownership. | | [ERR\_NGROK\_630](/docs/errors/err_ngrok_630) | You must be an admin to set credential ownership. | | [ERR\_NGROK\_631](/docs/errors/err_ngrok_631) | You may specify only one of owner\_id, owner\_email, or scim\_user\_id. | | [ERR\_NGROK\_632](/docs/errors/err_ngrok_632) | You may specify either an owner (owner\_id, owner\_email, or scim\_user\_id) or membership\_id, but not both. | | [ERR\_NGROK\_633](/docs/errors/err_ngrok_633) | Your account is not authorized to specify credential ownership. | | [ERR\_NGROK\_634](/docs/errors/err_ngrok_634) | No credential owner could be determined. | | [ERR\_NGROK\_635](/docs/errors/err_ngrok_635) | API access requires that the owner of the API key has write privileges. | | [ERR\_NGROK\_636](/docs/errors/err_ngrok_636) | The credential owner is marked as inactive on the account. | | [ERR\_NGROK\_637](/docs/errors/err_ngrok_637) | This credential cannot be deleted because it is the default authtoken tied to `EMAIL`. Removing the user from this account will also delete any associated tokens. | | [ERR\_NGROK\_638](/docs/errors/err_ngrok_638) | Your account is not allowed to create credentials with pre-computed tokens. To create a new credential, omit pre-computed token field and let ngrok generate one for you. Its value will be available as part of the response. | | [ERR\_NGROK\_702](/docs/errors/err_ngrok_702) | Too many connections! The tunnel session `SESSION` has violated the rate-limit policy of `THRESHOLD` connections per minute by initiating `COUNT` connections in the last `SECONDS` seconds. Decrease your inbound connection volume or upgrade to a paid plan for additional capacity. | | [ERR\_NGROK\_703](/docs/errors/err_ngrok_703) | Too many connections! The account hosting this endpoint has violated the rate-limit policy of `THRESHOLD` connections per minute by initiating `COUNT` connections in the last `SECONDS` seconds. Decrease your inbound connection volume or upgrade your account plan for additional capacity. | | [ERR\_NGROK\_714](/docs/errors/err_ngrok_714) | This ngrok account has reached its simultaneous connections limit. Log into [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) to view your options. | | [ERR\_NGROK\_715](/docs/errors/err_ngrok_715) | You have exceeded your limit on connections per minute. This limit will reset within 1 minute. If you expect to continually exceed these limits, please upgrade your plan at [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) | | [ERR\_NGROK\_716](/docs/errors/err_ngrok_716) | This unauthenticated ngrok session has reached its simultaneous connections limit. Sign up for a free account at [https://ngrok.com](https://ngrok.com) for a higher limit. | | [ERR\_NGROK\_717](/docs/errors/err_ngrok_717) | This unauthenticated ngrok session has reached its connection rate limit. Sign up for a free account at [https://ngrok.com](https://ngrok.com) for a higher limit. | | [ERR\_NGROK\_718](/docs/errors/err_ngrok_718) | Your account is rate limited to `MAX` bytes incoming traffic per `INTERVAL`. | | [ERR\_NGROK\_720](/docs/errors/err_ngrok_720) | Your account is rate limited to `MAX` bytes outgoing traffic per `INTERVAL`. | | [ERR\_NGROK\_722](/docs/errors/err_ngrok_722) | internal server error | | [ERR\_NGROK\_724](/docs/errors/err_ngrok_724) | No backend is available to serve requests for endpoint `URL`. | | [ERR\_NGROK\_725](/docs/errors/err_ngrok_725) | This ngrok account has reached its network bandwidth limit for the month. Log into [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) to view your options. | | [ERR\_NGROK\_727](/docs/errors/err_ngrok_727) | This ngrok account has reached its HTTP requests limit for the month. Log into [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) to view your options. | | [ERR\_NGROK\_729](/docs/errors/err_ngrok_729) | This ngrok account has reached its TCP connections limit for the month. Log into [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) to view your options. | | [ERR\_NGROK\_730](/docs/errors/err_ngrok_730) | This ngrok account has reached its TLS connections limit for the month. Log into [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) to view your options. | | [ERR\_NGROK\_734](/docs/errors/err_ngrok_734) | You have exceeded your limit of `MAX` requests per `INTERVAL`. This limit will reset within 1 minute. If you expect to continually exceed these limits, please upgrade your plan at [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) | | [ERR\_NGROK\_741](/docs/errors/err_ngrok_741) | Internal server error. | | [ERR\_NGROK\_742](/docs/errors/err_ngrok_742) | This server is draining connections and cannot accept new ones. Please reconnect to continue. | | [ERR\_NGROK\_743](/docs/errors/err_ngrok_743) | This server is temporarily shedding traffic due to abnormally high request volume. Please retry shortly. | | [ERR\_NGROK\_744](/docs/errors/err_ngrok_744) | This ngrok account has reached its TCP connections limit for the minute. It resets within a minute. Log into [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) to view your options. | | [ERR\_NGROK\_745](/docs/errors/err_ngrok_745) | This ngrok account has reached its TLS connections limit for the minute. It resets within a minute. Log into [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) to view your options. | | [ERR\_NGROK\_800](/docs/errors/err_ngrok_800) | Unknown stream type: `TYPE`. | | [ERR\_NGROK\_801](/docs/errors/err_ngrok_801) | The request payload you specified is not valid. | | [ERR\_NGROK\_802](/docs/errors/err_ngrok_802) | internal server error | | [ERR\_NGROK\_803](/docs/errors/err_ngrok_803) | The server process is shutting down and refusing new requests. | | [ERR\_NGROK\_804](/docs/errors/err_ngrok_804) | This ngrok agent does not support remote restarting: `REASON`. | | [ERR\_NGROK\_805](/docs/errors/err_ngrok_805) | This ngrok agent does not support remote stopping: `REASON`. | | [ERR\_NGROK\_806](/docs/errors/err_ngrok_806) | This ngrok agent does not support remote updating: `REASON`. | | [ERR\_NGROK\_807](/docs/errors/err_ngrok_807) | The remote ngrok agent failed to stop because of an error: `ERR` | | [ERR\_NGROK\_808](/docs/errors/err_ngrok_808) | The remote ngrok agent failed to restart because of an error: `ERR` | | [ERR\_NGROK\_809](/docs/errors/err_ngrok_809) | The remote ngrok agent failed to update because of an error: `ERR` | | [ERR\_NGROK\_810](/docs/errors/err_ngrok_810) | There was a networking error while trying to send this operation to the ngrok agent or trying to read the response. This usually indicates that the ngrok agent was in the process of shutting down or reconnecting, but it could also result from a networking timeout or failure. It is possible, but unlikely, that the operation succeeded. | | [ERR\_NGROK\_811](/docs/errors/err_ngrok_811) | Could not negotiate a protocol version. Requested: "`REQUESTED`", Supported: `SUPPORTED`" | | [ERR\_NGROK\_812](/docs/errors/err_ngrok_812) | Your request exceeded the maximum size of `SIZE`. | | [ERR\_NGROK\_813](/docs/errors/err_ngrok_813) | Your message "`MESSAGE`"" is not supported by this version of the protocol: `PROTOVERSION`. | | [ERR\_NGROK\_814](/docs/errors/err_ngrok_814) | No tunnel with the ID `TUNNELID` is bound to this agent session. | | [ERR\_NGROK\_1000](/docs/errors/err_ngrok_1000) | Your billing address is too long: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_1001](/docs/errors/err_ngrok_1001) | Your tax ID is too long: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_1002](/docs/errors/err_ngrok_1002) | Your account is limited to `MAX` billing email addresses. Email [support@ngrok.com](mailto:support@ngrok.com) to increase the limit | | [ERR\_NGROK\_1003](/docs/errors/err_ngrok_1003) | The billing email `ID` (`EMAIL`) may not be deleted | | [ERR\_NGROK\_1004](/docs/errors/err_ngrok_1004) | The billing email is too long: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_1005](/docs/errors/err_ngrok_1005) | The billing email address `EMAIL` is invalid: `ERR` | | [ERR\_NGROK\_1006](/docs/errors/err_ngrok_1006) | There was a problem with your credit card: `MSG` | | [ERR\_NGROK\_1007](/docs/errors/err_ngrok_1007) | Your subscription was updated, but we failed to charge your card: `MSG`. | | [ERR\_NGROK\_1008](/docs/errors/err_ngrok_1008) | Your account is limited to `MAX` billing email addresses. | | [ERR\_NGROK\_1010](/docs/errors/err_ngrok_1010) | The email address "`EMAIL`" is invalid. | | [ERR\_NGROK\_1011](/docs/errors/err_ngrok_1011) | Your account is currently past due. You may only enter a payment method to pay your overdue invoice or switch to the free plan. | | [ERR\_NGROK\_1012](/docs/errors/err_ngrok_1012) | You may not purchase fewer licenses than users. Your account currently has `USERS` users. | | [ERR\_NGROK\_1013](/docs/errors/err_ngrok_1013) | Yearly plans can only be downgraded or canceled in their final month. Try again after `TIMESTAMP`. | | [ERR\_NGROK\_1014](/docs/errors/err_ngrok_1014) | You may only purchase one license product at a time. | | [ERR\_NGROK\_1015](/docs/errors/err_ngrok_1015) | You are on a legacy custom subscription. Contact [support@ngrok.com](mailto:support@ngrok.com) for help with changing your subscription. | | [ERR\_NGROK\_1016](/docs/errors/err_ngrok_1016) | Cannot charge customer that has no active payment source. | | [ERR\_NGROK\_1017](/docs/errors/err_ngrok_1017) | Accounts in bad standing may only switch to a free plan or update their payment method. | | [ERR\_NGROK\_1018](/docs/errors/err_ngrok_1018) | You may only purchase `COUNT` licenses at the `TIER` tier. | | [ERR\_NGROK\_1019](/docs/errors/err_ngrok_1019) | The `ADDON` addon is not allowed for this plan. | | [ERR\_NGROK\_1020](/docs/errors/err_ngrok_1020) | Addon selections for `LICENSE` are not valid. | | [ERR\_NGROK\_1021](/docs/errors/err_ngrok_1021) | The billing email address `EMAIL` is already added | | [ERR\_NGROK\_1022](/docs/errors/err_ngrok_1022) | Your invoice tax ID type `INPUT` is not a valid option. | | [ERR\_NGROK\_1023](/docs/errors/err_ngrok_1023) | Your invoice tax ID country `INPUT` is not a valid option. | | [ERR\_NGROK\_1024](/docs/errors/err_ngrok_1024) | Your invoice billing address country is too long: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_1025](/docs/errors/err_ngrok_1025) | Your invoice billing address state is too long: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_1026](/docs/errors/err_ngrok_1026) | Your invoice billing address city is too long: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_1027](/docs/errors/err_ngrok_1027) | Your invoice billing address line1 is too long: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_1028](/docs/errors/err_ngrok_1028) | Your invoice billing address line2 is too long: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_1029](/docs/errors/err_ngrok_1029) | Your invoice billing address postal code is too long: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_1030](/docs/errors/err_ngrok_1030) | Your invoice billing address name is too long: `VAL` bytes. Max: `MAX` | | [ERR\_NGROK\_1031](/docs/errors/err_ngrok_1031) | Tax information is incomplete: Missing Type for Tax ID. Update or remove your tax information before making any changes to your account's subscription. | | [ERR\_NGROK\_1032](/docs/errors/err_ngrok_1032) | Missing Tax ID for Type. You must supply both the Tax ID and its Type or remove both | | [ERR\_NGROK\_1033](/docs/errors/err_ngrok_1033) | Our billing provider is temporarily unavailable. Please try again in a few minutes. | | [ERR\_NGROK\_1034](/docs/errors/err_ngrok_1034) | Your payment for `MSG` has not settled yet. Your account is restored automatically once it clears, so no further action is needed. | | [ERR\_NGROK\_1035](/docs/errors/err_ngrok_1035) | Another payment attempt for `MSG` is already in progress. Wait a moment and try again. | | [ERR\_NGROK\_1036](/docs/errors/err_ngrok_1036) | Your subscription ended, so this change didn't apply. Reload the page and try again. | | [ERR\_NGROK\_1100](/docs/errors/err_ngrok_1100) | No public keys were sent for this SSH tunnel. | | [ERR\_NGROK\_1101](/docs/errors/err_ngrok_1101) | Too many public keys sent for this SSH tunnel (sent `VAL`, max `MAX`). | | [ERR\_NGROK\_1102](/docs/errors/err_ngrok_1102) | Could not find your account based on the public keys sent for this SSH tunnel. Anonymous SSH tunneling is not supported. | | [ERR\_NGROK\_1103](/docs/errors/err_ngrok_1103) | The account `ACCOUNT` doesn't have the SSH tunneling feature enabled. | | [ERR\_NGROK\_1104](/docs/errors/err_ngrok_1104) | Multiplexing is not supported with SSH tunneling | | [ERR\_NGROK\_1105](/docs/errors/err_ngrok_1105) | Only one port forward per tunneling session is supported. | | [ERR\_NGROK\_1106](/docs/errors/err_ngrok_1106) | Could not find `USER` API version. Use `VERSION` as user when connecting with SSH to get the latest version. | | [ERR\_NGROK\_1107](/docs/errors/err_ngrok_1107) | No tunnel type specified. The SSH command specifies the type of tunnel to create, one of 'http', 'tls', or 'tcp'. | | [ERR\_NGROK\_1108](/docs/errors/err_ngrok_1108) | `CMD` is not a valid tunnel type. The SSH command must be one of 'http', 'tls', or 'tcp'. | | [ERR\_NGROK\_1109](/docs/errors/err_ngrok_1109) | `FLAG` flag is not supported when using SSH tunneling | | [ERR\_NGROK\_1110](/docs/errors/err_ngrok_1110) | Error while parsing the `CMD` command: | | [ERR\_NGROK\_1111](/docs/errors/err_ngrok_1111) | Timeout while waiting for SSH session | | [ERR\_NGROK\_1112](/docs/errors/err_ngrok_1112) | Timeout while waiting for port forward request. | | [ERR\_NGROK\_1113](/docs/errors/err_ngrok_1113) | Cannot use bind port `PORT` when using `CMD` command. | | [ERR\_NGROK\_1114](/docs/errors/err_ngrok_1114) | Cannot use bind address '-R `HOST`:`PORT`:' with '-remote-addr=`ADDR` parameter. | | [ERR\_NGROK\_1115](/docs/errors/err_ngrok_1115) | Cannot request a custom TCP port '-R `PORT`:' with '-remote-addr=`ADDR` parameter. | | [ERR\_NGROK\_1116](/docs/errors/err_ngrok_1116) | Cannot request a custom bind port '-R `PORT`:'. | | [ERR\_NGROK\_1117](/docs/errors/err_ngrok_1117) | Cannot request a random port with a custom bind address `ADDR` in '-R `ADDR`:0:'. | | [ERR\_NGROK\_1118](/docs/errors/err_ngrok_1118) | Cannot use binding address '-R `ADDR`:' with '-hostname=`HOSTNAME` parameter. | | [ERR\_NGROK\_1119](/docs/errors/err_ngrok_1119) | Cannot use binding address '-R `ADDR`:' with '-subdomain=`SUBDOMAIN` parameter. | | [ERR\_NGROK\_1120](/docs/errors/err_ngrok_1120) | Cannot use '-hostname=`HOSTNAME` with '-subdomain=`SUBDOMAIN` parameter. | | [ERR\_NGROK\_1121](/docs/errors/err_ngrok_1121) | SSH sessions do not support update. | | [ERR\_NGROK\_1122](/docs/errors/err_ngrok_1122) | SSH sessions do not support restart. | | [ERR\_NGROK\_1123](/docs/errors/err_ngrok_1123) | Tunnel '-proxy-proto=`PROXYPROTO` specifies invalid protocol version. | | [ERR\_NGROK\_1125](/docs/errors/err_ngrok_1125) | HIPAA-enabled accounts cannot use SSH tunneling. | | [ERR\_NGROK\_1200](/docs/errors/err_ngrok_1200) | Authorized account's primary email `EMAIL` is not verified. | | [ERR\_NGROK\_1202](/docs/errors/err_ngrok_1202) | You can't enable Google OAuth without first connecting a Google Apps login. | | [ERR\_NGROK\_1203](/docs/errors/err_ngrok_1203) | You can't enable Google OAuth because your connected Google login does not have a Google Apps domain. | | [ERR\_NGROK\_1204](/docs/errors/err_ngrok_1204) | You can't delete your payment method while you have an active subscription. | | [ERR\_NGROK\_1205](/docs/errors/err_ngrok_1205) | You failed to solve the CAPTCHA, please try again. | | [ERR\_NGROK\_1206](/docs/errors/err_ngrok_1206) | You failed to solve the CAPTCHA, please try again. | | [ERR\_NGROK\_1207](/docs/errors/err_ngrok_1207) | Cannot switch to an account you are not a member of. | | [ERR\_NGROK\_1208](/docs/errors/err_ngrok_1208) | You failed to solve the CAPTCHA, please try again. | | [ERR\_NGROK\_1210](/docs/errors/err_ngrok_1210) | Invalid subscription interval `INTERVAL`. | | [ERR\_NGROK\_1211](/docs/errors/err_ngrok_1211) | Warning: Your IP does not match the IP Policy for this Account. When IP Restrictions are enforced you will not be able to access the dashboard. Authenticate from an IP in the correct range or update your IP Restrictions. | | [ERR\_NGROK\_1212](/docs/errors/err_ngrok_1212) | Your IP does not match the IP Policy for this Account. Authenticate from an IP in the correct range or update your IP Restrictions from the ngrok dashboard. [https://dashboard.ngrok.com/ip-restrictions](https://dashboard.ngrok.com/ip-restrictions) | | [ERR\_NGROK\_1214](/docs/errors/err_ngrok_1214) | Your account is rate limited to `MAX` requests per `INTERVAL`. | | [ERR\_NGROK\_1215](/docs/errors/err_ngrok_1215) | The user `EMAIL` does not have permissions to invite other team members. | | [ERR\_NGROK\_1216](/docs/errors/err_ngrok_1216) | The user `EMAIL` does not have permissions to manage team members. | | [ERR\_NGROK\_1217](/docs/errors/err_ngrok_1217) | The user `EMAIL` does not have permissions to view billing information. | | [ERR\_NGROK\_1218](/docs/errors/err_ngrok_1218) | The user `EMAIL` does not have permissions to change billing information. | | [ERR\_NGROK\_1219](/docs/errors/err_ngrok_1219) | The user `EMAIL` does not have permissions to change developer resources. | | [ERR\_NGROK\_1220](/docs/errors/err_ngrok_1220) | The user `EMAIL` does not have administrative privileges which are required to perform this operation. | | [ERR\_NGROK\_1221](/docs/errors/err_ngrok_1221) | Refusing to perform an account delete operation because you are using the dashboard with a different active account than the one you requested deletion for. | | [ERR\_NGROK\_1222](/docs/errors/err_ngrok_1222) | You are not a member of any account. | | [ERR\_NGROK\_1223](/docs/errors/err_ngrok_1223) | You are logged out, please log back in. | | [ERR\_NGROK\_1224](/docs/errors/err_ngrok_1224) | You are no longer a member of the current account. | | [ERR\_NGROK\_1225](/docs/errors/err_ngrok_1225) | You must accept the terms of service and privacy policy to sign up for ngrok. | | [ERR\_NGROK\_1226](/docs/errors/err_ngrok_1226) | You are disallowed from creating an ngrok account due to violation of the terms of service. | | [ERR\_NGROK\_1227](/docs/errors/err_ngrok_1227) | The number of login attempts with this email has been exceeded, please try again later. | | [ERR\_NGROK\_1228](/docs/errors/err_ngrok_1228) | You are no longer an active member of the current account. | | [ERR\_NGROK\_1229](/docs/errors/err_ngrok_1229) | The resource you are attempting to access requires MFA, please authenticate via a second factor. | | [ERR\_NGROK\_1230](/docs/errors/err_ngrok_1230) | We do not allow sign ups from your location. Your location was determined using your IP address. | | [ERR\_NGROK\_1231](/docs/errors/err_ngrok_1231) | The email address `EMAIL` already has an active, unredeemed invitation to this account. | | [ERR\_NGROK\_1232](/docs/errors/err_ngrok_1232) | We do not allow sign ups from anonymous proxies such as Tor. | | [ERR\_NGROK\_1233](/docs/errors/err_ngrok_1233) | The email address `EMAIL` is already associated with an SSO Identity Provider connection. Contact support: [support@ngrok.com](mailto:support@ngrok.com) | | [ERR\_NGROK\_1400](/docs/errors/err_ngrok_1400) | The IP Policy update failed because no values were provided. Specify at least one value | | [ERR\_NGROK\_1401](/docs/errors/err_ngrok_1401) | The IP Policy Rule update failed because no values were provided. Specify at least one value | | [ERR\_NGROK\_1402](/docs/errors/err_ngrok_1402) | IP Policy not found | | [ERR\_NGROK\_1404](/docs/errors/err_ngrok_1404) | IP Policy Rule not found | | [ERR\_NGROK\_1405](/docs/errors/err_ngrok_1405) | Required parameter is missing. Specify `PARAM` | | [ERR\_NGROK\_1406](/docs/errors/err_ngrok_1406) | Invalid CIDR: `CIDR` | | [ERR\_NGROK\_1407](/docs/errors/err_ngrok_1407) | IP Policy with name `NAME` already exists | | [ERR\_NGROK\_1408](/docs/errors/err_ngrok_1408) | IP Policy Rule with CIDR `CIDR` already exists | | [ERR\_NGROK\_1409](/docs/errors/err_ngrok_1409) | IP Policy Rule CIDR `CIDR` must be specified using the lowest IP address in the range. Did you mean `SUGGESTION`? | | [ERR\_NGROK\_1410](/docs/errors/err_ngrok_1410) | Your account is limited to a maximum of `MAX` IP Policies | | [ERR\_NGROK\_1411](/docs/errors/err_ngrok_1411) | Your account is limited to a maximum of `MAX` IP Policy rules for each IP Policy | | [ERR\_NGROK\_1412](/docs/errors/err_ngrok_1412) | Your legacy account is not authorized to use per-tunnel IP policies. | | [ERR\_NGROK\_1413](/docs/errors/err_ngrok_1413) | IP Policy Rule is missing a network mask. Consider using `IP`/`BITS` if you want to target a single host IP. | | [ERR\_NGROK\_1414](/docs/errors/err_ngrok_1414) | The provided ip\_policy\_id is invalid. Check the value given with your request. | | [ERR\_NGROK\_1415](/docs/errors/err_ngrok_1415) | Your account is limited to `MAX` IP Policies. | | [ERR\_NGROK\_1417](/docs/errors/err_ngrok_1417) | Your account is limited to `MAX` rules per IP Policy. | | [ERR\_NGROK\_1419](/docs/errors/err_ngrok_1419) | Cannot delete only rule (`RULEID`) in use by dashboard IP Restriction (`RESTRICTIONID`). Detach this IP Policy from IP Restriction before deleting this rule. | | [ERR\_NGROK\_1420](/docs/errors/err_ngrok_1420) | Cannot delete IP Policy (`POLICYID`) because it is referenced by one or more IP Restrictions: (`RESTRICTIONIDS`). Detach this IP Policy from all IP Restrictions before deleting it. | | [ERR\_NGROK\_1421](/docs/errors/err_ngrok_1421) | The IP Policy Rule description exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_1422](/docs/errors/err_ngrok_1422) | An IP Policy Rule must have an 'action' of either 'allow' or 'deny'. | | [ERR\_NGROK\_1423](/docs/errors/err_ngrok_1423) | An action should now be specified on the IP Policy Rule, not IP Policy. Contact ngrok support if you need additional time to migrate. | | [ERR\_NGROK\_1500](/docs/errors/err_ngrok_1500) | Maintenance in progress, operations for some resources are read-only | | [ERR\_NGROK\_1501](/docs/errors/err_ngrok_1501) | Maintenance in progress, some operations are unavailable | | [ERR\_NGROK\_1600](/docs/errors/err_ngrok_1600) | Endpoint configuration not found | | [ERR\_NGROK\_1601](/docs/errors/err_ngrok_1601) | Invalid endpoint configuration ID | | [ERR\_NGROK\_1602](/docs/errors/err_ngrok_1602) | Invalid endpoint configuration request | | [ERR\_NGROK\_1607](/docs/errors/err_ngrok_1607) | The IP Policy ID `ID` specified for the IP Policy module does not exist. | | [ERR\_NGROK\_1608](/docs/errors/err_ngrok_1608) | Endpoint Configurations are deprecated. Use Cloud Endpoints instead. | | [ERR\_NGROK\_1609](/docs/errors/err_ngrok_1609) | Your account is limited to a maximum of `MAX` endpoint configurations. | | [ERR\_NGROK\_1611](/docs/errors/err_ngrok_1611) | Invalid endpoint configuration, circuit breaker error threshold percentage must be between 0.0 and 1.0 inclusive, was `VAL`. | | [ERR\_NGROK\_1612](/docs/errors/err_ngrok_1612) | The auth provider `VAL` does not exist. Specify an auth provider ID or the special value 'agent'. | | [ERR\_NGROK\_1613](/docs/errors/err_ngrok_1613) | The endpoint configuration exceeds the max number of ip policies. You specified `VAL`, the maximum is `MAX`. | | [ERR\_NGROK\_1614](/docs/errors/err_ngrok_1614) | The endpoint configuration TLS CA certificate size exceeds the max. You specified `VAL`, the maximum is `MAX`. | | [ERR\_NGROK\_1615](/docs/errors/err_ngrok_1615) | The endpoint configuration exceeds the max number of headers. You specified `VAL`, the maximum is `MAX`. | | [ERR\_NGROK\_1616](/docs/errors/err_ngrok_1616) | Invalid endpoint configuration, circuit breaker tripped duration must be greater than 0, was `VAL`. | | [ERR\_NGROK\_1617](/docs/errors/err_ngrok_1617) | Invalid endpoint configuration, circuit breaker rolling window must be greater than 0, was `VAL`. | | [ERR\_NGROK\_1618](/docs/errors/err_ngrok_1618) | Invalid endpoint configuration, circuit breaker num buckets must be greater than 0 and less than `MAX`, was `VAL`. | | [ERR\_NGROK\_1619](/docs/errors/err_ngrok_1619) | The endpoint configuration could not be deleted because it is still referenced by at least one reserved domain or reserved address. | | [ERR\_NGROK\_1620](/docs/errors/err_ngrok_1620) | The endpoint configuration must specify a type. | | [ERR\_NGROK\_1621](/docs/errors/err_ngrok_1621) | The module `NAME` is not supported on an endpoint configuration of type `TYPE`. | | [ERR\_NGROK\_1622](/docs/errors/err_ngrok_1622) | `NAME` is not a valid endpoint configuration type. Must be one of 'http', 'https', 'tcp'. | | [ERR\_NGROK\_1623](/docs/errors/err_ngrok_1623) | `KEY` is not a valid HTTP header name because it contains at least one invalid character. | | [ERR\_NGROK\_1624](/docs/errors/err_ngrok_1624) | `VAL` is not a valid HTTP header value: `REASON`. | | [ERR\_NGROK\_1625](/docs/errors/err_ngrok_1625) | You must specify at least one IP policy in the IP Policy module. | | [ERR\_NGROK\_1626](/docs/errors/err_ngrok_1626) | SNS webhook validation does not accept a secret. | | [ERR\_NGROK\_1627](/docs/errors/err_ngrok_1627) | Webhook validation for `T` requires a secret. | | [ERR\_NGROK\_1628](/docs/errors/err_ngrok_1628) | There were validation errors while saving the endpoint configuration:`ERRS` | | [ERR\_NGROK\_1629](/docs/errors/err_ngrok_1629) | You must specify a supported provider name. Supported providers: \[`T`] | | [ERR\_NGROK\_1630](/docs/errors/err_ngrok_1630) | Webhook provider `T` is not supported. Supported providers: \[`TYPE`] | | [ERR\_NGROK\_1631](/docs/errors/err_ngrok_1631) | Endpoint Configurations are deprecated. Use Cloud Endpoints instead. | | [ERR\_NGROK\_1632](/docs/errors/err_ngrok_1632) | You may not configure the TLS termination parameter `PARAMETER` when the HTTPS module disables TLS termination. | | [ERR\_NGROK\_1633](/docs/errors/err_ngrok_1633) | Invalid endpoint configuration, unmanaged provider configurations must specify a client ID and a client secret. | | [ERR\_NGROK\_1634](/docs/errors/err_ngrok_1634) | Invalid endpoint configuration, custom OAuth scopes on a managed OAuth application are not allowed. Define a custom OAuth application to use custom scopes. | | [ERR\_NGROK\_1635](/docs/errors/err_ngrok_1635) | Invalid endpoint configuration, OAuth auth check interval must be at least `MIN`, was `ACTUAL`. | | [ERR\_NGROK\_1636](/docs/errors/err_ngrok_1636) | Invalid endpoint configuration, OAuth may only have one provider configuration but multiple are defined: \[`PROVIDER_NAMES`]. | | [ERR\_NGROK\_1637](/docs/errors/err_ngrok_1637) | Invalid endpoint configuration, OAuth does not define any provider configurations but one is required. | | [ERR\_NGROK\_1638](/docs/errors/err_ngrok_1638) | Invalid endpoint configuration, OAuth provider `PROVIDER` requires at least the `SCOPE` scope to authenticate by `AUTH_FEATURE`.' | | [ERR\_NGROK\_1639](/docs/errors/err_ngrok_1639) | Invalid endpoint configuration, OAuth provider `PROVIDER` contains an invalid email domain: `NAME`. | | [ERR\_NGROK\_1640](/docs/errors/err_ngrok_1640) | Invalid endpoint configuration, OAuth GitHub Teams must be listed as either numerical ids or in the format 'org\_slug/team\_slug', `TEAM` does not match either format. | | [ERR\_NGROK\_1641](/docs/errors/err_ngrok_1641) | Endpoint Configurations are deprecated. Use Cloud Endpoints instead. | | [ERR\_NGROK\_1642](/docs/errors/err_ngrok_1642) | Invalid endpoint configuration, OAuth cookie prefix `PREFIX` must consist of only alphanumeric characters with periods. | | [ERR\_NGROK\_1643](/docs/errors/err_ngrok_1643) | Invalid endpoint configuration, OAuth provider `PROVIDER` contains an invalid email address, `NAME`. | | [ERR\_NGROK\_1644](/docs/errors/err_ngrok_1644) | Invalid endpoint configuration, OAuth provider `PROVIDER` contains an email domain with invalid punycode: `NAME`. | | [ERR\_NGROK\_1645](/docs/errors/err_ngrok_1645) | Invalid endpoint configuration, OAuth provider `PROVIDER` contains an email address with invalid punycode: `NAME`. | | [ERR\_NGROK\_1646](/docs/errors/err_ngrok_1646) | Invalid endpoint configuration, OAuth provider `PROVIDER` email addresses must be unique, but a duplicate is present: `DUPLICATE`. | | [ERR\_NGROK\_1647](/docs/errors/err_ngrok_1647) | Invalid endpoint configuration, OAuth provider `PROVIDER` email domains must be unique, but a duplicate is present: `DUPLICATE`. | | [ERR\_NGROK\_1648](/docs/errors/err_ngrok_1648) | Invalid endpoint configuration, OAuth provider `PROVIDER` teams must be unique, but a duplicate is present: `DUPLICATE`. | | [ERR\_NGROK\_1649](/docs/errors/err_ngrok_1649) | Invalid endpoint configuration, OAuth provider `PROVIDER` organizations must be unique, but a duplicate is present: `DUPLICATE`. | | [ERR\_NGROK\_1650](/docs/errors/err_ngrok_1650) | Invalid endpoint configuration, OAuth provider `PROVIDER` groups must be unique, but a duplicate is present: `DUPLICATE`. | | [ERR\_NGROK\_1651](/docs/errors/err_ngrok_1651) | Invalid endpoint configuration, OAuth provider `PROVIDER` email addresses must not contain empty values | | [ERR\_NGROK\_1652](/docs/errors/err_ngrok_1652) | Invalid endpoint configuration, OAuth provider `PROVIDER` email domains must not contain empty values | | [ERR\_NGROK\_1653](/docs/errors/err_ngrok_1653) | Invalid endpoint configuration, OAuth provider `PROVIDER` teams must not contain empty values | | [ERR\_NGROK\_1654](/docs/errors/err_ngrok_1654) | Invalid endpoint configuration, OAuth provider `PROVIDER` organizations must not contain empty values | | [ERR\_NGROK\_1655](/docs/errors/err_ngrok_1655) | Invalid endpoint configuration, OAuth provider `PROVIDER` groups must not contain empty values | | [ERR\_NGROK\_1656](/docs/errors/err_ngrok_1656) | The endpoint configuration exceeds the maximum number of OAuth scopes. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_1657](/docs/errors/err_ngrok_1657) | The endpoint configuration exceeds the maximum number of OAuth Github teams. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_1658](/docs/errors/err_ngrok_1658) | The endpoint configuration exceeds the maximum number of OAuth Github orgs. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_1659](/docs/errors/err_ngrok_1659) | The endpoint configuration exceeds the maximum number of OAuth Google groups. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_1660](/docs/errors/err_ngrok_1660) | The endpoint configuration exceeds the maximum number of OAuth emails. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_1661](/docs/errors/err_ngrok_1661) | The endpoint configuration exceeds the maximum number of OAuth domains. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_1662](/docs/errors/err_ngrok_1662) | The endpoint configuration specifies the following modules which may not be enabled when TLS is not terminated: `MODULE_NAMES`. | | [ERR\_NGROK\_1667](/docs/errors/err_ngrok_1667) | Invalid endpoint configuration, email addresses must be lowercase but `EMAIL` is not. | | [ERR\_NGROK\_1668](/docs/errors/err_ngrok_1668) | Invalid endpoint configuration, email domains must be lowercase but `DOMAIN` is not. | | [ERR\_NGROK\_1669](/docs/errors/err_ngrok_1669) | Duplicate 'add' header, `HEADER` was provided twice with different casings: `CASE_ONE` and `CASE_TWO`. | | [ERR\_NGROK\_1670](/docs/errors/err_ngrok_1670) | Your account is limited to `MAX` endpoint configurations. | | [ERR\_NGROK\_1672](/docs/errors/err_ngrok_1672) | The CA ID `ID` specified for the Mutual TLS module does not exist. | | [ERR\_NGROK\_1673](/docs/errors/err_ngrok_1673) | Description is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_1674](/docs/errors/err_ngrok_1674) | Metadata is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_1675](/docs/errors/err_ngrok_1675) | The Mutual TLS configurations exceeds the limit of `LIMIT` attached certificate authorities. | | [ERR\_NGROK\_1679](/docs/errors/err_ngrok_1679) | The endpoint configuration specifies conflicting authentication modules. Only one of SAML, OIDC, OAuth or Basic Auth may be enabled. | | [ERR\_NGROK\_1680](/docs/errors/err_ngrok_1680) | The SAML metadata exceeds the maximum length of `MAX` bytes, got `NBYTES`. | | [ERR\_NGROK\_1681](/docs/errors/err_ngrok_1681) | You must specify only the SAML IdP metadata or the SAML IdP metadata URL, but not both. | | [ERR\_NGROK\_1682](/docs/errors/err_ngrok_1682) | Failed to connect to IdP metadata URL `MDURL`: `ERR`. | | [ERR\_NGROK\_1683](/docs/errors/err_ngrok_1683) | Failed to parse the SAML IdP metadata: `ERR`. | | [ERR\_NGROK\_1684](/docs/errors/err_ngrok_1684) | Received unexpected status code `STATUSCODE` while fetching metadata URL `MDURL` | | [ERR\_NGROK\_1685](/docs/errors/err_ngrok_1685) | Failed to parse the SAML IdP metadata fetched from URL `MDURL`: `ERR`. | | [ERR\_NGROK\_1686](/docs/errors/err_ngrok_1686) | Encountered an error while reading the response body from the IdP metadata URL `MDURL`: `ERR`. | | [ERR\_NGROK\_1687](/docs/errors/err_ngrok_1687) | The specified OIDC issuer has a maximum length of 255 bytes. The specified value is `N` bytes. | | [ERR\_NGROK\_1688](/docs/errors/err_ngrok_1688) | The OIDC issuer property is required. | | [ERR\_NGROK\_1689](/docs/errors/err_ngrok_1689) | The OIDC Client ID property is required. | | [ERR\_NGROK\_1690](/docs/errors/err_ngrok_1690) | The OIDC Client Secret property is required. | | [ERR\_NGROK\_1691](/docs/errors/err_ngrok_1691) | One of the SAML IdP Metadata or IdP Metadata URL properties must be specified. | | [ERR\_NGROK\_1692](/docs/errors/err_ngrok_1692) | Invalid endpoint configuration, session inactivity timeout cannot be more than one year, was `ACTUAL`. | | [ERR\_NGROK\_1693](/docs/errors/err_ngrok_1693) | "`FORMAT`" is not a supported SAML nameid format. | | [ERR\_NGROK\_1694](/docs/errors/err_ngrok_1694) | Mutual TLS at edge requires TLS termination at edge. | | [ERR\_NGROK\_1695](/docs/errors/err_ngrok_1695) | No PEM data found in provided mutual TLS CA. | | [ERR\_NGROK\_1696](/docs/errors/err_ngrok_1696) | Certificate provided for mutual TLS is not a valid CA. | | [ERR\_NGROK\_1697](/docs/errors/err_ngrok_1697) | The header beginning with `PREFIX`...' exceeds the maximum header name length of `BYTES` bytes. | | [ERR\_NGROK\_1698](/docs/errors/err_ngrok_1698) | The header value for `HEADERKEY` beginning with `PREFIX`...' exceeds the maximum header value length of `BYTES` bytes. | | [ERR\_NGROK\_1699](/docs/errors/err_ngrok_1699) | The sendgrid verification key is not a base64 encoded ecdsa public key. The following error was encountered while trying to parse it: "`ERROR`" | | [ERR\_NGROK\_1700](/docs/errors/err_ngrok_1700) | Your legacy account is not authorized to use session operations. | | [ERR\_NGROK\_1701](/docs/errors/err_ngrok_1701) | Your legacy account is not authorized to stop or restart the agent. | | [ERR\_NGROK\_1702](/docs/errors/err_ngrok_1702) | Your legacy account is not authorized to update the agent. | | [ERR\_NGROK\_1800](/docs/errors/err_ngrok_1800) | You have reached the maximum of `MAX` IP Policies on a restriction. | | [ERR\_NGROK\_1801](/docs/errors/err_ngrok_1801) | Invalid IP Restriction. The policy `POLICY_ID` does not exist on your account. | | [ERR\_NGROK\_1802](/docs/errors/err_ngrok_1802) | Only one IP Restriction is allowed per type. Consider either deleting the existing `TYPE` restriction first, or updating it instead. | | [ERR\_NGROK\_1803](/docs/errors/err_ngrok_1803) | The provided IP Restriction type "`TYPE`" is invalid. Valid types: `SUPPORTED`. | | [ERR\_NGROK\_1804](/docs/errors/err_ngrok_1804) | An IP Restriction must specify at least one IP Policy. | | [ERR\_NGROK\_1805](/docs/errors/err_ngrok_1805) | Only Pay-as-you-go accounts with the Account Governance add-on can use dashboard IP restrictions. | | [ERR\_NGROK\_1806](/docs/errors/err_ngrok_1806) | Only Pay-as-you-go accounts with the Account Governance add-on can use API IP restrictions. | | [ERR\_NGROK\_1807](/docs/errors/err_ngrok_1807) | Only Pay-as-you-go accounts with the Account Governance add-on can use agent IP restrictions. | | [ERR\_NGROK\_1808](/docs/errors/err_ngrok_1808) | Only Pay-as-you-go accounts with the Account Governance add-on can use endpoints IP restrictions. | | [ERR\_NGROK\_1900](/docs/errors/err_ngrok_1900) | The provided certificate is in an unknown or unsupported format. The error encountered while parsing was: "`ERROR`" | | [ERR\_NGROK\_1901](/docs/errors/err_ngrok_1901) | No PEM-encoded private key was found in the creation request. | | [ERR\_NGROK\_1902](/docs/errors/err_ngrok_1902) | The provided private key is encrypted. | | [ERR\_NGROK\_1903](/docs/errors/err_ngrok_1903) | The provided private key is in an unknown or unsupported format. | | [ERR\_NGROK\_1904](/docs/errors/err_ngrok_1904) | Your account can't upload custom TLS certificates. | | [ERR\_NGROK\_1905](/docs/errors/err_ngrok_1905) | Your account can't use managed TLS certificates. | | [ERR\_NGROK\_1906](/docs/errors/err_ngrok_1906) | The domain `DOMAIN` is not a valid domain name. | | [ERR\_NGROK\_1907](/docs/errors/err_ngrok_1907) | The ip `IP` is not a valid IP address. | | [ERR\_NGROK\_1908](/docs/errors/err_ngrok_1908) | Invalid authority `AUTHORITY`. Valid authorities: `SUPPORTED`. | | [ERR\_NGROK\_1909](/docs/errors/err_ngrok_1909) | Invalid private key type `TYPE`. Valid types: `SUPPORTED`. | | [ERR\_NGROK\_1910](/docs/errors/err_ngrok_1910) | The provided PEM data contains a non-zero number of headers, which are not allowed. | | [ERR\_NGROK\_1911](/docs/errors/err_ngrok_1911) | The provided data is a valid PEM, but it has an unexpected type: `TYPE`. It must be "CERTIFICATE". | | [ERR\_NGROK\_1912](/docs/errors/err_ngrok_1912) | The upload contained `COUNT` certificates: `CNS`. Only a single certificate may be uploaded at a time. | | [ERR\_NGROK\_1913](/docs/errors/err_ngrok_1913) | The uploaded certificate's x509 Basic Constraints did not mark it as a certificate authority (see RFC 5280, 4.2.1.9). | | [ERR\_NGROK\_1914](/docs/errors/err_ngrok_1914) | The description is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_1915](/docs/errors/err_ngrok_1915) | Metadata is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_1916](/docs/errors/err_ngrok_1916) | The description is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_1917](/docs/errors/err_ngrok_1917) | Metadata is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_1918](/docs/errors/err_ngrok_1918) | Your account is not allowed to upload certificate authorities. | | [ERR\_NGROK\_1919](/docs/errors/err_ngrok_1919) | No certificate PEM data was sent. | | [ERR\_NGROK\_1920](/docs/errors/err_ngrok_1920) | The certificate data could not be parsed as PEM. Certificate data: "`PREFIX`". | | [ERR\_NGROK\_1921](/docs/errors/err_ngrok_1921) | The uploaded certificate PEM data exceeds the maximum length limit of `MAX_SIZE` bytes. | | [ERR\_NGROK\_1922](/docs/errors/err_ngrok_1922) | No PEM-encoded certificate was found in the creation request. | | [ERR\_NGROK\_1923](/docs/errors/err_ngrok_1923) | The provided certificate and private key do not match each other. | | [ERR\_NGROK\_1924](/docs/errors/err_ngrok_1924) | The provided private key is actually a certificate. | | [ERR\_NGROK\_1925](/docs/errors/err_ngrok_1925) | The provided certificate is actually a private key. | | [ERR\_NGROK\_1926](/docs/errors/err_ngrok_1926) | The uploaded certificate bundle contained the following certificates in this order: \[`ORDER`], however `INVALID` has not been signed by `INTERMEDIATE`. | | [ERR\_NGROK\_1927](/docs/errors/err_ngrok_1927) | The domain `DOMAIN` is not available for certificate management. | | [ERR\_NGROK\_1929](/docs/errors/err_ngrok_1929) | Invalid nameserver count. There should be `WANT` but only found `GOT` for `ZONE` | | [ERR\_NGROK\_1930](/docs/errors/err_ngrok_1930) | Nameservers are not set up correctly for `DOMAIN`, expected `WANT` but got `GOT` | | [ERR\_NGROK\_1934](/docs/errors/err_ngrok_1934) | Got error `ERROR` where parsing public key `PUBLICKEY` | | [ERR\_NGROK\_1935](/docs/errors/err_ngrok_1935) | `CERTTYPE` is not a valid SSH certificate type. Valid certificate types: `SUPPORTED`. | | [ERR\_NGROK\_1936](/docs/errors/err_ngrok_1936) | "`KEYTYPE`" is not a valid SSH key type. Valid key types: `SUPPORTED`. | | [ERR\_NGROK\_1937](/docs/errors/err_ngrok_1937) | The description is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_1938](/docs/errors/err_ngrok_1938) | Metadata is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_1939](/docs/errors/err_ngrok_1939) | Invalid key size `KEYSIZE`. Valid key sizes: `SUPPORTED`. | | [ERR\_NGROK\_1940](/docs/errors/err_ngrok_1940) | Key size is supported only with a key type of "rsa", but the specified key type was "`KEYTYPE`". | | [ERR\_NGROK\_1941](/docs/errors/err_ngrok_1941) | Invalid elliptic curve "`CURVE`". Valid elliptic curves: `SUPPORTED`. | | [ERR\_NGROK\_1942](/docs/errors/err_ngrok_1942) | Elliptic curve is supported only with a key type of "ecdsa", but the specified key type was "`KEYTYPE`". | | [ERR\_NGROK\_1943](/docs/errors/err_ngrok_1943) | The SSH CA Certificate duration must be greater than zero; got duration of `DURATION`. | | [ERR\_NGROK\_1945](/docs/errors/err_ngrok_1945) | The description is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_1946](/docs/errors/err_ngrok_1946) | Metadata is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_1947](/docs/errors/err_ngrok_1947) | The description is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_1948](/docs/errors/err_ngrok_1948) | Metadata is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_1949](/docs/errors/err_ngrok_1949) | The SSH CA `CA` does not exist. | | [ERR\_NGROK\_1950](/docs/errors/err_ngrok_1950) | Certificates are not supported for public keys of type `KEYTYPE`. | | [ERR\_NGROK\_1951](/docs/errors/err_ngrok_1951) | The SSH CA `CA` does not exist. | | [ERR\_NGROK\_1952](/docs/errors/err_ngrok_1952) | The public key to sign in the certificate is required, but no public key was specified. | | [ERR\_NGROK\_1953](/docs/errors/err_ngrok_1953) | Your account is not allowed to use SSH certificates. | | [ERR\_NGROK\_1954](/docs/errors/err_ngrok_1954) | Your account is limited to `MAX` Certificate Authorities. | | [ERR\_NGROK\_1956](/docs/errors/err_ngrok_1956) | Your account is limited to `MAX` SSH Certificate Authorities. | | [ERR\_NGROK\_1958](/docs/errors/err_ngrok_1958) | Your account is limited to `MAX` SSH Host Certificates. | | [ERR\_NGROK\_1960](/docs/errors/err_ngrok_1960) | Your account is limited to `MAX` SSH User Certificates. | | [ERR\_NGROK\_1962](/docs/errors/err_ngrok_1962) | SSH User Certificate duration must be greater than zero; got duration of `DURATION`. | | [ERR\_NGROK\_1963](/docs/errors/err_ngrok_1963) | SSH Host Certificate duration must be greater than zero; got duration of `DURATION`. | | [ERR\_NGROK\_1964](/docs/errors/err_ngrok_1964) | Can't delete SSH CA `ID` because an SSH certificate signed by it exists. Delete all host and user SSH certificates signed by this CA before deleting it. | | [ERR\_NGROK\_1965](/docs/errors/err_ngrok_1965) | The certificate `ID` does not exist. | | [ERR\_NGROK\_1966](/docs/errors/err_ngrok_1966) | The certificate `ID` could not be deleted because it has an attached Managed Certificate Policy. If this certificate is automatically generated, delete the domain reservation or use a different certificate type. | | [ERR\_NGROK\_2032](/docs/errors/err_ngrok_2032) | Webhook Validation middleware requires a secret for `PROVIDER`. | | [ERR\_NGROK\_2062](/docs/errors/err_ngrok_2062) | Paths given in HTTPMux rules must be either empty or absolute. Perhaps you meant to specify '/`PATH`? | | [ERR\_NGROK\_2063](/docs/errors/err_ngrok_2063) | The method `METHOD` was specified multiple times for the path `PATH`. Each method must be unique among all rules with the same path. | | [ERR\_NGROK\_2065](/docs/errors/err_ngrok_2065) | HTTPMux must have at least one rule. | | [ERR\_NGROK\_2067](/docs/errors/err_ngrok_2067) | Too many rules given to HTTPMux. You provided `VAL` rules, but the limit is `MAX`. | | [ERR\_NGROK\_2069](/docs/errors/err_ngrok_2069) | The method (`METHOD`) specified in an HTTPMux rule is invalid or otherwise not currently supported. | | [ERR\_NGROK\_2070](/docs/errors/err_ngrok_2070) | The path specified in an HTTPMux rule is too long. The path provided has `VAL` bytes, but the limit is `MAX`. | | [ERR\_NGROK\_2071](/docs/errors/err_ngrok_2071) | The path specified in an HTTPMux rule is unsupported or otherwise invalid. | | [ERR\_NGROK\_2087](/docs/errors/err_ngrok_2087) | The sendgrid verification key is not a base64 encoded ecdsa public key. The following error was encountered while trying to parse it: "`ERROR`" | | [ERR\_NGROK\_2089](/docs/errors/err_ngrok_2089) | Found duplicate routing rule. Each rule must be unique. | | [ERR\_NGROK\_2090](/docs/errors/err_ngrok_2090) | The user agent filter regular expression `REGEX` is an invalid regular expression: `ERR`. | | [ERR\_NGROK\_2200](/docs/errors/err_ngrok_2200) | Invalid policy expression. | | [ERR\_NGROK\_2201](/docs/errors/err_ngrok_2201) | Invalid policy action type `TYP`. | | [ERR\_NGROK\_2202](/docs/errors/err_ngrok_2202) | Action type `TYP` is not supported for `PHASE` traffic policy rule. | | [ERR\_NGROK\_2203](/docs/errors/err_ngrok_2203) | Failed to parse the configuration for policy action type `TYP`. | | [ERR\_NGROK\_2204](/docs/errors/err_ngrok_2204) | Configuration for policy action type `TYP` has invalid value: `FIELD`. | | [ERR\_NGROK\_2205](/docs/errors/err_ngrok_2205) | Policy action type `TYP` does not support the `PROTO` protocol. | | [ERR\_NGROK\_2206](/docs/errors/err_ngrok_2206) | A policy must define at least one action. | | [ERR\_NGROK\_2207](/docs/errors/err_ngrok_2207) | Policy action type `TYP` is unreachable due to a prior terminating action. | | [ERR\_NGROK\_2208](/docs/errors/err_ngrok_2208) | Failed to parse configuration for `PHASE` `TYPE`: exceeds maximum allowed name length of `MAX`, was `VAL`. | | [ERR\_NGROK\_2209](/docs/errors/err_ngrok_2209) | Failed to parse configuration for `PHASE` `TYPE`: exceeds maximum allowed value length of `MAX`, was `VAL`. | | [ERR\_NGROK\_2210](/docs/errors/err_ngrok_2210) | Failed to parse configuration for `PHASE` `TYPE`: name is invalid `NAME`. | | [ERR\_NGROK\_2211](/docs/errors/err_ngrok_2211) | Failed to parse configuration for `PHASE` `TYPE`: value is invalid `NAME`. | | [ERR\_NGROK\_2212](/docs/errors/err_ngrok_2212) | Failed to parse configuration for `PHASE` `TYPE`: can have at most `MAX` header values, but `VAL` were provided. | | [ERR\_NGROK\_2213](/docs/errors/err_ngrok_2213) | Failed to parse configuration for `PHASE` add-headers: contained an invalid template: `TEMPLATE`: `ERR`. | | [ERR\_NGROK\_2214](/docs/errors/err_ngrok_2214) | Failed to parse configuration for compress: contained an invalid algorithm: `ALGORITHM`. | | [ERR\_NGROK\_2215](/docs/errors/err_ngrok_2215) | Failed to parse configuration for `ACTION`: `CONFIG` must be between `MIN` and `MAX` inclusive, was `ACTUAL`. | | [ERR\_NGROK\_2216](/docs/errors/err_ngrok_2216) | Invalid IP Policy Reference(s): `REFERENCE`. | | [ERR\_NGROK\_2217](/docs/errors/err_ngrok_2217) | Invalid CIDR(s): `CIDR`. | | [ERR\_NGROK\_2218](/docs/errors/err_ngrok_2218) | Too many entries for `FIELD`. You provided `ENTRIES` entries, but the limit is `LIMIT`. | | [ERR\_NGROK\_2219](/docs/errors/err_ngrok_2219) | `FIELD` cannot be user-configured. | | [ERR\_NGROK\_2221](/docs/errors/err_ngrok_2221) | Invalid configuration for `ACTION`: `CONFIG` length of `ACTUAL` exceeded limit of `MAX`. | | [ERR\_NGROK\_2222](/docs/errors/err_ngrok_2222) | Invalid configuration for `ACTION`: `CONFIG` must be `VALID`, was `ACTUAL`. | | [ERR\_NGROK\_2223](/docs/errors/err_ngrok_2223) | Invalid configuration for `ACTION`: `CONFIG` cannot be empty. | | [ERR\_NGROK\_2224](/docs/errors/err_ngrok_2224) | Invalid configuration for action `ACTION`: failed to compile CEL template expressions in `FIELD`. | | [ERR\_NGROK\_2225](/docs/errors/err_ngrok_2225) | Invalid configuration for `ACTION`: `CONFIG` duration of `ACTUAL` exceeded limit of `MAX`. | | [ERR\_NGROK\_2226](/docs/errors/err_ngrok_2226) | Invalid configuration for `ACTION`: `CONFIG` must be a URL, but was `ACTUAL`. | | [ERR\_NGROK\_2227](/docs/errors/err_ngrok_2227) | Invalid configuration for `ACTION`: The issuer URL at `CONFIG` with a value of `ISSUERURL` did not return a valid OpenID Connect Discovery document. | | [ERR\_NGROK\_2228](/docs/errors/err_ngrok_2228) | Invalid configuration for `ACTION`: Failed to parse `CONFIG`: `REASON`. | | [ERR\_NGROK\_2229](/docs/errors/err_ngrok_2229) | Failed to validate Traffic Policy for unknown protocol `PROTO`.' | | [ERR\_NGROK\_2230](/docs/errors/err_ngrok_2230) | Invalid Traffic Policy phase `PHASE` for protocol `PROTO`. | | [ERR\_NGROK\_2231](/docs/errors/err_ngrok_2231) | Invalid configuration for `ACTION`: `CONFIG` contains duplicate values: `DUPLICATES`. | | [ERR\_NGROK\_2232](/docs/errors/err_ngrok_2232) | Failed to resolve validated configuration for policy action type `TYP`. | | [ERR\_NGROK\_2233](/docs/errors/err_ngrok_2233) | Failed to compile resolved configuration for policy action type `TYP`. | | [ERR\_NGROK\_2234](/docs/errors/err_ngrok_2234) | Invalid configuration for `ACTION`: `CONFIG` is missing required element `ELEMENT`. | | [ERR\_NGROK\_2235](/docs/errors/err_ngrok_2235) | The traffic policy provided is not guaranteed to handle traffic. Ensure traffic always results in an action, e.g. custom response or forward action. | | [ERR\_NGROK\_2236](/docs/errors/err_ngrok_2236) | The traffic policy was not able to be parsed: | | [ERR\_NGROK\_2237](/docs/errors/err_ngrok_2237) | Invalid configuration for `ACTION`: `CONFIG` length of `ACTUAL` does not satisfy minimum length of `MIN`. | | [ERR\_NGROK\_2238](/docs/errors/err_ngrok_2238) | Failed to resolve interpolated CEL expression. The expression was: `CEL` | | [ERR\_NGROK\_2239](/docs/errors/err_ngrok_2239) | Invalid policy: Only one auth action allowed per endpoint. | | [ERR\_NGROK\_2240](/docs/errors/err_ngrok_2240) | Invalid configuration for `ACTION`: `MISSING` is required when `SPECIFIED` is specified | | [ERR\_NGROK\_2241](/docs/errors/err_ngrok_2241) | Invalid configuration for `ACTION`: The domain `DOMAIN` cannot be used as it doesn't belong to the endpoint. | | [ERR\_NGROK\_2242](/docs/errors/err_ngrok_2242) | Invalid configuration for `ACTION`: `CONFIG` `DOMAIN` contains invalid puny code. | | [ERR\_NGROK\_2243](/docs/errors/err_ngrok_2243) | Invalid configuration for `ACTION`: `CONFIG` `DOMAIN` is not a valid domain. | | [ERR\_NGROK\_2244](/docs/errors/err_ngrok_2244) | Invalid configuration for `PHASE` `ACTION`: `CONFIG` can have at most `MAX` query key/value pairs, but `VAL` were provided. | | [ERR\_NGROK\_2245](/docs/errors/err_ngrok_2245) | Invalid configuration for `PHASE` `ACTION`: `CONFIG`: exceeds maximum allowed key length of `MAX`, was `VAL`. | | [ERR\_NGROK\_2246](/docs/errors/err_ngrok_2246) | Invalid configuration for `PHASE` `ACTION`: `CONFIG`: exceeds maximum allowed key length of `MAX`, was `VAL`. | | [ERR\_NGROK\_2247](/docs/errors/err_ngrok_2247) | Already validated url didn't parse during `STEP` step: `URL` `ERR` | | [ERR\_NGROK\_2248](/docs/errors/err_ngrok_2248) | HTTPS Edges do not support the on\_tcp\_connect Traffic Policy phase. Use a Cloud Endpoint instead. | | [ERR\_NGROK\_2249](/docs/errors/err_ngrok_2249) | Memory limit exceeded by `ACTION` action: `ERR` | | [ERR\_NGROK\_2250](/docs/errors/err_ngrok_2250) | Invalid configuration for `ACTION`: Nested variables are too deep. Maximum nesting allowed is `MAX` | | [ERR\_NGROK\_2252](/docs/errors/err_ngrok_2252) | Invalid format for set-vars: vars must be a list of maps of exactly size 1 | | [ERR\_NGROK\_2253](/docs/errors/err_ngrok_2253) | Failed to evaluate CEL expression on `FIELD` for `ACTION` at runtime: `ERR` | | [ERR\_NGROK\_2254](/docs/errors/err_ngrok_2254) | Failed to resolve CEL expression. The expression was: `CEL` | | [ERR\_NGROK\_2255](/docs/errors/err_ngrok_2255) | Invalid configuration for `ACTION`: Maximum `ELEMENT` allowed per action is `MAX` | | [ERR\_NGROK\_2256](/docs/errors/err_ngrok_2256) | The action `ACTION` is not available for your current plan. Upgrade to `PLANS` to use this action. | | [ERR\_NGROK\_2257](/docs/errors/err_ngrok_2257) | Invalid basic auth credential. Username `USERNAME` fails to meet requirements of RFC 7617 #2.1: `ERR` | | [ERR\_NGROK\_2258](/docs/errors/err_ngrok_2258) | Invalid basic auth credential. The password of user `USERNAME` fails to meet requirements of RFC 7617 #2.1: `ERR` | | [ERR\_NGROK\_2259](/docs/errors/err_ngrok_2259) | No endpoints exist in this account | | [ERR\_NGROK\_2260](/docs/errors/err_ngrok_2260) | No endpoints could be found matching the supplied combination of labels | | [ERR\_NGROK\_2261](/docs/errors/err_ngrok_2261) | Failed to parse the configuration for policy action type `TYP`. | | [ERR\_NGROK\_2262](/docs/errors/err_ngrok_2262) | Internal HTTP/HTTPS endpoints do not support the on\_tcp\_connect Traffic Policy phase. Consider using a TCP or TLS endpoint instead. | | [ERR\_NGROK\_2263](/docs/errors/err_ngrok_2263) | This policy includes paid traffic policy features that are not allowed on your current plan. Upgrade to use this policy. | | [ERR\_NGROK\_2264](/docs/errors/err_ngrok_2264) | Policy configuration has exceeded its rule per policy limit of `LIMIT`. Please check [https://ngrok.com/features/traffic-policy](https://ngrok.com/features/traffic-policy) to view your options or contact us at [support@ngrok.com](mailto:support@ngrok.com). | | [ERR\_NGROK\_2265](/docs/errors/err_ngrok_2265) | Policy configuration has exceeded its `TYP` per rule limit of `LIMIT` during `PHASE` at index `IDX`. Please check [https://ngrok.com/features/traffic-policy](https://ngrok.com/features/traffic-policy) to view your options or contact us at [support@ngrok.com](mailto:support@ngrok.com). | | [ERR\_NGROK\_2266](/docs/errors/err_ngrok_2266) | Traffic policy contains more than one AI Gateway Action. Only one AI Gateway action is allowed per traffic policy. | | [ERR\_NGROK\_2267](/docs/errors/err_ngrok_2267) | Configuring provider API keys or key selection in Traffic Policy is no longer supported for standard providers. Attach provider keys to your AI Gateway API Key instead: [https://ngrok.com/docs/ai-gateway/guides/migrate-to-attached-provider-keys](https://ngrok.com/docs/ai-gateway/guides/migrate-to-attached-provider-keys). Custom providers can still configure API keys in Traffic Policy. | | [ERR\_NGROK\_2300](/docs/errors/err_ngrok_2300) | Internal gateway error occurred while processing policy action: `REASON` | | [ERR\_NGROK\_3002](/docs/errors/err_ngrok_3002) | Error performing TLS handshake: `ERR` | | [ERR\_NGROK\_3003](/docs/errors/err_ngrok_3003) | This client does not support TLS SNI, but the endpoint's TLS configuration requires SNI. See [https://ngrok.com/docs/cloud-edge/edges/tls#compatible-clients](https://ngrok.com/docs/cloud-edge/edges/tls#compatible-clients) | | [ERR\_NGROK\_3004](/docs/errors/err_ngrok_3004) | The server returned an invalid or incomplete HTTP response. | | [ERR\_NGROK\_3005](/docs/errors/err_ngrok_3005) | A connection to the server not be established. | | [ERR\_NGROK\_3006](/docs/errors/err_ngrok_3006) | The server refused our connection | | [ERR\_NGROK\_3007](/docs/errors/err_ngrok_3007) | ngrok gateway error | | [ERR\_NGROK\_3008](/docs/errors/err_ngrok_3008) | None of the configured backends responded in time | | [ERR\_NGROK\_3009](/docs/errors/err_ngrok_3009) | Received an HTTP network error (status code 0) from a backend | | [ERR\_NGROK\_3010](/docs/errors/err_ngrok_3010) | Received an HTTP response with unknown status code `CODE` from a backend | | [ERR\_NGROK\_3100](/docs/errors/err_ngrok_3100) | Your session has expired due to age. | | [ERR\_NGROK\_3101](/docs/errors/err_ngrok_3101) | We lack permission to retrieve your data from `PROVIDER` and cannot continue. | | [ERR\_NGROK\_3102](/docs/errors/err_ngrok_3102) | `PROVIDER` requires that you correct an issue with your account prior to using this website. | | [ERR\_NGROK\_3103](/docs/errors/err_ngrok_3103) | `PROVIDER` requires that you correct an issue with your account prior to using this website: | | [ERR\_NGROK\_3104](/docs/errors/err_ngrok_3104) | The email "`EMAIL`" associated with your `PROVIDER` account is not valid. | | [ERR\_NGROK\_3105](/docs/errors/err_ngrok_3105) | You lack the required permission to use this site. | | [ERR\_NGROK\_3106](/docs/errors/err_ngrok_3106) | The initial information required to authenticate with `PROVIDER` is invalid. | | [ERR\_NGROK\_3107](/docs/errors/err_ngrok_3107) | The state parameter used to validate your request is missing. | | [ERR\_NGROK\_3108](/docs/errors/err_ngrok_3108) | The state parameter used to validate your request cannot be read. | | [ERR\_NGROK\_3109](/docs/errors/err_ngrok_3109) | `PROVIDER` did not provide an email for your account. | | [ERR\_NGROK\_3110](/docs/errors/err_ngrok_3110) | The email permission from `PROVIDER` was not granted but is required. Ensure that the email permission is granted. | | [ERR\_NGROK\_3111](/docs/errors/err_ngrok_3111) | All requested permissions with `PROVIDER` must be granted in order to use this site, but the following were not: `PERMISSIONS`. | | [ERR\_NGROK\_3112](/docs/errors/err_ngrok_3112) | `PROVIDER` has returned an error when attempting to authorize you to use this site: | | [ERR\_NGROK\_3113](/docs/errors/err_ngrok_3113) | `PROVIDER` denied access to this application when retrieving your information. This is likely caused by expiration or revocation of your authorization. | | [ERR\_NGROK\_3114](/docs/errors/err_ngrok_3114) | In order to continue, you must authorize this application's access to `PROVIDER`. | | [ERR\_NGROK\_3115](/docs/errors/err_ngrok_3115) | The redirect\_uri configured for this application is not properly configured or whitelisted with `PROVIDER`. | | [ERR\_NGROK\_3116](/docs/errors/err_ngrok_3116) | `PROVIDER` denied access to your data when attempting to authorize you. | | [ERR\_NGROK\_3117](/docs/errors/err_ngrok_3117) | The initial link used to authorize with `PROVIDER` had unsupported parameters. | | [ERR\_NGROK\_3118](/docs/errors/err_ngrok_3118) | `PROVIDER` denied access to your data. | | [ERR\_NGROK\_3119](/docs/errors/err_ngrok_3119) | Logging in with `PROVIDER` took more than 15 minutes and was rejected for security. | | [ERR\_NGROK\_3120](/docs/errors/err_ngrok_3120) | The state parameter required to finish authorization with `PROVIDER` is missing. | | [ERR\_NGROK\_3121](/docs/errors/err_ngrok_3121) | The nonce cookie required to finish authorization with `PROVIDER` is missing. | | [ERR\_NGROK\_3122](/docs/errors/err_ngrok_3122) | The access code from `PROVIDER` has expired and cannot be used. | | [ERR\_NGROK\_3123](/docs/errors/err_ngrok_3123) | The access code from `PROVIDER` has already been used. | | [ERR\_NGROK\_3124](/docs/errors/err_ngrok_3124) | The state parameter required to finish authorization with `PROVIDER` does not match this host. | | [ERR\_NGROK\_3125](/docs/errors/err_ngrok_3125) | `PROVIDER` denied access to your data. | | [ERR\_NGROK\_3126](/docs/errors/err_ngrok_3126) | `PROVIDER` rejected authorization due to a invalid or unknown scope requested by this application. | | [ERR\_NGROK\_3127](/docs/errors/err_ngrok_3127) | `PROVIDER` rejected authorization due to invalid or expired credentials. | | [ERR\_NGROK\_3150](/docs/errors/err_ngrok_3150) | An error occurred when retrieving your session. | | [ERR\_NGROK\_3151](/docs/errors/err_ngrok_3151) | `PROVIDER` rejected use of your session's OAuth token: "`ERROR`". Your session is invalid and cannot continue. | | [ERR\_NGROK\_3152](/docs/errors/err_ngrok_3152) | An invalid request was sent to `PROVIDER` but its response is required in order to continue. This is a bug and it has been reported; please try again in a couple of hours to see if it has been resolved. | | [ERR\_NGROK\_3160](/docs/errors/err_ngrok_3160) | An unexpected error occurred when communicating with `PROVIDER`. | | [ERR\_NGROK\_3161](/docs/errors/err_ngrok_3161) | `PROVIDER` has throttled this application and cannot be reached right now. | | [ERR\_NGROK\_3162](/docs/errors/err_ngrok_3162) | `PROVIDER` has throttled this application and cannot be reached right now. | | [ERR\_NGROK\_3163](/docs/errors/err_ngrok_3163) | An error occurred when finishing authorization with `PROVIDER`. This is likely caused by authorization taking too long. | | [ERR\_NGROK\_3164](/docs/errors/err_ngrok_3164) | An error occurred when authorizing with `PROVIDER`: | | [ERR\_NGROK\_3165](/docs/errors/err_ngrok_3165) | `PROVIDER` is too busy to handle requests and authorization cannot be completed at this time. | | [ERR\_NGROK\_3166](/docs/errors/err_ngrok_3166) | `PROVIDER` encountered an error and authorization cannot be completed at this time. | | [ERR\_NGROK\_3167](/docs/errors/err_ngrok_3167) | `PROVIDER` is too busy to handle requests and required data cannot be retrieved right now. Wait a few minutes before trying again. | | [ERR\_NGROK\_3200](/docs/errors/err_ngrok_3200) | The endpoint `ENDPOINTURL` is offline. | | [ERR\_NGROK\_3202](/docs/errors/err_ngrok_3202) | Circuit breaker tripped | | [ERR\_NGROK\_3203](/docs/errors/err_ngrok_3203) | Request blocked by IP Policy | | [ERR\_NGROK\_3204](/docs/errors/err_ngrok_3204) | Request failed webhook verification | | [ERR\_NGROK\_3205](/docs/errors/err_ngrok_3205) | Request blocked by Endpoints IP Restriction | | [ERR\_NGROK\_3206](/docs/errors/err_ngrok_3206) | Expected a websocket request with a "Connection: upgrade" header but did not receive one. | | [ERR\_NGROK\_3208](/docs/errors/err_ngrok_3208) | The ngrok account that was hosting this webpage has been banned for violation of ngrok's Terms of Service. | | [ERR\_NGROK\_3209](/docs/errors/err_ngrok_3209) | Tunnel not found | | [ERR\_NGROK\_3210](/docs/errors/err_ngrok_3210) | Error forwarding the connection to the ngrok agent | | [ERR\_NGROK\_3211](/docs/errors/err_ngrok_3211) | The server does not authorize requests from your user-agent | | [ERR\_NGROK\_3212](/docs/errors/err_ngrok_3212) | Tunnel `HOSTNAME` has multiple bindings but no binding was specified | | [ERR\_NGROK\_3213](/docs/errors/err_ngrok_3213) | Upgrade request origin forbidden | | [ERR\_NGROK\_3214](/docs/errors/err_ngrok_3214) | exceeded maximum number of redirects `MAX`: `URLS` | | [ERR\_NGROK\_3300](/docs/errors/err_ngrok_3300) | URL "state" parameter is invalid | | [ERR\_NGROK\_3301](/docs/errors/err_ngrok_3301) | URL "state" parameter is expired. | | [ERR\_NGROK\_3302](/docs/errors/err_ngrok_3302) | URL "state" parameter is invalid. | | [ERR\_NGROK\_3303](/docs/errors/err_ngrok_3303) | URL "state" parameter is invalid. | | [ERR\_NGROK\_3304](/docs/errors/err_ngrok_3304) | URL "state" parameter is invalid. | | [ERR\_NGROK\_3305](/docs/errors/err_ngrok_3305) | This request has expired. Try again. | | [ERR\_NGROK\_3306](/docs/errors/err_ngrok_3306) | URL "state" parameter is invalid. | | [ERR\_NGROK\_3307](/docs/errors/err_ngrok_3307) | URL "state" parameter is invalid. | | [ERR\_NGROK\_3308](/docs/errors/err_ngrok_3308) | URL "state" parameter is invalid. | | [ERR\_NGROK\_3309](/docs/errors/err_ngrok_3309) | The route match type `TYP` is incompatible with OAuth, OIDC, or SAML. Compatible selector match types are "EXACT\_PATH" or "PATH\_PREFIX". | | [ERR\_NGROK\_3310](/docs/errors/err_ngrok_3310) | URL "state" parameter is invalid. | | [ERR\_NGROK\_3311](/docs/errors/err_ngrok_3311) | URL "state" parameter is invalid. | | [ERR\_NGROK\_3312](/docs/errors/err_ngrok_3312) | URL "state" parameter is invalid. | | [ERR\_NGROK\_3313](/docs/errors/err_ngrok_3313) | Invalid Auth Cookie Domain: `DOMAIN` | | [ERR\_NGROK\_3401](/docs/errors/err_ngrok_3401) | Your request lacks valid credentials. | | [ERR\_NGROK\_3403](/docs/errors/err_ngrok_3403) | You are forbidden from making this request. | | [ERR\_NGROK\_3406](/docs/errors/err_ngrok_3406) | Token could not be found in request. | | [ERR\_NGROK\_3408](/docs/errors/err_ngrok_3408) | Configured HTTP Header `TYP` was not found in request. | | [ERR\_NGROK\_3411](/docs/errors/err_ngrok_3411) | Failed to parse JSON body. | | [ERR\_NGROK\_3412](/docs/errors/err_ngrok_3412) | Failed to parse form. | | [ERR\_NGROK\_3413](/docs/errors/err_ngrok_3413) | Configured prefix `TYP` not found. | | [ERR\_NGROK\_3414](/docs/errors/err_ngrok_3414) | Failed to read content-type header. | | [ERR\_NGROK\_3415](/docs/errors/err_ngrok_3415) | Tokens in body not supported for HTTP method `TYP`. | | [ERR\_NGROK\_3416](/docs/errors/err_ngrok_3416) | Token could not be parsed, possibly malformed. | | [ERR\_NGROK\_3422](/docs/errors/err_ngrok_3422) | Failed to fetch JWKS. | | [ERR\_NGROK\_3501](/docs/errors/err_ngrok_3501) | ForwardInternal may only forward through `MAX` endpoints. | | [ERR\_NGROK\_3502](/docs/errors/err_ngrok_3502) | The forward-internal action can't forward between endpoints that use different protocols. This endpoint uses `PARENTPROTO`, and the endpoint you're forwarding to uses `TARGETPROTO`. | | [ERR\_NGROK\_3601](/docs/errors/err_ngrok_3601) | The response received from the http-request target had a size exceeding the maximum allowed. | | [ERR\_NGROK\_3602](/docs/errors/err_ngrok_3602) | The configured http-request timeout was reached before a successful request could be made. | | [ERR\_NGROK\_3603](/docs/errors/err_ngrok_3603) | Request rejected, A loop was detected. | | [ERR\_NGROK\_3604](/docs/errors/err_ngrok_3604) | HTTP Status Code `CODE` does not allow including a response body. | | [ERR\_NGROK\_3700](/docs/errors/err_ngrok_3700) | The request was blocked by the WAF. | | [ERR\_NGROK\_3701](/docs/errors/err_ngrok_3701) | Something went wrong with the WAF. | | [ERR\_NGROK\_3702](/docs/errors/err_ngrok_3702) | The response was blocked by the WAF. | | [ERR\_NGROK\_3810](/docs/errors/err_ngrok_3810) | Request timed out after `ACTIONTIMEOUT`. Try a shorter prompt or increase your timeout. See [https://ngrok.com/docs/ai-gateway/reference/error-codes#err\_ngrok\_3810](https://ngrok.com/docs/ai-gateway/reference/error-codes#err_ngrok_3810) for more information. | | [ERR\_NGROK\_3811](/docs/errors/err_ngrok_3811) | Input too large: `INPUTTOKENS` tokens (max `MAXALLOWED`). Shorten your prompt. See [https://ngrok.com/docs/ai-gateway/reference/error-codes#err\_ngrok\_3811](https://ngrok.com/docs/ai-gateway/reference/error-codes#err_ngrok_3811) for more information. | | [ERR\_NGROK\_3820](/docs/errors/err_ngrok_3820) | AI Gateway has a new home at [https://app.ngrok.ai](https://app.ngrok.ai). The old ai-gateway traffic policy action has been retired. Existing credits have been migrated to [https://app.ngrok.ai](https://app.ngrok.ai). If you have any questions or issues, please reach out to [support@ngrok.com](mailto:support@ngrok.com). | | [ERR\_NGROK\_3900](/docs/errors/err_ngrok_3900) | CEL interpolation was unsuccessful in action `ACTIONTYPE` field `FIELD` | | [ERR\_NGROK\_3901](/docs/errors/err_ngrok_3901) | CEL interpolation in action `ACTIONTYPE` field `FIELD` did not result in a valid regex. | | [ERR\_NGROK\_3902](/docs/errors/err_ngrok_3902) | Buffering for the body was not successfully initialized for action type `ACTIONTYPE`. | | [ERR\_NGROK\_3903](/docs/errors/err_ngrok_3903) | The body could not be successfully buffered for action type `ACTIONTYPE` | | [ERR\_NGROK\_3904](/docs/errors/err_ngrok_3904) | The body content could not be successfully modified for action type `ACTIONTYPE` | | [ERR\_NGROK\_3905](/docs/errors/err_ngrok_3905) | The content-length header could not be successfully updated for action type `ACTIONTYPE` | | [ERR\_NGROK\_4001](/docs/errors/err_ngrok_4001) | Plan `PLAN_ID` does not exist. | | [ERR\_NGROK\_4003](/docs/errors/err_ngrok_4003) | Account name must not be empty. | | [ERR\_NGROK\_4004](/docs/errors/err_ngrok_4004) | Account name exceeds max length, `LENGTH` bytes, max: `MAX`. | | [ERR\_NGROK\_4005](/docs/errors/err_ngrok_4005) | Another account is already configured with the GSuite domain `DOMAIN`. | | [ERR\_NGROK\_4011](/docs/errors/err_ngrok_4011) | Your password must be at least `LENGTH` characters. | | [ERR\_NGROK\_4012](/docs/errors/err_ngrok_4012) | Your account does not have access to `FEATURE`. | | [ERR\_NGROK\_4013](/docs/errors/err_ngrok_4013) | You may not create a new account because you are already a member of the free account "`ACCOUNT_NAME`". Upgrade or delete that account first before creating a new account. | | [ERR\_NGROK\_4014](/docs/errors/err_ngrok_4014) | The account `NAME` was suspended. It may not be deleted. Contact [support@ngrok.com](mailto:support@ngrok.com) if you believe this is an error. | | [ERR\_NGROK\_4015](/docs/errors/err_ngrok_4015) | The account `NAME` was banned for violation of the terms of service. It may not be deleted. Contact [support@ngrok.com](mailto:support@ngrok.com) if you believe this is an error. | | [ERR\_NGROK\_4016](/docs/errors/err_ngrok_4016) | You may not create a new account because another account you were the administrator of was suspended. Contact [support@ngrok.com](mailto:support@ngrok.com) if you believe this is an error. | | [ERR\_NGROK\_4017](/docs/errors/err_ngrok_4017) | You may not create a new account because another account you were the administrator of was banned for violation of the terms of service. Contact [support@ngrok.com](mailto:support@ngrok.com) if you believe this is an error. | | [ERR\_NGROK\_4018](/docs/errors/err_ngrok_4018) | This ngrok session is not authenticated. ngrok requires an account and a valid credential to start a session. | | [ERR\_NGROK\_4019](/docs/errors/err_ngrok_4019) | This account cannot be deleted because it has an active billing subscription. Contact [support@ngrok.com](mailto:support@ngrok.com) for help deleting this account. | | [ERR\_NGROK\_4020](/docs/errors/err_ngrok_4020) | Your password must be less then `LENGTH` characters. | | [ERR\_NGROK\_4021](/docs/errors/err_ngrok_4021) | This account cannot be deleted because it has an unpaid invoice. Contact [support@ngrok.com](mailto:support@ngrok.com) for help deleting this account. | | [ERR\_NGROK\_4022](/docs/errors/err_ngrok_4022) | You do not have permission to change the observability capture settings for this account. Contact an admin for this account to change these settings. | | [ERR\_NGROK\_4023](/docs/errors/err_ngrok_4023) | Your account does not have access to `FEATURE`. | | [ERR\_NGROK\_4024](/docs/errors/err_ngrok_4024) | Your account plan cannot be downgraded: `REASON` | | [ERR\_NGROK\_4025](/docs/errors/err_ngrok_4025) | Your account plan cannot be upgraded: `REASON` | | [ERR\_NGROK\_4026](/docs/errors/err_ngrok_4026) | Your account has run out of credit, please upgrade to continue using paid actions, or use your dev domain and remove all paid traffic policy from it. | | [ERR\_NGROK\_4027](/docs/errors/err_ngrok_4027) | Your account’s included bandwidth has been depleted. If you still have credit, you may still use it to buy more bandwidth. | | [ERR\_NGROK\_4028](/docs/errors/err_ngrok_4028) | A reason is required to change MFA enforcement for this account. | | [ERR\_NGROK\_4029](/docs/errors/err_ngrok_4029) | Just-in-Time provisioning now requires Domain Control. Contact ngrok support at [support@ngrok.com](mailto:support@ngrok.com). | | [ERR\_NGROK\_4100](/docs/errors/err_ngrok_4100) | The email or password you entered is not valid. | | [ERR\_NGROK\_4101](/docs/errors/err_ngrok_4101) | A user with the email address `EMAIL` already exists. | | [ERR\_NGROK\_4103](/docs/errors/err_ngrok_4103) | `EMAIL` is not a valid email address. | | [ERR\_NGROK\_4104](/docs/errors/err_ngrok_4104) | User name must not be empty. | | [ERR\_NGROK\_4105](/docs/errors/err_ngrok_4105) | User name exceeds max length, `LENGTH` bytes, max: `MAX`. | | [ERR\_NGROK\_4106](/docs/errors/err_ngrok_4106) | Your user `EMAIL` was suspended for violation of the terms of service. It may not be deleted. Contact [support@ngrok.com](mailto:support@ngrok.com) if you believe this is an error. | | [ERR\_NGROK\_4107](/docs/errors/err_ngrok_4107) | Your user `EMAIL` was banned for violation of the terms of service. It may not be deleted. Contact [support@ngrok.com](mailto:support@ngrok.com) if you believe this is an error. | | [ERR\_NGROK\_4108](/docs/errors/err_ngrok_4108) | Sign ups are disallowed for the email provider "`DOMAIN`". Sign up with a different email provider. | | [ERR\_NGROK\_4109](/docs/errors/err_ngrok_4109) | Service User name must not be empty. | | [ERR\_NGROK\_4110](/docs/errors/err_ngrok_4110) | Service User name exceeds max length, `LENGTH` bytes, max: `MAX`. | | [ERR\_NGROK\_4111](/docs/errors/err_ngrok_4111) | You must be an admin to create, update, or delete Bots. | | [ERR\_NGROK\_4112](/docs/errors/err_ngrok_4112) | Service User names must be unique within an account. | | [ERR\_NGROK\_4114](/docs/errors/err_ngrok_4114) | The email domain "`DOMAIN`" does not allow self-serve user registrations. Contact your ngrok administrator or [support@ngrok.com](mailto:support@ngrok.com) for access. | | [ERR\_NGROK\_4115](/docs/errors/err_ngrok_4115) | The email domain "`DOMAIN`" does not allow account creation. Contact your ngrok administrator or [support@ngrok.com](mailto:support@ngrok.com) for access. | | [ERR\_NGROK\_4116](/docs/errors/err_ngrok_4116) | The domain of the email entered is registered under a domain control and must signup via SSO | | [ERR\_NGROK\_4117](/docs/errors/err_ngrok_4117) | The email address `EMAIL` already exists under a similar alias. Use that email address to login. | | [ERR\_NGROK\_4118](/docs/errors/err_ngrok_4118) | Sign ups are disallowed for the email provided "`EMAIL`". Sign up with a different email address. | | [ERR\_NGROK\_4119](/docs/errors/err_ngrok_4119) | This account does not have access to service users, please upgrade to a paid plan. | | [ERR\_NGROK\_4200](/docs/errors/err_ngrok_4200) | You may not delete all or your logins, you must have at least one. | | [ERR\_NGROK\_4201](/docs/errors/err_ngrok_4201) | This `LOGIN` login is already connected to another ngrok user. | | [ERR\_NGROK\_4202](/docs/errors/err_ngrok_4202) | A reason is required to convert a user to password login. | | [ERR\_NGROK\_4203](/docs/errors/err_ngrok_4203) | This user already has an SSO identity bound to a different identity provider. | | [ERR\_NGROK\_4300](/docs/errors/err_ngrok_4300) | No user with the email address `EMAIL` exists. | | [ERR\_NGROK\_4301](/docs/errors/err_ngrok_4301) | You have not set a password for this account. Login using Google or GitHub instead. | | [ERR\_NGROK\_4302](/docs/errors/err_ngrok_4302) | Unable to redeem password: `REASON` | | [ERR\_NGROK\_4303](/docs/errors/err_ngrok_4303) | A message template is required when bulk expiring passwords. | | [ERR\_NGROK\_4304](/docs/errors/err_ngrok_4304) | At least one email or user ID must be provided. | | [ERR\_NGROK\_4305](/docs/errors/err_ngrok_4305) | Message template is not valid: `REASON` | | [ERR\_NGROK\_4400](/docs/errors/err_ngrok_4400) | An email address is required to invite a new user | | [ERR\_NGROK\_4401](/docs/errors/err_ngrok_4401) | The invited email address is too long: `LENGTH` bytes. Max `MAX` bytes. | | [ERR\_NGROK\_4402](/docs/errors/err_ngrok_4402) | The invited user would exceed your current team size limit of `TEAM_SIZE`. Add another seat at: [https://dashboard.ngrok.com/billing/choose-a-plan](https://dashboard.ngrok.com/billing/choose-a-plan). Then you'll be able to invite your team. | | [ERR\_NGROK\_4403](/docs/errors/err_ngrok_4403) | You cannot invite any more users. Contact [support@ngrok.com](mailto:support@ngrok.com) if you believe this is an error. | | [ERR\_NGROK\_4404](/docs/errors/err_ngrok_4404) | Unable to redeem invitation: `REASON` | | [ERR\_NGROK\_4405](/docs/errors/err_ngrok_4405) | `EMAIL` is not a valid email address. | | [ERR\_NGROK\_4407](/docs/errors/err_ngrok_4407) | You are not currently permitted to enroll in the beta. | | [ERR\_NGROK\_4408](/docs/errors/err_ngrok_4408) | Feature request suggestions must be between 1 and `MAXLEN` characters, got `LENGTH`. | | [ERR\_NGROK\_4409](/docs/errors/err_ngrok_4409) | Your account is not allowed to set invitation permissions. | | [ERR\_NGROK\_4410](/docs/errors/err_ngrok_4410) | You do not have permissions to invite an administrator to the account. Only administrators may invite other administrators. | | [ERR\_NGROK\_4411](/docs/errors/err_ngrok_4411) | You do not have permissions to invite users to the account. | | [ERR\_NGROK\_4412](/docs/errors/err_ngrok_4412) | You may not invite users to the account with higher permissions than your own. | | [ERR\_NGROK\_4413](/docs/errors/err_ngrok_4413) | Developer Read Write must be set on a user if their permissions include Invite. | | [ERR\_NGROK\_4414](/docs/errors/err_ngrok_4414) | You are not allowed to create invitations, this account uses SCIM 2.0 to manage members. | | [ERR\_NGROK\_4415](/docs/errors/err_ngrok_4415) | You are not allowed to redeem this invitation, this account uses SCIM 2.0 to manage members. | | [ERR\_NGROK\_4416](/docs/errors/err_ngrok_4416) | Your email does not match the email intended for this invitation, `EMAIL`. | | [ERR\_NGROK\_4451](/docs/errors/err_ngrok_4451) | A user with the email address `EMAIL` already exists. | | [ERR\_NGROK\_4452](/docs/errors/err_ngrok_4452) | The email address being verified, `EMAIL`, belongs to another user. | | [ERR\_NGROK\_4453](/docs/errors/err_ngrok_4453) | A similar email address to the one being verified, `EMAIL`, belongs to another user. | | [ERR\_NGROK\_4454](/docs/errors/err_ngrok_4454) | SCIM-managed users cannot change their email. Your email is managed by your identity provider. | | [ERR\_NGROK\_4455](/docs/errors/err_ngrok_4455) | Users belonging to accounts that enforce SSO cannot change their email. | | [ERR\_NGROK\_4500](/docs/errors/err_ngrok_4500) | The URL `URL` is not a valid tcp address or URL. Use either hostname:port or a full URL including protocol for HTTP or TLS endpoints. | | [ERR\_NGROK\_4501](/docs/errors/err_ngrok_4501) | The TCP port `PORT` in `HOST` is not a valid numerical TCP port. | | [ERR\_NGROK\_4502](/docs/errors/err_ngrok_4502) | The TCP port `PORT` for `HOST` is not within the range of possible ngrok ports. | | [ERR\_NGROK\_4503](/docs/errors/err_ngrok_4503) | The IP `IP` is not a known ngrok TCP IP. | | [ERR\_NGROK\_4504](/docs/errors/err_ngrok_4504) | The host `HOST` is not a known ngrok TCP hostname. | | [ERR\_NGROK\_4701](/docs/errors/err_ngrok_4701) | You must only specify a single SSO configuration but you specified both SAML and OpenID Connect. | | [ERR\_NGROK\_4702](/docs/errors/err_ngrok_4702) | You cannot delete your last IdP provider when SSO enforced is enabled | | [ERR\_NGROK\_5000](/docs/errors/err_ngrok_5000) | Your account is limited to `MAX` `RESOURCE`. | | [ERR\_NGROK\_5001](/docs/errors/err_ngrok_5001) | Your account is rate limited to `MAX` `RESOURCE` per `INTERVAL`. | | [ERR\_NGROK\_5100](/docs/errors/err_ngrok_5100) | An event destination must specify a destination target. Valid targets include: `TYPE`. | | [ERR\_NGROK\_5102](/docs/errors/err_ngrok_5102) | You have reached the maximum of `MAX` destinations for your account. | | [ERR\_NGROK\_5103](/docs/errors/err_ngrok_5103) | No event destinations with ID: `ID` associated with this account. | | [ERR\_NGROK\_5104](/docs/errors/err_ngrok_5104) | Event Destination role ARN "`ARN`" is not a valid ARN for an aws IAM role. Error: `MSG`. | | [ERR\_NGROK\_5105](/docs/errors/err_ngrok_5105) | Event Destination role is missing an external ID. | | [ERR\_NGROK\_5106](/docs/errors/err_ngrok_5106) | Event Destination target is missing the auth field. | | [ERR\_NGROK\_5107](/docs/errors/err_ngrok_5107) | Event Destination auth is missing a role or credentials to send events to the destination target. | | [ERR\_NGROK\_5108](/docs/errors/err_ngrok_5108) | Update to target type not allowed. Create a new destination if you would like to send to a new target. | | [ERR\_NGROK\_5112](/docs/errors/err_ngrok_5112) | Event Destination "`ID`" cannot be deleted because it is still in use by at least one Event Stream | | [ERR\_NGROK\_5115](/docs/errors/err_ngrok_5115) | An event destination may not specify more than one destination target. | | [ERR\_NGROK\_5116](/docs/errors/err_ngrok_5116) | An event destination may not specify more than one type of authentication. | | [ERR\_NGROK\_5117](/docs/errors/err_ngrok_5117) | The property `NAME` is an immutable property and may not be updated. Create a new destination instead. | | [ERR\_NGROK\_5118](/docs/errors/err_ngrok_5118) | The partition (`VALUE`) of the provided `FIELD` (`ARN`) is invalid. | | [ERR\_NGROK\_5119](/docs/errors/err_ngrok_5119) | The service (`VALUE`) of the provided `FIELD` (`ARN`) is invalid. | | [ERR\_NGROK\_5120](/docs/errors/err_ngrok_5120) | The provided `FIELD` (`ARN`) is invalid. Make sure it matches the ARN you obtained from Amazon exactly. | | [ERR\_NGROK\_5122](/docs/errors/err_ngrok_5122) | `ERR` | | [ERR\_NGROK\_5123](/docs/errors/err_ngrok_5123) | Event Subscription `ID` not found. | | [ERR\_NGROK\_5124](/docs/errors/err_ngrok_5124) | Your account can't use the event subscriptions feature. | | [ERR\_NGROK\_5125](/docs/errors/err_ngrok_5125) | Source `TYP` not found on Event Subscription `ID`. | | [ERR\_NGROK\_5126](/docs/errors/err_ngrok_5126) | You have reached the maximum of `MAX` destinations on a subscription. | | [ERR\_NGROK\_5127](/docs/errors/err_ngrok_5127) | The event type for each source must be unique within a subscription. | | [ERR\_NGROK\_5128](/docs/errors/err_ngrok_5128) | Your account can't create event destinations. | | [ERR\_NGROK\_5129](/docs/errors/err_ngrok_5129) | The ARN (`ARN`) is not valid for the `SERVICE` service. | | [ERR\_NGROK\_5130](/docs/errors/err_ngrok_5130) | The ARN (`ARN`) is not valid for the `RESOURCE` resource type. | | [ERR\_NGROK\_5131](/docs/errors/err_ngrok_5131) | You have reached the maximum of `MAX` event subscriptions for your account. | | [ERR\_NGROK\_5132](/docs/errors/err_ngrok_5132) | The Datadog API key provided is not authorized to send events into Datadog: `ERR` | | [ERR\_NGROK\_5133](/docs/errors/err_ngrok_5133) | Event Destination target is missing API Key. | | [ERR\_NGROK\_5134](/docs/errors/err_ngrok_5134) | The Datadog site provided (`DDSITE`) is not valid, please see the "SITE" column from [https://docs.datadoghq.com/getting\_started/site/#access-the-datadog-site](https://docs.datadoghq.com/getting_started/site/#access-the-datadog-site) | | [ERR\_NGROK\_5135](/docs/errors/err_ngrok_5135) | Your account can't use the event source filter feature. | | [ERR\_NGROK\_5136](/docs/errors/err_ngrok_5136) | The provided azure logs ingestion data collection rule ID is missing or invalid. | | [ERR\_NGROK\_5137](/docs/errors/err_ngrok_5137) | The provided azure logs ingestion data collection stream name is missing or invalid. | | [ERR\_NGROK\_5138](/docs/errors/err_ngrok_5138) | The provided azure logs ingestion URI is missing or invalid. | | [ERR\_NGROK\_5139](/docs/errors/err_ngrok_5139) | The provided azure configuration is invalid: \$`ERR` | | [ERR\_NGROK\_5140](/docs/errors/err_ngrok_5140) | One or more provided fields are invalid: \$`ERR` | | [ERR\_NGROK\_5141](/docs/errors/err_ngrok_5141) | Your account requires the Account Governance add-on to export audit events. Only http\_request\_completed and tcp\_connection\_closed events can be exported without this add-on. | | [ERR\_NGROK\_5200](/docs/errors/err_ngrok_5200) | URL "state" parameter is invalid. | | [ERR\_NGROK\_5201](/docs/errors/err_ngrok_5201) | URL "state" parameter is invalid. | | [ERR\_NGROK\_5202](/docs/errors/err_ngrok_5202) | Encountered an error while communicating with the OIDC provider: | | [ERR\_NGROK\_5203](/docs/errors/err_ngrok_5203) | The OIDC provider failed to return a OIDC ID Token. | | [ERR\_NGROK\_5204](/docs/errors/err_ngrok_5204) | Failed to verify the OIDC ID Token: | | [ERR\_NGROK\_5205](/docs/errors/err_ngrok_5205) | Failed to extract claims from the OIDC ID Token: | | [ERR\_NGROK\_5206](/docs/errors/err_ngrok_5206) | The endpoint configuration "`ECID`" does not exist. | | [ERR\_NGROK\_5207](/docs/errors/err_ngrok_5207) | OIDC is not enabled on the endpoint configuration "`ECID`". | | [ERR\_NGROK\_5208](/docs/errors/err_ngrok_5208) | Encountered an error while attempting to fetch OIDC provider metadata: | | [ERR\_NGROK\_5209](/docs/errors/err_ngrok_5209) | The authentication flow was not completed in time. Try authenticating again. | | [ERR\_NGROK\_5210](/docs/errors/err_ngrok_5210) | The authentication flow was not completed in time. Try authenticating again. | | [ERR\_NGROK\_5211](/docs/errors/err_ngrok_5211) | The OIDC provider returned the error code "`ERROR`" with the additional details: "`DETAILS`". Try authenticating again. | | [ERR\_NGROK\_5212](/docs/errors/err_ngrok_5212) | OIDC is not enabled on the domain "`HOSTNAME`". | | [ERR\_NGROK\_5300](/docs/errors/err_ngrok_5300) | SAML RelayState parameter is invalid. | | [ERR\_NGROK\_5301](/docs/errors/err_ngrok_5301) | The SAML IdP sent an invalid SAML assertion: | | [ERR\_NGROK\_5302](/docs/errors/err_ngrok_5302) | The SAML module for the endpoint configuration "`ECID`" disallows IdP-initiated logins. | | [ERR\_NGROK\_5303](/docs/errors/err_ngrok_5303) | The endpoint configuration "`ECID`" is not attached to any domains. | | [ERR\_NGROK\_5304](/docs/errors/err_ngrok_5304) | Could not find the domain "`DOMAIN`" specified in the IdP-initiated RelayState. | | [ERR\_NGROK\_5305](/docs/errors/err_ngrok_5305) | The SAML IdP sent an invalid SAML assertion: | | [ERR\_NGROK\_5306](/docs/errors/err_ngrok_5306) | SAML RelayState parameter is invalid. | | [ERR\_NGROK\_5307](/docs/errors/err_ngrok_5307) | The domain "`DOMAIN`" specified in the IdP-initiated RelayState is not attached to the endpoint configuration "`ECID`". | | [ERR\_NGROK\_5308](/docs/errors/err_ngrok_5308) | The SAML IdP sent an invalid SAML logout response: | | [ERR\_NGROK\_5309](/docs/errors/err_ngrok_5309) | URL "state" parameter is invalid. | | [ERR\_NGROK\_5310](/docs/errors/err_ngrok_5310) | URL "state" parameter is invalid. | | [ERR\_NGROK\_5311](/docs/errors/err_ngrok_5311) | The SAML IdP sent an invalid SAML assertion with `N` Authn statements. | | [ERR\_NGROK\_5312](/docs/errors/err_ngrok_5312) | You are not a member of the configured authorized groups on the ngrok edge. | | [ERR\_NGROK\_5313](/docs/errors/err_ngrok_5313) | The authentication flow was not completed in time. Try authenticating again. | | [ERR\_NGROK\_5314](/docs/errors/err_ngrok_5314) | The logout flow was not completed in time. Try authenticating again. | | [ERR\_NGROK\_5315](/docs/errors/err_ngrok_5315) | The authentication flow was not completed in time. Try authenticating again. | | [ERR\_NGROK\_5316](/docs/errors/err_ngrok_5316) | The logout flow was not completed in time. Try authenticating again. | | [ERR\_NGROK\_5317](/docs/errors/err_ngrok_5317) | The SAML module for the edge route "`RTEID`" disallows IdP-initiated logins. | | [ERR\_NGROK\_5318](/docs/errors/err_ngrok_5318) | The edge "`EDGID`" is not attached to any domains. | | [ERR\_NGROK\_5319](/docs/errors/err_ngrok_5319) | Failed to complete IdP-initiated SAML login. The RelayState specified an invalid domain "`DOMAIN`" to redirect to. Ensure that the domain "`DOMAIN`" is configured as a hostport for "`EDGID`". | | [ERR\_NGROK\_5320](/docs/errors/err_ngrok_5320) | The edge route "`RTEID`" does not exist. | | [ERR\_NGROK\_5321](/docs/errors/err_ngrok_5321) | The RelayState must be configured with the redirect URL for IdP-initiated logins. | | [ERR\_NGROK\_5322](/docs/errors/err_ngrok_5322) | The domain "`DOMAIN`" specified in the IdP-initiated RelayState is not attached to the edge "`EDGID`". | | [ERR\_NGROK\_5323](/docs/errors/err_ngrok_5323) | The SAML module is not enabled for the configuration "`ID`". | | [ERR\_NGROK\_5324](/docs/errors/err_ngrok_5324) | The ID "`ID`" that was specified is invalid. This usually indicates a malformed URL. | | [ERR\_NGROK\_5325](/docs/errors/err_ngrok_5325) | Failed to complete IdP-initiated SAML login. Edge does not have any attached domains. Ensure that at least one domain is configured as a hostport for "`EDGID`". | | [ERR\_NGROK\_5326](/docs/errors/err_ngrok_5326) | Failed to complete IdP-initiated SAML login. The RelayState must be configured with the redirect URL for IdP-initiated logins when using a route selector match type of "`MATCHTYPE`". | | [ERR\_NGROK\_5327](/docs/errors/err_ngrok_5327) | Failed to parse the SAML configuration for the configuration "`ID`". Please ensure that all URLs in the SAML configuration are well-formed and include a valid scheme, e.g. "https\://". | | [ERR\_NGROK\_5400](/docs/errors/err_ngrok_5400) | Event Sources for the "`TYP`" event type must specify fields to capture. | | [ERR\_NGROK\_5401](/docs/errors/err_ngrok_5401) | Event Sources for the "`TYP`" event may not specify fields. | | [ERR\_NGROK\_5402](/docs/errors/err_ngrok_5402) | Event Sources for the "`TYP`" event may not specify a filter. | | [ERR\_NGROK\_5403](/docs/errors/err_ngrok_5403) | Event Sources must be configured with fields that match its event type. "`FIELD`" is not a "`TYP`" field | | [ERR\_NGROK\_5404](/docs/errors/err_ngrok_5404) | Event Source filter is invalid: "`ERR`" | | [ERR\_NGROK\_5405](/docs/errors/err_ngrok_5405) | Invalid Event Field: "`FIELD`" | | [ERR\_NGROK\_5406](/docs/errors/err_ngrok_5406) | Field is not yet supported in event filters: "`FIELD`" | | [ERR\_NGROK\_5500](/docs/errors/err_ngrok_5500) | URL "state" parameter is invalid. | | [ERR\_NGROK\_5501](/docs/errors/err_ngrok_5501) | The authentication flow was not completed in time. Try authenticating again. | | [ERR\_NGROK\_5502](/docs/errors/err_ngrok_5502) | Authorization with `PROVIDER` succeeded, but these required scopes not were granted: `SCOPES`. | | [ERR\_NGROK\_5503](/docs/errors/err_ngrok_5503) | URL "state" parameter is invalid. | | [ERR\_NGROK\_5504](/docs/errors/err_ngrok_5504) | The authentication flow was not completed in time. Try authenticating again. | | [ERR\_NGROK\_5507](/docs/errors/err_ngrok_5507) | `ERROR_NAME` `DETAILS` | | [ERR\_NGROK\_5508](/docs/errors/err_ngrok_5508) | Authorization with `PROVIDER` succeeded, but these required scopes not were granted: `SCOPES`. | | [ERR\_NGROK\_5510](/docs/errors/err_ngrok_5510) | `PROVIDER` did not provide an email for your account. | | [ERR\_NGROK\_5511](/docs/errors/err_ngrok_5511) | Email "`EMAIL`" is not authorized for use by this account. | | [ERR\_NGROK\_5512](/docs/errors/err_ngrok_5512) | Email is malformed. Email "`EMAIL`" is missing an @ sign. | | [ERR\_NGROK\_5513](/docs/errors/err_ngrok_5513) | Email domain failed to normalize to a FQDN "`DOMAIN`": | | [ERR\_NGROK\_5517](/docs/errors/err_ngrok_5517) | `PROVIDER` denied access to your data during the final step of authorization: `DESCRIPTION`. | | [ERR\_NGROK\_5518](/docs/errors/err_ngrok_5518) | `PROVIDER` rejected authorization with access denied: `DESCRIPTION`. | | [ERR\_NGROK\_5520](/docs/errors/err_ngrok_5520) | `PROVIDER` rejected authorization due to a invalid or unknown scope requested by this application: `DESCRIPTION`. | | [ERR\_NGROK\_5521](/docs/errors/err_ngrok_5521) | `PROVIDER` rejected authorization due to an internal server error: `DESCRIPTION`. | | [ERR\_NGROK\_5522](/docs/errors/err_ngrok_5522) | `PROVIDER` is too busy to handle requests and authorization cannot be completed at this time: `DESCRIPTION`. | | [ERR\_NGROK\_5523](/docs/errors/err_ngrok_5523) | `PROVIDER` rejected authorization as the redirect URL for this application is improperly configured: `DESCRIPTION`. | | [ERR\_NGROK\_5524](/docs/errors/err_ngrok_5524) | `PROVIDER` rejected authorization: `DESCRIPTION`. | | [ERR\_NGROK\_5525](/docs/errors/err_ngrok_5525) | `PROVIDER` has throttled this application and cannot be reached right now. | | [ERR\_NGROK\_5526](/docs/errors/err_ngrok_5526) | `PROVIDER` rejected access to your information. This is likely caused by expiration or revocation of your authorization. | | [ERR\_NGROK\_5527](/docs/errors/err_ngrok_5527) | `PROVIDER` rejected the final step of authorization due to invalid credentials. | | [ERR\_NGROK\_5528](/docs/errors/err_ngrok_5528) | The access code from `PROVIDER` has expired and cannot be used. | | [ERR\_NGROK\_5529](/docs/errors/err_ngrok_5529) | `PROVIDER` denied access to your data during the final step of authorization. | | [ERR\_NGROK\_5530](/docs/errors/err_ngrok_5530) | OAuth is not enabled on the domain "`HOSTNAME`". | | [ERR\_NGROK\_5531](/docs/errors/err_ngrok_5531) | `PROVIDER` denied access to your account as the correct account could not be chosen automatically. | | [ERR\_NGROK\_5532](/docs/errors/err_ngrok_5532) | `PROVIDER` denied access to your account as reauthentication is required. | | [ERR\_NGROK\_5533](/docs/errors/err_ngrok_5533) | `PROVIDER` had an internal server error when refreshing your information. | | [ERR\_NGROK\_5534](/docs/errors/err_ngrok_5534) | Encountered an error while attempting to fetch OIDC provider metadata: | | [ERR\_NGROK\_5535](/docs/errors/err_ngrok_5535) | The OAuth provider "`PROVIDER`" cannot be used without a user-defined client ID and client secret. | | [ERR\_NGROK\_5536](/docs/errors/err_ngrok_5536) | Encountered an error while fetching user data: `PROVIDER` did not provide a user ID for your account. | | [ERR\_NGROK\_5537](/docs/errors/err_ngrok_5537) | Encountered an error while resolving CEL expression: | | [ERR\_NGROK\_5538](/docs/errors/err_ngrok_5538) | Encountered an error while encrypting the field `FIELD`. | | [ERR\_NGROK\_5539](/docs/errors/err_ngrok_5539) | Encountered an error while decrypting the field `FIELD`. | | [ERR\_NGROK\_6000](/docs/errors/err_ngrok_6000) | This tunnel was unable to update; this may be temporary, please try again shortly. | | [ERR\_NGROK\_6001](/docs/errors/err_ngrok_6001) | This tunnel is no longer available as its account was not found. | | [ERR\_NGROK\_6002](/docs/errors/err_ngrok_6002) | This tunnel is no longer available as its account does not own resources required to run. | | [ERR\_NGROK\_6003](/docs/errors/err_ngrok_6003) | Endpoint configurations cannot be used with agent-specified basic auth. Either remove the endpoint configuration from this endpoint or restart the agent without basic auth. | | [ERR\_NGROK\_6006](/docs/errors/err_ngrok_6006) | The Event Destination `EDID` required by `ECID` was not found. If these were recently created or removed, this should be temporary and resolve itself. | | [ERR\_NGROK\_6008](/docs/errors/err_ngrok_6008) | This endpoint is unavailable as its account is suspended due to lack of payment. | | [ERR\_NGROK\_6009](/docs/errors/err_ngrok_6009) | The ngrok account that was hosting this webpage has been suspended for violation of ngrok's Terms of Service. | | [ERR\_NGROK\_6010](/docs/errors/err_ngrok_6010) | Endpoint configuration is incompatible with endpoint; module `MODULE` required by Endpoint Configuration `ECID` is incompatible with `PROTO` endpoints. | | [ERR\_NGROK\_6011](/docs/errors/err_ngrok_6011) | You may not specify `MIDDLEWARE` options from the ngrok agent because this domain has a configured endpoint configuration: `ECID`. Remove the endpoint configuration from this domain or remove the conflicting options when starting the ngrok agent. | | [ERR\_NGROK\_6013](/docs/errors/err_ngrok_6013) | Failed to compile filter for Event Subscription `ESID`. | | [ERR\_NGROK\_6014](/docs/errors/err_ngrok_6014) | Failed to run filter for Event Subscription `ESID`. | | [ERR\_NGROK\_6018](/docs/errors/err_ngrok_6018) | The Endpoint `HOSTPORT` has both an Endpoint Configuration (`EPCID`) and a Tunnel with incompatible configuration (`TUNNELID`) online. | | [ERR\_NGROK\_6019](/docs/errors/err_ngrok_6019) | The Endpoint `HOSTPORT` has both an Edge (`EDGID`) and Endpoint Configuration (`EPCID`) configured. | | [ERR\_NGROK\_6020](/docs/errors/err_ngrok_6020) | The Endpoint `HOSTPORT` has both an Edge (`EDGID`) and Tunnel (`TUNID`) online. | | [ERR\_NGROK\_6021](/docs/errors/err_ngrok_6021) | HTML content may only be served after you upgrade to a paid account. | | [ERR\_NGROK\_6022](/docs/errors/err_ngrok_6022) | Before you can serve HTML content, you must sign up for an ngrok account and install your authtoken. | | [ERR\_NGROK\_6023](/docs/errors/err_ngrok_6023) | HTML content may only be served in this region after you upgrade to a paid account. | | [ERR\_NGROK\_6024](/docs/errors/err_ngrok_6024) | You are about to visit `HOSTPORT``SERVEDBYSTR`. This website is served for free through ngrok.com. You should only visit this website if you trust whoever sent the link to you. | | [ERR\_NGROK\_6025](/docs/errors/err_ngrok_6025) | The tunnel `URL` is already bound to another tunnel session. Start the ngrok agent with a different domain. | | [ERR\_NGROK\_6026](/docs/errors/err_ngrok_6026) | The Endpoint `HOSTPORT` has both a Cloud Endpoint (`CLEPID`) and Tunnel (`TUNID`) online. | | [ERR\_NGROK\_6027](/docs/errors/err_ngrok_6027) | The Endpoint `HOSTPORT` has both a Cloud Endpoint (`CLEPID`) and Edge (`EDGID`) configured. | | [ERR\_NGROK\_6028](/docs/errors/err_ngrok_6028) | The Endpoint `HOSTPORT` has both a Cloud Endpoint (`CLEPID`) and Endpoint Configuration (`TUNID`) configured. | | [ERR\_NGROK\_6029](/docs/errors/err_ngrok_6029) | The traffic policy configured for cloud endpoint `CLEPID` is non-terminating. | | [ERR\_NGROK\_6030](/docs/errors/err_ngrok_6030) | The url `URL` has multiple endpoints, but they do not all have pooling enabled. | | [ERR\_NGROK\_6031](/docs/errors/err_ngrok_6031) | HTTP endpoints are disabled for HIPAA-enabled plans | | [ERR\_NGROK\_6032](/docs/errors/err_ngrok_6032) | This ngrok account is not allowed to use the paid traffic policy features configured on this endpoint. Upgrade your plan to enable this endpoint. | | [ERR\_NGROK\_6033](/docs/errors/err_ngrok_6033) | Insuffiucient AI Gateway Credits - Your credit balance is too low to use AI Gateway. You can purchase credits from the dashboard: [https://dashboard.ngrok.com/ai-gateways](https://dashboard.ngrok.com/ai-gateways) . If you've already upgraded, the change may take a few minutes to propagate, please try again in a little bit. If you are still experiencing this issue after 20-30 minutes, please contact support. | | [ERR\_NGROK\_6500](/docs/errors/err_ngrok_6500) | Your account is not authorized to use the backends feature. | | [ERR\_NGROK\_6501](/docs/errors/err_ngrok_6501) | Cannot save backend reference `ID` because it does not exists. | | [ERR\_NGROK\_6502](/docs/errors/err_ngrok_6502) | The backend could not be deleted because it is still referenced by at least one backend. | | [ERR\_NGROK\_6503](/docs/errors/err_ngrok_6503) | Description is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_6504](/docs/errors/err_ngrok_6504) | Metadata is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_6505](/docs/errors/err_ngrok_6505) | Static backend requires address in the host:port form. | | [ERR\_NGROK\_6506](/docs/errors/err_ngrok_6506) | Static backend address `ADDR` is invalid (`REASON`). Use host:port as address. | | [ERR\_NGROK\_6507](/docs/errors/err_ngrok_6507) | Static backend address `ADDR` is missing host. Use host:port as address. | | [ERR\_NGROK\_6508](/docs/errors/err_ngrok_6508) | Tunnel group labels count is limited to `MAX`; you have `VAL` labels. | | [ERR\_NGROK\_6509](/docs/errors/err_ngrok_6509) | Tunnel group label key length is limited to between `MIN` and `MAX`; key `NAME` has `VAL` characters. | | [ERR\_NGROK\_6510](/docs/errors/err_ngrok_6510) | Tunnel group label value length is limited to between `MIN` and `MAX`; value `NAME` has `VAL` characters. | | [ERR\_NGROK\_6511](/docs/errors/err_ngrok_6511) | Weighted backends count is limited to `MAX`; you have `VAL` backends. | | [ERR\_NGROK\_6512](/docs/errors/err_ngrok_6512) | Weighted backend `ID` weight `VAL` is out of range; it must be between `MIN` and `MAX`. | | [ERR\_NGROK\_6513](/docs/errors/err_ngrok_6513) | Failover backends may only contain up to `MAX` failover backends; you have `VAL` failover backends. | | [ERR\_NGROK\_6514](/docs/errors/err_ngrok_6514) | Backend not found. | | [ERR\_NGROK\_6515](/docs/errors/err_ngrok_6515) | Tunnel Group Backend `ID` did not match any endpoints. | | [ERR\_NGROK\_6516](/docs/errors/err_ngrok_6516) | HTTP Response Backend `ID` had too large of a body. | | [ERR\_NGROK\_6517](/docs/errors/err_ngrok_6517) | HTTP Response Backend `ID` had an invalid status code. | | [ERR\_NGROK\_6522](/docs/errors/err_ngrok_6522) | The header beginning with `PREFIX`...' exceeds the maximum header name length of `BYTES` bytes. | | [ERR\_NGROK\_6523](/docs/errors/err_ngrok_6523) | The header value for `HEADERKEY` beginning with `PREFIX`...' exceeds the maximum header value length of `BYTES` bytes. | | [ERR\_NGROK\_6524](/docs/errors/err_ngrok_6524) | A Weighted Backend may not reference the backend `ID` because it is a `BACKENDTYPE` backend. | | [ERR\_NGROK\_6525](/docs/errors/err_ngrok_6525) | A Failover Backend may not reference the backend `ID` because it is a `BACKENDTYPE` backend. | | [ERR\_NGROK\_6526](/docs/errors/err_ngrok_6526) | The id `ID` is not a valid backend ID. | | [ERR\_NGROK\_6527](/docs/errors/err_ngrok_6527) | The id `ID` is invalid; expected an id of type `BACKENDTYPE` backend. | | [ERR\_NGROK\_6528](/docs/errors/err_ngrok_6528) | Traffic made it to the ngrok edge, but there are no endpoints online serving an app. | | [ERR\_NGROK\_6529](/docs/errors/err_ngrok_6529) | Your account is not authorized to use the weighted backends feature. | | [ERR\_NGROK\_6530](/docs/errors/err_ngrok_6530) | The `ID` header cannot be removed without specifying a replacement. | | [ERR\_NGROK\_6531](/docs/errors/err_ngrok_6531) | The tunnel group label key/value `ID` may not contain '=' characters. | | [ERR\_NGROK\_6600](/docs/errors/err_ngrok_6600) | The account `NAME` has reached its team size limit and may not add additional users. | | [ERR\_NGROK\_6601](/docs/errors/err_ngrok_6601) | Failed to add `USER_NAME` as a member of account `ACCOUNT_NAME` because they are already a member. | | [ERR\_NGROK\_6602](/docs/errors/err_ngrok_6602) | You may not remove the last administrator of the account. Grant administrator privileges to another member first. | | [ERR\_NGROK\_6603](/docs/errors/err_ngrok_6603) | Your account is not allowed to set membership permissions. | | [ERR\_NGROK\_6604](/docs/errors/err_ngrok_6604) | You may not remove an administrator from the account. Only administrators may remove other administrators. | | [ERR\_NGROK\_6605](/docs/errors/err_ngrok_6605) | You may not grant administrator privileges to an account member. Only administrators may grant administrative privileges. | | [ERR\_NGROK\_6606](/docs/errors/err_ngrok_6606) | You may not change an administrator's permissions. Only other administrators may make that change. | | [ERR\_NGROK\_6607](/docs/errors/err_ngrok_6607) | You may not change the account administrator. Only the current account administrator may make this change. | | [ERR\_NGROK\_6608](/docs/errors/err_ngrok_6608) | Could not transfer administrative control to membership `ID` because it could not be found on this account. | | [ERR\_NGROK\_6609](/docs/errors/err_ngrok_6609) | Swapping administrators is not supported on accounts with RBAC enabled. | | [ERR\_NGROK\_6612](/docs/errors/err_ngrok_6612) | You are not allowed to remove members. | | [ERR\_NGROK\_6613](/docs/errors/err_ngrok_6613) | You are not allowed to create members. | | [ERR\_NGROK\_6614](/docs/errors/err_ngrok_6614) | You are not allowed to set the permissions of members. | | [ERR\_NGROK\_6615](/docs/errors/err_ngrok_6615) | You may not create users with higher permissions than your own. | | [ERR\_NGROK\_6616](/docs/errors/err_ngrok_6616) | You may not set user permissions higher than your own. | | [ERR\_NGROK\_6617](/docs/errors/err_ngrok_6617) | Users must have a defined billing permission. | | [ERR\_NGROK\_6618](/docs/errors/err_ngrok_6618) | You are not allowed to create members, this account uses SCIM 2.0 to manage members. | | [ERR\_NGROK\_6619](/docs/errors/err_ngrok_6619) | You are not allowed to remove members, this account uses SCIM 2.0 to manage members. | | [ERR\_NGROK\_6620](/docs/errors/err_ngrok_6620) | You are not allowed to activate/deactivate members, this account uses SCIM 2.0 to manage members. | | [ERR\_NGROK\_6621](/docs/errors/err_ngrok_6621) | You are not allowed to activate/deactivate admins. Only other administrators may make that change. | | [ERR\_NGROK\_6622](/docs/errors/err_ngrok_6622) | You may not deactivate the last administrator of the account. Grant administrator privileges to another member first. | | [ERR\_NGROK\_6623](/docs/errors/err_ngrok_6623) | Could not transfer administrative control to membership `ID` because it is deactivated. Activate the member first. | | [ERR\_NGROK\_6700](/docs/errors/err_ngrok_6700) | The description is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_6701](/docs/errors/err_ngrok_6701) | Metadata is limited to `MAX` characters, but the provided value had `VAL` characters. | | [ERR\_NGROK\_6702](/docs/errors/err_ngrok_6702) | No DNS records were returned when querying for an NS record for the domain `DOMAIN`. | | [ERR\_NGROK\_6703](/docs/errors/err_ngrok_6703) | The NS records for the domain `DOMAIN` "`GOT`" do not match the expected targets "`EXPECTED`". | | [ERR\_NGROK\_6704](/docs/errors/err_ngrok_6704) | The specified domain "`DOMAIN`" is not a valid domain name. Enter a valid DNS name. | | [ERR\_NGROK\_6705](/docs/errors/err_ngrok_6705) | The agent ingress domain "`DOMAIN`" must be a third party domain, you may not enter a domain controlled by ngrok. | | [ERR\_NGROK\_6706](/docs/errors/err_ngrok_6706) | The agent ingress domain "`DOMAIN`" already exists, choose another domain. | | [ERR\_NGROK\_6707](/docs/errors/err_ngrok_6707) | Your account can't reserve custom agent ingresses. | | [ERR\_NGROK\_6708](/docs/errors/err_ngrok_6708) | Multiple certificate sources provided. | | [ERR\_NGROK\_6709](/docs/errors/err_ngrok_6709) | Some of the dedicated IPs "`IPS`" are already used in a different agent ingress. | | [ERR\_NGROK\_6710](/docs/errors/err_ngrok_6710) | Dedicated IP "`IP`" is not a valid IP. | | [ERR\_NGROK\_6711](/docs/errors/err_ngrok_6711) | Dedicated IP "`IP`" is duplicated in the set of IPs. | | [ERR\_NGROK\_7000](/docs/errors/err_ngrok_7000) | Edge not found | | [ERR\_NGROK\_7001](/docs/errors/err_ngrok_7001) | Invalid edge ID | | [ERR\_NGROK\_7002](/docs/errors/err_ngrok_7002) | Invalid edge request | | [ERR\_NGROK\_7007](/docs/errors/err_ngrok_7007) | The IP Policy ID `ID` specified for the IP Restriction module does not exist. | | [ERR\_NGROK\_7008](/docs/errors/err_ngrok_7008) | Edges are deprecated and no longer available. | | [ERR\_NGROK\_7009](/docs/errors/err_ngrok_7009) | Your account is limited to a maximum of `MAX` edges. | | [ERR\_NGROK\_7011](/docs/errors/err_ngrok_7011) | Invalid edge, circuit breaker error threshold percentage must be between 0.0 and 1.0 inclusive, was `VAL`. | | [ERR\_NGROK\_7012](/docs/errors/err_ngrok_7012) | The auth provider `VAL` does not exist. Specify an auth provider ID or the special value 'agent'. | | [ERR\_NGROK\_7013](/docs/errors/err_ngrok_7013) | The edge module exceeds the max number of IP Policies. You specified `VAL`, the maximum is `MAX`. | | [ERR\_NGROK\_7014](/docs/errors/err_ngrok_7014) | The edge module TLS CA certificate size exceeds the max. You specified `VAL`, the maximum is `MAX`. | | [ERR\_NGROK\_7015](/docs/errors/err_ngrok_7015) | The edge module exceeds the max number of headers. You specified `VAL`, the maximum is `MAX`. | | [ERR\_NGROK\_7016](/docs/errors/err_ngrok_7016) | Invalid edge module, circuit breaker tripped duration must be greater than 0, was `VAL`. | | [ERR\_NGROK\_7017](/docs/errors/err_ngrok_7017) | Invalid edge module, circuit breaker rolling window must be greater than 0, was `VAL`. | | [ERR\_NGROK\_7018](/docs/errors/err_ngrok_7018) | Invalid edge module, circuit breaker num buckets must be greater than 0 and less than `MAX`, was `VAL`. | | [ERR\_NGROK\_7019](/docs/errors/err_ngrok_7019) | The edge could not be deleted because it is still referenced by at least one reserved domain or reserved address. | | [ERR\_NGROK\_7020](/docs/errors/err_ngrok_7020) | The edge must specify a type. | | [ERR\_NGROK\_7021](/docs/errors/err_ngrok_7021) | The module `NAME` is not supported on an edge of type `TYPE`. | | [ERR\_NGROK\_7022](/docs/errors/err_ngrok_7022) | `NAME` is not a valid edge type. Must be one of 'http', 'https', 'tcp'. | | [ERR\_NGROK\_7023](/docs/errors/err_ngrok_7023) | `KEY` is not a valid HTTP header name because it contains at least one invalid character. | | [ERR\_NGROK\_7024](/docs/errors/err_ngrok_7024) | `VAL` is not a valid HTTP header value: `REASON`. | | [ERR\_NGROK\_7025](/docs/errors/err_ngrok_7025) | You must specify at least one IP Policy in the IP Restriction module. | | [ERR\_NGROK\_7026](/docs/errors/err_ngrok_7026) | SNS webhook verification does not accept a secret. | | [ERR\_NGROK\_7027](/docs/errors/err_ngrok_7027) | Webhook verification for `T` requires a secret. | | [ERR\_NGROK\_7028](/docs/errors/err_ngrok_7028) | There were validation errors while saving the edge:`ERRS` | | [ERR\_NGROK\_7029](/docs/errors/err_ngrok_7029) | You must specify a supported provider name. Supported providers: \[`T`] | | [ERR\_NGROK\_7030](/docs/errors/err_ngrok_7030) | Webhook provider `T` is not supported. Supported providers: \[`TYPE`] | | [ERR\_NGROK\_7031](/docs/errors/err_ngrok_7031) | Your account is not authorized to use webhook verification. | | [ERR\_NGROK\_7032](/docs/errors/err_ngrok_7032) | You may not configure the TLS termination parameter `PARAMETER` when the HTTPS module disables TLS termination. | | [ERR\_NGROK\_7033](/docs/errors/err_ngrok_7033) | Invalid edge module, unmanaged provider configurations must specify a client ID and a client secret. | | [ERR\_NGROK\_7034](/docs/errors/err_ngrok_7034) | Invalid edge module, custom OAuth scopes on a managed OAuth application are not allowed. Define a custom OAuth application to use custom scopes. | | [ERR\_NGROK\_7035](/docs/errors/err_ngrok_7035) | Invalid edge module, OAuth auth check interval must be at least `MIN`, was `ACTUAL`. | | [ERR\_NGROK\_7036](/docs/errors/err_ngrok_7036) | Invalid edge module, OAuth may only have one provider configuration but multiple are defined: \[`PROVIDER_NAMES`]. | | [ERR\_NGROK\_7037](/docs/errors/err_ngrok_7037) | Invalid edge module, OAuth does not define any provider configurations but one is required. | | [ERR\_NGROK\_7038](/docs/errors/err_ngrok_7038) | Invalid edge module, OAuth provider `PROVIDER` requires at least the `SCOPE` scope to authenticate by `AUTH_FEATURE`.' | | [ERR\_NGROK\_7039](/docs/errors/err_ngrok_7039) | Invalid edge module, OAuth provider `PROVIDER` contains an invalid email domain: `NAME`. | | [ERR\_NGROK\_7040](/docs/errors/err_ngrok_7040) | Invalid edge module, OAuth GitHub Teams must be listed as either numerical ids or in the format 'org\_slug/team\_slug', `TEAM` does not match either format. | | [ERR\_NGROK\_7041](/docs/errors/err_ngrok_7041) | Your account is not authorized to use OAuth. | | [ERR\_NGROK\_7042](/docs/errors/err_ngrok_7042) | Invalid edge module, OAuth cookie prefix `PREFIX` must consist of only alphanumeric characters with periods. | | [ERR\_NGROK\_7043](/docs/errors/err_ngrok_7043) | Invalid edge module, OAuth provider `PROVIDER` contains an invalid email address, `NAME`. | | [ERR\_NGROK\_7044](/docs/errors/err_ngrok_7044) | Invalid edge module, OAuth provider `PROVIDER` contains an email domain with invalid punycode: `NAME`. | | [ERR\_NGROK\_7045](/docs/errors/err_ngrok_7045) | Invalid edge module, OAuth provider `PROVIDER` contains an email address with invalid punycode: `NAME`. | | [ERR\_NGROK\_7046](/docs/errors/err_ngrok_7046) | Invalid edge module, OAuth provider `PROVIDER` email addresses must be unique, but a duplicate is present: `DUPLICATE`. | | [ERR\_NGROK\_7047](/docs/errors/err_ngrok_7047) | Invalid edge module, OAuth provider `PROVIDER` email domains must be unique, but a duplicate is present: `DUPLICATE`. | | [ERR\_NGROK\_7048](/docs/errors/err_ngrok_7048) | Invalid edge module, OAuth provider `PROVIDER` teams must be unique, but a duplicate is present: `DUPLICATE`. | | [ERR\_NGROK\_7049](/docs/errors/err_ngrok_7049) | Invalid edge module, OAuth provider `PROVIDER` organizations must be unique, but a duplicate is present: `DUPLICATE`. | | [ERR\_NGROK\_7050](/docs/errors/err_ngrok_7050) | Invalid edge module, OAuth provider `PROVIDER` groups must be unique, but a duplicate is present: `DUPLICATE`. | | [ERR\_NGROK\_7051](/docs/errors/err_ngrok_7051) | Invalid edge module, OAuth provider `PROVIDER` email addresses must not contain empty values | | [ERR\_NGROK\_7052](/docs/errors/err_ngrok_7052) | Invalid edge module, OAuth provider `PROVIDER` email domains must not contain empty values | | [ERR\_NGROK\_7053](/docs/errors/err_ngrok_7053) | Invalid edge module, OAuth provider `PROVIDER` teams must not contain empty values | | [ERR\_NGROK\_7054](/docs/errors/err_ngrok_7054) | Invalid edge module, OAuth provider `PROVIDER` organizations must not contain empty values | | [ERR\_NGROK\_7055](/docs/errors/err_ngrok_7055) | Invalid edge module, OAuth provider `PROVIDER` groups must not contain empty values | | [ERR\_NGROK\_7056](/docs/errors/err_ngrok_7056) | The edge module exceeds the maximum number of OAuth scopes. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_7057](/docs/errors/err_ngrok_7057) | The edge module exceeds the maximum number of OAuth GitHub teams. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_7058](/docs/errors/err_ngrok_7058) | The edge module exceeds the maximum number of OAuth GitHub orgs. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_7059](/docs/errors/err_ngrok_7059) | The edge module exceeds the maximum number of OAuth Google groups. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_7060](/docs/errors/err_ngrok_7060) | The edge module exceeds the maximum number of OAuth emails. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_7061](/docs/errors/err_ngrok_7061) | The edge module exceeds the maximum number of OAuth domains. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_7062](/docs/errors/err_ngrok_7062) | The edge specifies the following modules which may not be enabled when TLS is not terminated: `MODULE_NAMES`. | | [ERR\_NGROK\_7067](/docs/errors/err_ngrok_7067) | Invalid edge module, email addresses must be lowercase but `EMAIL` is not. | | [ERR\_NGROK\_7068](/docs/errors/err_ngrok_7068) | Invalid edge module, email domains must be lowercase but `DOMAIN` is not. | | [ERR\_NGROK\_7069](/docs/errors/err_ngrok_7069) | Duplicate 'add' header, `HEADER` was provided twice with different casings: `CASE_ONE` and `CASE_TWO`. | | [ERR\_NGROK\_7070](/docs/errors/err_ngrok_7070) | Your account is limited to `MAX` edges. | | [ERR\_NGROK\_7071](/docs/errors/err_ngrok_7071) | Your account is rate limited for adding `MAX` edges per `INTERVAL`. | | [ERR\_NGROK\_7072](/docs/errors/err_ngrok_7072) | The CA ID `ID` specified for the Mutual TLS module does not exist. | | [ERR\_NGROK\_7073](/docs/errors/err_ngrok_7073) | Description is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_7074](/docs/errors/err_ngrok_7074) | Metadata is limited to `MAX` characters; you have entered `VAL` characters. | | [ERR\_NGROK\_7075](/docs/errors/err_ngrok_7075) | The Mutual TLS configurations exceeds the limit of `LIMIT` attached certificate authorities. | | [ERR\_NGROK\_7079](/docs/errors/err_ngrok_7079) | The edge specifies conflicting authentication modules. Only one of SAML, OIDC, or OAuth may be enabled. | | [ERR\_NGROK\_7080](/docs/errors/err_ngrok_7080) | The SAML metadata exceeds the maximum length of `MAX` bytes, got `NBYTES`. | | [ERR\_NGROK\_7081](/docs/errors/err_ngrok_7081) | You must specify only the SAML IdP metadata or the SAML IdP metadata URL, but not both. | | [ERR\_NGROK\_7082](/docs/errors/err_ngrok_7082) | Failed to connect to IdP metadata URL `MDURL`: `ERR`. | | [ERR\_NGROK\_7083](/docs/errors/err_ngrok_7083) | Failed to parse the SAML IdP metadata: `ERR`. | | [ERR\_NGROK\_7084](/docs/errors/err_ngrok_7084) | Received unexpected status code `STATUSCODE` while fetching metadata URL `MDURL` | | [ERR\_NGROK\_7085](/docs/errors/err_ngrok_7085) | Failed to parse the SAML IdP metadata fetched from URL `MDURL`: `ERR`. | | [ERR\_NGROK\_7086](/docs/errors/err_ngrok_7086) | Encountered an error while reading the response body from the IdP metadata URL `MDURL`: `ERR`. | | [ERR\_NGROK\_7087](/docs/errors/err_ngrok_7087) | The specified OIDC issuer has a maximum length of `MAX` bytes. The specified value is `N` bytes. | | [ERR\_NGROK\_7088](/docs/errors/err_ngrok_7088) | The OIDC issuer property is required. | | [ERR\_NGROK\_7089](/docs/errors/err_ngrok_7089) | The OIDC Client ID property is required. | | [ERR\_NGROK\_7090](/docs/errors/err_ngrok_7090) | The OIDC Client Secret property is required. | | [ERR\_NGROK\_7091](/docs/errors/err_ngrok_7091) | One of the SAML IdP Metadata or IdP Metadata URL properties must be specified. | | [ERR\_NGROK\_7092](/docs/errors/err_ngrok_7092) | Invalid edge module, session inactivity timeout cannot be more than one year, was `ACTUAL`. | | [ERR\_NGROK\_7093](/docs/errors/err_ngrok_7093) | Your account is not authorized to use basic auth. | | [ERR\_NGROK\_7094](/docs/errors/err_ngrok_7094) | Your account is not authorized to use compression. | | [ERR\_NGROK\_7095](/docs/errors/err_ngrok_7095) | Your account is not authorized to use circuit breaker. | | [ERR\_NGROK\_7096](/docs/errors/err_ngrok_7096) | Your account is not authorized to use request headers. | | [ERR\_NGROK\_7097](/docs/errors/err_ngrok_7097) | Your account is not authorized to use response headers. | | [ERR\_NGROK\_7098](/docs/errors/err_ngrok_7098) | Your account is not authorized to use OIDC. | | [ERR\_NGROK\_7099](/docs/errors/err_ngrok_7099) | Your account is not authorized to use SAML. | | [ERR\_NGROK\_7100](/docs/errors/err_ngrok_7100) | Your account is not authorized to use TLS termination. | | [ERR\_NGROK\_7101](/docs/errors/err_ngrok_7101) | Your account is not authorized to use IP Restrictions. | | [ERR\_NGROK\_7102](/docs/errors/err_ngrok_7102) | Your account is not authorized to use TCP address backends. | | [ERR\_NGROK\_7103](/docs/errors/err_ngrok_7103) | Your account is not authorized to use mutual TLS. | | [ERR\_NGROK\_7104](/docs/errors/err_ngrok_7104) | Edge has invalid hostports, must be specified in the format 'hostname:portnumber': `INVALID` | | [ERR\_NGROK\_7105](/docs/errors/err_ngrok_7105) | The header beginning with `PREFIX`...' exceeds the maximum header name length of `BYTES` bytes. | | [ERR\_NGROK\_7106](/docs/errors/err_ngrok_7106) | The header value for `HEADERKEY` beginning with `PREFIX`...' exceeds the maximum header value length of `BYTES` bytes. | | [ERR\_NGROK\_7107](/docs/errors/err_ngrok_7107) | The sendgrid verification key is not a base64 encoded ecdsa public key. The following error was encountered while trying to parse it: "`ERROR`" | | [ERR\_NGROK\_7108](/docs/errors/err_ngrok_7108) | Your account is not authorized to use the websocket to TCP converter. | | [ERR\_NGROK\_7109](/docs/errors/err_ngrok_7109) | You may not configure the TLS termination parameter 'terminate\_at' for an HTTPS edge. | | [ERR\_NGROK\_7110](/docs/errors/err_ngrok_7110) | The request edge ID `EID` does not match the route edge ID `REID`. | | [ERR\_NGROK\_7111](/docs/errors/err_ngrok_7111) | The edge hostport `HOST`:`PORT` does not exist. | | [ERR\_NGROK\_7112](/docs/errors/err_ngrok_7112) | The hostport `HOSTPORT` has port out of range: `PORT` is not in range 1-65535. | | [ERR\_NGROK\_7113](/docs/errors/err_ngrok_7113) | The address `HOSTPORT` is not reserved. | | [ERR\_NGROK\_7114](/docs/errors/err_ngrok_7114) | The address `HOSTPORT` is not reserved by your account. | | [ERR\_NGROK\_7115](/docs/errors/err_ngrok_7115) | The address `HOSTPORT` has an endpoint configuration which must be removed before it can be used by an edge. | | [ERR\_NGROK\_7116](/docs/errors/err_ngrok_7116) | The domain `HOSTPORT` is unsupported because it does not use port 443. | | [ERR\_NGROK\_7117](/docs/errors/err_ngrok_7117) | The domain `HOSTPORT` is not reserved. | | [ERR\_NGROK\_7118](/docs/errors/err_ngrok_7118) | The domain `HOSTPORT` is not reserved by your account. | | [ERR\_NGROK\_7119](/docs/errors/err_ngrok_7119) | The domain `HOSTPORT` has an endpoint configuration which must be removed before it can be used by an edge. | | [ERR\_NGROK\_7120](/docs/errors/err_ngrok_7120) | TCP edges can only be associated with TCP addresses: `HOSTPORT` is a domain. | | [ERR\_NGROK\_7121](/docs/errors/err_ngrok_7121) | HTTPS edges can only be associated with reserved domains: `HOSTPORT` is a TCP address. | | [ERR\_NGROK\_7122](/docs/errors/err_ngrok_7122) | Hostport `HOSTPORT` already exists for another edge. | | [ERR\_NGROK\_7123](/docs/errors/err_ngrok_7123) | Edge routes require a match expression. | | [ERR\_NGROK\_7124](/docs/errors/err_ngrok_7124) | The edge route `ID` could not be found. | | [ERR\_NGROK\_7125](/docs/errors/err_ngrok_7125) | There is a duplicate route `ID` for HTTPS edge. | | [ERR\_NGROK\_7126](/docs/errors/err_ngrok_7126) | Route match expression `MATCH` must be unique across all routes for HTTPS edge. | | [ERR\_NGROK\_7127](/docs/errors/err_ngrok_7127) | OAuth may not currently be configured for any route except '/', could not configure for route `MATCH`. | | [ERR\_NGROK\_7128](/docs/errors/err_ngrok_7128) | Your account is limited to a maximum of `MAX` routes for a single edge. | | [ERR\_NGROK\_7129](/docs/errors/err_ngrok_7129) | Your account is limited to a maximum of `MAX` hostports for a single edge. | | [ERR\_NGROK\_7130](/docs/errors/err_ngrok_7130) | Invalid match type, valid values are "exact\_path" and "path\_prefix". | | [ERR\_NGROK\_7131](/docs/errors/err_ngrok_7131) | The route selector expression `MATCH` is invalid. Valid route selector match expressions are of the form /path (e.g. "/foo", or "/foo/bar"). | | [ERR\_NGROK\_7132](/docs/errors/err_ngrok_7132) | The hostport `HOSTPORT` is already in use by an agent directly. | | [ERR\_NGROK\_7133](/docs/errors/err_ngrok_7133) | TLS edges can only be associated with reserved domains: `HOSTPORT` is a TCP address. | | [ERR\_NGROK\_7134](/docs/errors/err_ngrok_7134) | OAuth, OIDC and SAML may only be configured for routes with path selectors (i.e. 'exact\_path', 'path\_prefix'). | | [ERR\_NGROK\_7135](/docs/errors/err_ngrok_7135) | The route selector expression exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7136](/docs/errors/err_ngrok_7136) | The OAuth Cookie Prefix exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7137](/docs/errors/err_ngrok_7137) | The OAuth Client ID exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7138](/docs/errors/err_ngrok_7138) | The OAuth Client Secret exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7139](/docs/errors/err_ngrok_7139) | The OAuth Email Address exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7140](/docs/errors/err_ngrok_7140) | The OAuth Email Domain exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7141](/docs/errors/err_ngrok_7141) | The OAuth Scope exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7142](/docs/errors/err_ngrok_7142) | The edge module exceeds the maximum number of GitHub teams. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_7143](/docs/errors/err_ngrok_7143) | The edge module exceeds the maximum number of GitHub orgs. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_7144](/docs/errors/err_ngrok_7144) | The OAuth GitHub team exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7145](/docs/errors/err_ngrok_7145) | The OAuth GitHub org exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7146](/docs/errors/err_ngrok_7146) | The edge module exceeds the maximum number of Google groups. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_7147](/docs/errors/err_ngrok_7147) | The OAuth Google group exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7148](/docs/errors/err_ngrok_7148) | The OIDC Client ID exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7149](/docs/errors/err_ngrok_7149) | The OIDC Client Secret exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7150](/docs/errors/err_ngrok_7150) | The edge module exceeds the maximum number of OIDC scopes. You specified `COUNT`, the max is `MAX`. | | [ERR\_NGROK\_7151](/docs/errors/err_ngrok_7151) | The OIDC Scope exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7152](/docs/errors/err_ngrok_7152) | The OIDC Authn URL exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7153](/docs/errors/err_ngrok_7153) | The OIDC cookie prefix exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7154](/docs/errors/err_ngrok_7154) | The Webhook Verification secret exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_7155](/docs/errors/err_ngrok_7155) | Enabling OAuth, OIDC, or SAML on more than one route is not supported. | | [ERR\_NGROK\_7158](/docs/errors/err_ngrok_7158) | Invalid edge module, OAuth auth check interval must be no greater than `MAX`, was `ACTUAL`. | | [ERR\_NGROK\_7159](/docs/errors/err_ngrok_7159) | Invalid edge module, OIDC auth check interval must be at least `MIN`, was `ACTUAL`. | | [ERR\_NGROK\_7160](/docs/errors/err_ngrok_7160) | Invalid edge module, OIDC auth check interval must be no greater than `MAX`, was `ACTUAL`. | | [ERR\_NGROK\_7161](/docs/errors/err_ngrok_7161) | Invalid edge module, OAuth max session duration must be no greater than `MAX`, was `ACTUAL`. | | [ERR\_NGROK\_7162](/docs/errors/err_ngrok_7162) | Invalid edge module, OIDC max session duration must be no greater than `MAX`, was `ACTUAL`. | | [ERR\_NGROK\_7163](/docs/errors/err_ngrok_7163) | Your account is not authorized to use the TLS edges. | | [ERR\_NGROK\_7164](/docs/errors/err_ngrok_7164) | The `ID` header cannot be removed without specifying a replacement. | | [ERR\_NGROK\_7165](/docs/errors/err_ngrok_7165) | An unsupported OAuth Provider was configured. | | [ERR\_NGROK\_7166](/docs/errors/err_ngrok_7166) | The "`PROVIDER`" OAuth Provider does not currently have a managed app available, specify a client id and client secret. | | [ERR\_NGROK\_7167](/docs/errors/err_ngrok_7167) | Your account is not authorized to use user agent filter. | | [ERR\_NGROK\_7168](/docs/errors/err_ngrok_7168) | Your account is not authorized to use JWT Validation. | | [ERR\_NGROK\_7169](/docs/errors/err_ngrok_7169) | The `TYPE` token type is invalid. Valid values are 'jwt', 'at+jwt', and 'it+jwt'. | | [ERR\_NGROK\_7170](/docs/errors/err_ngrok_7170) | The `METHOD` token method is invalid. Valid values are 'header' and 'body'. | | [ERR\_NGROK\_7171](/docs/errors/err_ngrok_7171) | The JWT Validation configuration is invalid: `DETAIL`. | | [ERR\_NGROK\_7172](/docs/errors/err_ngrok_7172) | The JWT Validation configuration must have at least one issuer in the allow list. | | [ERR\_NGROK\_7173](/docs/errors/err_ngrok_7173) | The JWT Validation issuer is invalid: `ISSUER` is not a valid URL. | | [ERR\_NGROK\_7174](/docs/errors/err_ngrok_7174) | The JWT Validation configuration must have at least one audience in the allow list. | | [ERR\_NGROK\_7175](/docs/errors/err_ngrok_7175) | The JWT Validation audience is invalid: `MSG`. | | [ERR\_NGROK\_7176](/docs/errors/err_ngrok_7176) | The JWT Validation configuration must have at least one allowed algorithm. | | [ERR\_NGROK\_7177](/docs/errors/err_ngrok_7177) | The `TYPE` algorithm is invalid. Consult the documentation for valid algorithms. | | [ERR\_NGROK\_7178](/docs/errors/err_ngrok_7178) | The JWT Validation configuration must have at least one token configuration. | | [ERR\_NGROK\_7179](/docs/errors/err_ngrok_7179) | The JWT Validation http token is invalid: `MSG`. | | [ERR\_NGROK\_7180](/docs/errors/err_ngrok_7180) | The JWT Validation http token name `NAME` is invalid. | | [ERR\_NGROK\_7181](/docs/errors/err_ngrok_7181) | The JWT Validation http token prefix `PREFIX` is invalid. | | [ERR\_NGROK\_7182](/docs/errors/err_ngrok_7182) | The JWT Validation signing key must have a way to be identified such as the 'kid' token claim. | | [ERR\_NGROK\_7183](/docs/errors/err_ngrok_7183) | The JWT Validation signing key must have a valid identification mechanism such as the 'kid' token claim. | | [ERR\_NGROK\_7184](/docs/errors/err_ngrok_7184) | The JWT Validation signing key must have at least one source. | | [ERR\_NGROK\_7185](/docs/errors/err_ngrok_7185) | The JWT Validation signing key source has an invalid additional jku: `JKU` is not a valid URL. | | [ERR\_NGROK\_7186](/docs/errors/err_ngrok_7186) | The JWT Validation signing key source has an invalid additional jku: `JKU` did not serve a JWKS. | | [ERR\_NGROK\_7187](/docs/errors/err_ngrok_7187) | The JWT Validation issuer `ISSUER` must be unique. Make sure to include it only once in the allow list. | | [ERR\_NGROK\_7188](/docs/errors/err_ngrok_7188) | The JWT Validation audience `AUDIENCE` must be unique. Make sure to include it only once in the allow list. | | [ERR\_NGROK\_7189](/docs/errors/err_ngrok_7189) | The JWT Validation http token identified by the method: `METHOD` and name: `NAME` must be unique. Make sure to include it only once in the token list. | | [ERR\_NGROK\_7190](/docs/errors/err_ngrok_7190) | The JWT Validation signing key algorithm `ALGORITHM` must be unique. Make sure to include it only once in the allowed algorithms list. | | [ERR\_NGROK\_7191](/docs/errors/err_ngrok_7191) | The JWT Validation signing key identification token claim `CLAIM` must be unique. Make sure to include it only once in the token claim list. | | [ERR\_NGROK\_7192](/docs/errors/err_ngrok_7192) | The JWT Validation signing key source additional jku `JKU` must be unique. Make sure to include it only once in the additional jku list. | | [ERR\_NGROK\_7193](/docs/errors/err_ngrok_7193) | The JWT Validation signing key sources have too many possible sources. You can supply at most 100. | | [ERR\_NGROK\_7194](/docs/errors/err_ngrok_7194) | Your account is not authorized to use policies. | | [ERR\_NGROK\_7195](/docs/errors/err_ngrok_7195) | This edge module has exceeded its policy rule limit of `LIMIT`. Log into [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) to view your options. | | [ERR\_NGROK\_7196](/docs/errors/err_ngrok_7196) | Your account has read-only access to Edges. | | [ERR\_NGROK\_8000](/docs/errors/err_ngrok_8000) | Failed to resolve host `HOST`: `ERR` | | [ERR\_NGROK\_8001](/docs/errors/err_ngrok_8001) | Failed to establish internet connectivity: `ERR` | | [ERR\_NGROK\_8002](/docs/errors/err_ngrok_8002) | Failed to establish TCP connection to `HOST` with error: `ERR` | | [ERR\_NGROK\_8003](/docs/errors/err_ngrok_8003) | Failed to establish TLS connection to `HOST` with error: `ERR`. Possible Man-in-the Middle. | | [ERR\_NGROK\_8004](/docs/errors/err_ngrok_8004) | Failed to establish tunnel protocol connection to `HOST` with error: `ERR` | | [ERR\_NGROK\_8005](/docs/errors/err_ngrok_8005) | Failed to resolve proxy IP with error: `ERR` | | [ERR\_NGROK\_8006](/docs/errors/err_ngrok_8006) | Failed to connect to proxy `URL` with error: `ERR` | | [ERR\_NGROK\_8007](/docs/errors/err_ngrok_8007) | No tunnel servers were reachable via TCP. | | [ERR\_NGROK\_8008](/docs/errors/err_ngrok_8008) | No tunnel servers could establish a TLS connection. | | [ERR\_NGROK\_8009](/docs/errors/err_ngrok_8009) | No tunnel servers could establish a tunnel connection. | | [ERR\_NGROK\_8010](/docs/errors/err_ngrok_8010) | `RESOLVER` was the only resolver to return `IP` for the hostname `HOSTNAME`. This, or the other resolvers, may be returning incorrect results. | | [ERR\_NGROK\_8011](/docs/errors/err_ngrok_8011) | IPV6 errors encountered - it may be disabled. | | [ERR\_NGROK\_8012](/docs/errors/err_ngrok_8012) | Traffic successfully made it to the ngrok agent, but the agent failed to establish a connection to the upstream web service at `ADDR`. | | [ERR\_NGROK\_8013](/docs/errors/err_ngrok_8013) | You must add a credit or debit card before you can use TCP endpoints on a free account. We require a valid card as a way to combat abuse and keep the internet a safe place. This card will NOT be charged. | | [ERR\_NGROK\_8014](/docs/errors/err_ngrok_8014) | Your agent has been blocked from connecting to ngrok because it has been suspected of violating our Acceptable Use policy: [https://ngrok.com/tos](https://ngrok.com/tos) Your actions have been reported to our security team. | | [ERR\_NGROK\_9000](/docs/errors/err_ngrok_9000) | An account is required to use basic authentication. | | [ERR\_NGROK\_9001](/docs/errors/err_ngrok_9001) | Your account is not authorized to use compression. | | [ERR\_NGROK\_9002](/docs/errors/err_ngrok_9002) | Your account is not authorized to use circuit breaker. | | [ERR\_NGROK\_9003](/docs/errors/err_ngrok_9003) | Your legacy account is not authorized to use request headers. | | [ERR\_NGROK\_9004](/docs/errors/err_ngrok_9004) | Your legacy account is not authorized to use response headers. | | [ERR\_NGROK\_9005](/docs/errors/err_ngrok_9005) | Your legacy account is not authorized to use OIDC. | | [ERR\_NGROK\_9006](/docs/errors/err_ngrok_9006) | Your legacy account is not authorized to use SAML. | | [ERR\_NGROK\_9007](/docs/errors/err_ngrok_9007) | Your legacy account is not authorized to use TLS Termination. | | [ERR\_NGROK\_9008](/docs/errors/err_ngrok_9008) | Your legacy account is not authorized to run the agent as a service. | | [ERR\_NGROK\_9009](/docs/errors/err_ngrok_9009) | Running the agent with an http/s proxy is a Pay-as-you-go feature. If this is unexpected, verify that there is no proxy\_url value in your ngrok configuration file and that the http\_proxy environment variable is not set. | | [ERR\_NGROK\_9010](/docs/errors/err_ngrok_9010) | Your legacy account is not authorized to run the agent with socks5 proxy. | | [ERR\_NGROK\_9011](/docs/errors/err_ngrok_9011) | Your legacy account is not authorized to run the agent on a custom network interface. | | [ERR\_NGROK\_9012](/docs/errors/err_ngrok_9012) | Your legacy account is not authorized to run the agent with custom CAs. | | [ERR\_NGROK\_9013](/docs/errors/err_ngrok_9013) | Your legacy account is not authorized to run the agent with mutual TLS. | | [ERR\_NGROK\_9014](/docs/errors/err_ngrok_9014) | Your legacy account is not authorized to run the agent with proxy proto. | | [ERR\_NGROK\_9015](/docs/errors/err_ngrok_9015) | Your account is not authorized to use OAuth. | | [ERR\_NGROK\_9016](/docs/errors/err_ngrok_9016) | Your account is not authorized to use webhook verification. | | [ERR\_NGROK\_9017](/docs/errors/err_ngrok_9017) | Your legacy account is not authorized to use ip restrictions. | | [ERR\_NGROK\_9018](/docs/errors/err_ngrok_9018) | An account is required to use host header rewrite. | | [ERR\_NGROK\_9019](/docs/errors/err_ngrok_9019) | Your legacy account is not authorized to use mutual TLS. | | [ERR\_NGROK\_9020](/docs/errors/err_ngrok_9020) | Your legacy account is not authorized to use the websocket to TCP converter. | | [ERR\_NGROK\_9021](/docs/errors/err_ngrok_9021) | You may specify at most one of subdomain, hostname, or domain for a tunnel. | | [ERR\_NGROK\_9022](/docs/errors/err_ngrok_9022) | Your legacy account is not authorized to use user agent filtering. | | [ERR\_NGROK\_9023](/docs/errors/err_ngrok_9023) | Your account is not authorized to use the policy module. | | [ERR\_NGROK\_9024](/docs/errors/err_ngrok_9024) | The account `ACCOUNT` is not authorized to use the requested leg number `LEG`. | | [ERR\_NGROK\_9025](/docs/errors/err_ngrok_9025) | You cannot specify both "Policy" and "TrafficPolicy". Use "TrafficPolicy" as "Policy" is deprecated. | | [ERR\_NGROK\_9026](/docs/errors/err_ngrok_9026) | The TrafficPolicy you provided is invalid: `ERROR`. | | [ERR\_NGROK\_9027](/docs/errors/err_ngrok_9027) | Your account is not authorized to use internal bindings. | | [ERR\_NGROK\_9028](/docs/errors/err_ngrok_9028) | You must specify a domain name for a internally bound endpoint. | | [ERR\_NGROK\_9029](/docs/errors/err_ngrok_9029) | Failed to start endpoint with `URL`. Endpoints with a "internal" binding must have a url that ends in one of: `SUFFIXES`. | | [ERR\_NGROK\_9030](/docs/errors/err_ngrok_9030) | Failed to start endpoint with URL `URL`, invalid port. Port must be in range range 1-65535. | | [ERR\_NGROK\_9031](/docs/errors/err_ngrok_9031) | Failed to start endpoint with URL `URL`. Endpoints must have at most `MAX` parts, but had `DEPTH`. | | [ERR\_NGROK\_9032](/docs/errors/err_ngrok_9032) | Provided domain `DOMAIN` could not be parsed: `ERROR`. | | [ERR\_NGROK\_9033](/docs/errors/err_ngrok_9033) | Provided domain `DOMAIN` had a non-numeric port component. | | [ERR\_NGROK\_9034](/docs/errors/err_ngrok_9034) | Malformed URL: `ERRMSG`. | | [ERR\_NGROK\_9035](/docs/errors/err_ngrok_9035) | Invalid port for URL with scheme `CURRENTSCHEME`, allowed ports are `WANTPORT`. | | [ERR\_NGROK\_9036](/docs/errors/err_ngrok_9036) | Invalid scheme `UNSUPPORTEDSCHEME`, allowed schemes: `ALLOWEDSCHEMES`. | | [ERR\_NGROK\_9037](/docs/errors/err_ngrok_9037) | Invalid URL `URL`, `PARTNAME` `PART` is not allowed. | | [ERR\_NGROK\_9038](/docs/errors/err_ngrok_9038) | Invalid URL, trailing '/' is not allowed. Did you mean `URLWITHOUTPATH`? | | [ERR\_NGROK\_9039](/docs/errors/err_ngrok_9039) | You must specify a domain name for a kubernetes operator bound endpoint. | | [ERR\_NGROK\_9040](/docs/errors/err_ngrok_9040) | We do not allow agents to connect to ngrok from your IP address (`IP`). | | [ERR\_NGROK\_9041](/docs/errors/err_ngrok_9041) | Supported protocols for kubernetes-bound endpoints are http, tls, and tcp. You provided `PROTO`. | | [ERR\_NGROK\_9042](/docs/errors/err_ngrok_9042) | TLS endpoints cannot be created on this version of the ngrok agent due to a vulnerability. Upgrade your agent to version 3.24.0 or later | | [ERR\_NGROK\_9043](/docs/errors/err_ngrok_9043) | HTTP endpoints are restricted for HIPAA-compliant accounts. Use HTTPS instead! | | [ERR\_NGROK\_10077](/docs/errors/err_ngrok_10077) | This account does not support JIT provisioning for SSO users. | | [ERR\_NGROK\_10078](/docs/errors/err_ngrok_10078) | This account requires users to be authenticated with a valid SSO provider. | | [ERR\_NGROK\_10080](/docs/errors/err_ngrok_10080) | The invitation `INVITE_ID` was not found | | [ERR\_NGROK\_10081](/docs/errors/err_ngrok_10081) | You must specify only the correct ID for the requested team member entry. | | [ERR\_NGROK\_10082](/docs/errors/err_ngrok_10082) | You must specify a user, bot, or membership ID when attempting to update a credentials owner. | | [ERR\_NGROK\_10083](/docs/errors/err_ngrok_10083) | You already have a valid MFA configuration of this type enabled. | | [ERR\_NGROK\_10084](/docs/errors/err_ngrok_10084) | You cannot delete the SSO Identity Provider you used to logged when SSO Enforcement is enabled on your account. | | [ERR\_NGROK\_10085](/docs/errors/err_ngrok_10085) | The endpoint of the request you are trying to replay was not found. | | [ERR\_NGROK\_10086](/docs/errors/err_ngrok_10086) | You have exceeded the rate limit of `MAX` replay requests per `INTERVAL`. | | [ERR\_NGROK\_10087](/docs/errors/err_ngrok_10087) | Request header "`HEADER_NAME`" value too large. | | [ERR\_NGROK\_10088](/docs/errors/err_ngrok_10088) | Request body size too large. | | [ERR\_NGROK\_10089](/docs/errors/err_ngrok_10089) | Failed to replay the request. | | [ERR\_NGROK\_10090](/docs/errors/err_ngrok_10090) | The request you are trying to view was not found. | | [ERR\_NGROK\_10091](/docs/errors/err_ngrok_10091) | The usage page has timed out. Refresh the page and try again. | | [ERR\_NGROK\_10092](/docs/errors/err_ngrok_10092) | The replay request's X-Account-Id header is missing or does not match the authenticated account. | | [ERR\_NGROK\_10093](/docs/errors/err_ngrok_10093) | `REASON` | | [ERR\_NGROK\_10094](/docs/errors/err_ngrok_10094) | `REASON` | | [ERR\_NGROK\_10095](/docs/errors/err_ngrok_10095) | The client IP filter value `VALUE` is not an IP address that can match a request. | | [ERR\_NGROK\_10096](/docs/errors/err_ngrok_10096) | The `FIELD` filter is too large. Filter on at most `LIMIT`. | | [ERR\_NGROK\_10097](/docs/errors/err_ngrok_10097) | The path filter value cannot match a request path: `REASON`. | | [ERR\_NGROK\_10098](/docs/errors/err_ngrok_10098) | The sign-in state is invalid or expired. Restart the sign-in flow and try again. | | [ERR\_NGROK\_10099](/docs/errors/err_ngrok_10099) | SSO is not authorized for this email domain. Contact your administrator or ngrok support if you believe this is a mistake. | | [ERR\_NGROK\_10100](/docs/errors/err_ngrok_10100) | Just-in-Time provisioning now requires Domain Control. Contact ngrok support at [support@ngrok.com](mailto:support@ngrok.com). | | [ERR\_NGROK\_10101](/docs/errors/err_ngrok_10101) | Signup domains need the Identity & Access Governance Suite add-on. Add it to your plan at [https://dashboard.ngrok.com/legacy/billing/subscription/add-ons](https://dashboard.ngrok.com/legacy/billing/subscription/add-ons), then request your domain again. | | [ERR\_NGROK\_10102](/docs/errors/err_ngrok_10102) | Signup domains send new users to your SSO identity provider. Set one up at [https://dashboard.ngrok.com/settings/auth](https://dashboard.ngrok.com/settings/auth), then request your domain again. | | [ERR\_NGROK\_10103](/docs/errors/err_ngrok_10103) | Enter a valid domain name, like example.com, then request your domain again. | | [ERR\_NGROK\_10104](/docs/errors/err_ngrok_10104) | Signup domains aren't available on this account. Contact ngrok support at [support@ngrok.com](mailto:support@ngrok.com) to enable them. | | [ERR\_NGROK\_10105](/docs/errors/err_ngrok_10105) | Your signup domain request is `COUNT` characters, and the limit is `LIMIT`. Shorten the additional information, then request your domain again. | | [ERR\_NGROK\_10106](/docs/errors/err_ngrok_10106) | Signup domain requests aren't available right now. Contact ngrok support at [support@ngrok.com](mailto:support@ngrok.com) to request your domain. | | [ERR\_NGROK\_10107](/docs/errors/err_ngrok_10107) | Something went wrong and we didn't record your signup domain request. Request your domain again, or contact ngrok support at [support@ngrok.com](mailto:support@ngrok.com). | | [ERR\_NGROK\_14000](/docs/errors/err_ngrok_14000) | The `ID` header cannot be removed without specifying a replacement. | | [ERR\_NGROK\_14001](/docs/errors/err_ngrok_14001) | Got an invalid TLS key/cert pair for TLS termination: `ERR` | | [ERR\_NGROK\_14002](/docs/errors/err_ngrok_14002) | The PEM data provided for mutual TLS could not be parsed as a certificate: `ERROR`. | | [ERR\_NGROK\_15000](/docs/errors/err_ngrok_15000) | You are not allowed to create endpoints with subdomains on `BASE`. Your account is on the `PLAN` plan, which doesn't allow starting endpoints on `BASE`. | | [ERR\_NGROK\_15001](/docs/errors/err_ngrok_15001) | The Webhook Verification secret exceeds the maximum length of `MAX`, got `SIZE`. | | [ERR\_NGROK\_15002](/docs/errors/err_ngrok_15002) | Your account is on the `PLAN` plan, which requires you to register a Domain or TCP Address through the ngrok Dashboard before starting an Endpoint. Visit [https://dashboard.ngrok.com/domains](https://dashboard.ngrok.com/domains), for HTTP and TLS endpoints, or [https://dashboard.ngrok.com/tcp-addresses](https://dashboard.ngrok.com/tcp-addresses), for TCP endpoints, to register a domain or address. Then use the `--url` flag when starting an HTTP, TLS, or TCP endpoint with your Agent. | | [ERR\_NGROK\_15003](/docs/errors/err_ngrok_15003) | Upstream protocol is invalid: `APPPROTOCOL` | | [ERR\_NGROK\_15004](/docs/errors/err_ngrok_15004) | This agent has exceeded its policy rule limit of `LIMIT`. Log into [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) to view your options. | | [ERR\_NGROK\_15005](/docs/errors/err_ngrok_15005) | The binding `BINDING` is invalid. | | [ERR\_NGROK\_15006](/docs/errors/err_ngrok_15006) | The binding `BINDING` was specified multiple times, but duplicate bindings are not allowed. | | [ERR\_NGROK\_15007](/docs/errors/err_ngrok_15007) | The Webhook Verification secret was not specified. | | [ERR\_NGROK\_15008](/docs/errors/err_ngrok_15008) | Invalid basic auth credential. Username must be at most `MAXLENGTH` characters, got `GOTLENGTH`. | | [ERR\_NGROK\_15009](/docs/errors/err_ngrok_15009) | Invalid basic auth credentials list. Your plan is limited to `MAXLENGTH` sets of credentials, got `GOTLENGTH`. | | [ERR\_NGROK\_15010](/docs/errors/err_ngrok_15010) | The binding `BINDING` refers to a k8s operator that does not exist. | | [ERR\_NGROK\_15011](/docs/errors/err_ngrok_15011) | Invalid basic auth credential. Username `USERNAME` fails to meet requirements of RFC 7617 #2.1: `ERROR` | | [ERR\_NGROK\_15012](/docs/errors/err_ngrok_15012) | Invalid basic auth credential. The password of user `USERNAME` fails to meet requirements of RFC 7617 #2.1: `ERROR` | | [ERR\_NGROK\_15013](/docs/errors/err_ngrok_15013) | Your account is requesting a dev domain that does not exist. Visit [https://dashboard.ngrok.com/domains](https://dashboard.ngrok.com/domains) to request a dev domain. | | [ERR\_NGROK\_15014](/docs/errors/err_ngrok_15014) | The credential `ID` does not have the `SCOPE` scope required to start a tunnel session. | | [ERR\_NGROK\_16000](/docs/errors/err_ngrok_16000) | The email of the user you are trying to add, `EMAIL`, does not use a domain verified for your account. | | [ERR\_NGROK\_17000](/docs/errors/err_ngrok_17000) | The MFA device type "`TYPE`" is invalid. | | [ERR\_NGROK\_17001](/docs/errors/err_ngrok_17001) | The information supplied for MFA verification was invalid. | | [ERR\_NGROK\_17002](/docs/errors/err_ngrok_17002) | You do not have any recovery codes configured. | | [ERR\_NGROK\_17003](/docs/errors/err_ngrok_17003) | The information supplied for recovery code verification was invalid. | | [ERR\_NGROK\_17004](/docs/errors/err_ngrok_17004) | A matching MFA device was not found. | | [ERR\_NGROK\_17005](/docs/errors/err_ngrok_17005) | MFA Device verification is not supported. | | [ERR\_NGROK\_17006](/docs/errors/err_ngrok_17006) | A reason is required to reset MFA for a user. | | [ERR\_NGROK\_18001](/docs/errors/err_ngrok_18001) | The provided cloud endpoint configuration is invalid: url is invalid. | | [ERR\_NGROK\_18002](/docs/errors/err_ngrok_18002) | The provided cloud endpoint traffic policy configuration is invalid: `MESSAGE` | | [ERR\_NGROK\_18003](/docs/errors/err_ngrok_18003) | The provided cloud endpoint configuration is invalid: port is invalid or missing and cannot be inferred from scheme | | [ERR\_NGROK\_18004](/docs/errors/err_ngrok_18004) | The cloud endpoint update failed because no values were provided. Specify at least one value. | | [ERR\_NGROK\_18005](/docs/errors/err_ngrok_18005) | The cloud endpoint update failed because you are not allowed to change the protocol/URL scheme in-place. | | [ERR\_NGROK\_18006](/docs/errors/err_ngrok_18006) | This account does not have any domains or addresses reserved that match the provided URL. | | [ERR\_NGROK\_18008](/docs/errors/err_ngrok_18008) | This account does not have access to Cloud Endpoints. Upgrade or switch to a pay-as-you-go plan for access. | | [ERR\_NGROK\_18009](/docs/errors/err_ngrok_18009) | This account already has a cloud endpoint on this URL. | | [ERR\_NGROK\_18010](/docs/errors/err_ngrok_18010) | You have exceeded the maximum number of cloud endpoints allowed for this account. Log into [https://dashboard.ngrok.com/billing/choose-a-plan](https://dashboard.ngrok.com/billing/choose-a-plan) to view your subscription options. | | [ERR\_NGROK\_18012](/docs/errors/err_ngrok_18012) | The description field cannot be longer than 255 characters. | | [ERR\_NGROK\_18013](/docs/errors/err_ngrok_18013) | The metadata field cannot be longer than 4096 characters. | | [ERR\_NGROK\_18014](/docs/errors/err_ngrok_18014) | The provided cloud endpoint has invalid value for field `FIELD`. Reason: | | [ERR\_NGROK\_18015](/docs/errors/err_ngrok_18015) | The provided cloud endpoint is missing the required field \`\`FIELD`.` | | [ERR\_NGROK\_18016](/docs/errors/err_ngrok_18016) | The provided cloud endpoint was invalid because pooling was set to enabled when another endpoint exists for this url that has pooling disabled. | | [ERR\_NGROK\_18017](/docs/errors/err_ngrok_18017) | The provided cloud endpoint was invalid because pooling was set to disabled when another endpoint exists for this url. | | [ERR\_NGROK\_18018](/docs/errors/err_ngrok_18018) | Supported protocols for kubernetes-bound endpoints are http, tls, and tcp. You provided `PROTO`. | | [ERR\_NGROK\_18019](/docs/errors/err_ngrok_18019) | This account does not have access to Endpoint pooling. Upgrade or switch to a pay-as-you-go plan for access. | | [ERR\_NGROK\_18020](/docs/errors/err_ngrok_18020) | The maximum number of endpoints allowed in this pool has been exceeded. Your account is limited to `LIMIT` endpoints per pool. | | [ERR\_NGROK\_18021](/docs/errors/err_ngrok_18021) | You have exceeded the maximum number of concurrent endpoints allowed for this account. Log into [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing) to review your usage and [https://dashboard.ngrok.com/billing/choose-a-plan](https://dashboard.ngrok.com/billing/choose-a-plan) to view your subscription options. | | [ERR\_NGROK\_18022](/docs/errors/err_ngrok_18022) | The provided cloud endpoint configuration is invalid: scheme is invalid or not present. | | [ERR\_NGROK\_18023](/docs/errors/err_ngrok_18023) | Invalid port for URL with scheme `CURRENTSCHEME`, allowed ports are `WANTPORT`. | | [ERR\_NGROK\_18024](/docs/errors/err_ngrok_18024) | AI Gateway has moved to [https://app.ngrok.ai](https://app.ngrok.ai). This API only allows managing existing legacy gateways. | | [ERR\_NGROK\_18025](/docs/errors/err_ngrok_18025) | `ID` is not a valid endpoint ID. | | [ERR\_NGROK\_19000](/docs/errors/err_ngrok_19000) | Internal Server Error. | | [ERR\_NGROK\_19001](/docs/errors/err_ngrok_19001) | Certificate Signing Requests Failed! Reason: `ERROR` | | [ERR\_NGROK\_19002](/docs/errors/err_ngrok_19002) | Failed to validate Kubernetes Operator! Reason: `ERROR` | | [ERR\_NGROK\_19003](/docs/errors/err_ngrok_19003) | Failed to validate endpoint selectors! Reason: `ERROR` | | [ERR\_NGROK\_20000](/docs/errors/err_ngrok_20000) | An internal error occurred. | | [ERR\_NGROK\_20001](/docs/errors/err_ngrok_20001) | Operation failed due to a configuration error: `REASON` | | [ERR\_NGROK\_20002](/docs/errors/err_ngrok_20002) | Failed to create the Upstream Service for BoundEndpoint. | | [ERR\_NGROK\_20003](/docs/errors/err_ngrok_20003) | Failed to create the Target Service for BoundEndpoint. | | [ERR\_NGROK\_20004](/docs/errors/err_ngrok_20004) | Failed to establish a connection between the Upstream and Target Services. | | [ERR\_NGROK\_20005](/docs/errors/err_ngrok_20005) | BoundEndpoint is Denied due to no matching pattern in AllowedURLs | | [ERR\_NGROK\_20006](/docs/errors/err_ngrok_20006) | Failed to create CSR. | | [ERR\_NGROK\_21000](/docs/errors/err_ngrok_21000) | Failed to lookup IP `IP`: `ERR`. | | [ERR\_NGROK\_23000](/docs/errors/err_ngrok_23000) | The vault name "`VAULT`" is invalid. Must only contain the characters "a-zA-Z0-9\_/.". | | [ERR\_NGROK\_23001](/docs/errors/err_ngrok_23001) | The vault name cannot be empty. | | [ERR\_NGROK\_23002](/docs/errors/err_ngrok_23002) | Your vault name must be less than `LENGTH` characters. | | [ERR\_NGROK\_23003](/docs/errors/err_ngrok_23003) | A Vault must be empty before it can be deleted. Remove all secrets from the vault and try again. | | [ERR\_NGROK\_23004](/docs/errors/err_ngrok_23004) | Vault names must be unique within an account. | | [ERR\_NGROK\_23005](/docs/errors/err_ngrok_23005) | You've reached the maximum number of vaults (`MAX`) under your current plan. To increase your limit, upgrade your plan at [https://dashboard.ngrok.com/billing](https://dashboard.ngrok.com/billing). | | [ERR\_NGROK\_24000](/docs/errors/err_ngrok_24000) | The secret name "`SECRET`" is invalid. Must only contain the characters "a-zA-Z0-9\_/.". | | [ERR\_NGROK\_24001](/docs/errors/err_ngrok_24001) | The secret name cannot be empty. | | [ERR\_NGROK\_24002](/docs/errors/err_ngrok_24002) | Your secret name must be less than `LENGTH` characters. | | [ERR\_NGROK\_24003](/docs/errors/err_ngrok_24003) | The secret value cannot be empty. | | [ERR\_NGROK\_24004](/docs/errors/err_ngrok_24004) | Your secret value must be less than `LENGTH` bytes. | | [ERR\_NGROK\_24005](/docs/errors/err_ngrok_24005) | Secret names must be unique within a vault. | | [ERR\_NGROK\_24006](/docs/errors/err_ngrok_24006) | Vault with ID `ID` not found. | | [ERR\_NGROK\_24007](/docs/errors/err_ngrok_24007) | You've reached the maximum number of secrets (`MAX`) allowed for vault `VID` under your current plan. To increase your limit, please upgrade your plan or contact support. | | [ERR\_NGROK\_24008](/docs/errors/err_ngrok_24008) | Required parameter 'VaultID' is missing. | | [ERR\_NGROK\_24009](/docs/errors/err_ngrok_24009) | Vault not found with name: `NAME` | | [ERR\_NGROK\_25000](/docs/errors/err_ngrok_25000) | Domain controls with redirect\_blocked\_signups\_to\_sso enabled cannot have an empty or null sso account IDs list. | | [ERR\_NGROK\_26000](/docs/errors/err_ngrok_26000) | CAA records for domain `DOMAIN` found at `FOUNDAT` do not authorize Let's Encrypt to issue certificates: `REASON` | | [ERR\_NGROK\_26001](/docs/errors/err_ngrok_26001) | CAA records for domain `DOMAIN` found at `FOUNDAT` contain an accounturi parameter restriction (`ACCOUNTURI`). This restriction cannot be validated for managed certificates. | | [ERR\_NGROK\_26002](/docs/errors/err_ngrok_26002) | CAA records for domain `DOMAIN` found at `FOUNDAT` contain unknown critical property tags `TAGS` which cannot be processed. | | [ERR\_NGROK\_26004](/docs/errors/err_ngrok_26004) | CAA records for domain `DOMAIN` found at `FOUNDAT` restrict validation methods to `ALLOWEDMETHOD`, but `REQUIREDMETHOD` is required for this domain type. | | [ERR\_NGROK\_26005](/docs/errors/err_ngrok_26005) | CAA records for domain `DOMAIN` found at `FOUNDAT` contain malformed data: `ERROR` | | [ERR\_NGROK\_26006](/docs/errors/err_ngrok_26006) | Certificate provisioning failed unexpectedly: `ERR` | | [ERR\_NGROK\_26007](/docs/errors/err_ngrok_26007) | Load CSR failed unexpectedly: `ERR` | | [ERR\_NGROK\_26008](/docs/errors/err_ngrok_26008) | Preflight check failed unexpectedly: `ERR` | | [ERR\_NGROK\_26009](/docs/errors/err_ngrok_26009) | Certificate obtain failed unexpectedly: `ERR` | | [ERR\_NGROK\_26010](/docs/errors/err_ngrok_26010) | Certificate conversion failed unexpectedly: `ERR` | | [ERR\_NGROK\_26011](/docs/errors/err_ngrok_26011) | Certificate finalization failed unexpectedly: `ERR` | | [ERR\_NGROK\_26012](/docs/errors/err_ngrok_26012) | Preflight check failed: `CHECK` | | [ERR\_NGROK\_26013](/docs/errors/err_ngrok_26013) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26014](/docs/errors/err_ngrok_26014) | CAA check failed: CAA blocked | | [ERR\_NGROK\_26015](/docs/errors/err_ngrok_26015) | DCV check failed: CNAME not found | | [ERR\_NGROK\_26016](/docs/errors/err_ngrok_26016) | DCV check failed: CNAME mismatch | | [ERR\_NGROK\_26017](/docs/errors/err_ngrok_26017) | DCV check failed: NS not found | | [ERR\_NGROK\_26018](/docs/errors/err_ngrok_26018) | DCV check failed: NS mismatch | | [ERR\_NGROK\_26019](/docs/errors/err_ngrok_26019) | DNS-01 check failed: DNS-01 challenge CNAME not found | | [ERR\_NGROK\_26020](/docs/errors/err_ngrok_26020) | DNS-01 check failed: DNS-01 challenge CNAME mismatch | | [ERR\_NGROK\_26021](/docs/errors/err_ngrok_26021) | HTTP-01 check failed: HTTP-01 unreachable | | [ERR\_NGROK\_26022](/docs/errors/err_ngrok_26022) | HTTP-01 check failed: HTTP-01 token mismatch | | [ERR\_NGROK\_26023](/docs/errors/err_ngrok_26023) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26024](/docs/errors/err_ngrok_26024) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26025](/docs/errors/err_ngrok_26025) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26026](/docs/errors/err_ngrok_26026) | CAA check failed: CAA malformed | | [ERR\_NGROK\_26027](/docs/errors/err_ngrok_26027) | CAA check failed: CAA DNS lookup failed | | [ERR\_NGROK\_26028](/docs/errors/err_ngrok_26028) | CAA check failed: CAA unknown critical flag | | [ERR\_NGROK\_26029](/docs/errors/err_ngrok_26029) | CAA check failed: CAA TLD not found | | [ERR\_NGROK\_26030](/docs/errors/err_ngrok_26030) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26031](/docs/errors/err_ngrok_26031) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26032](/docs/errors/err_ngrok_26032) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26033](/docs/errors/err_ngrok_26033) | Certificate provisioning failed: account is suspended | | [ERR\_NGROK\_26034](/docs/errors/err_ngrok_26034) | Check if account is suspended failed unexpectedly: `ERR` | | [ERR\_NGROK\_26035](/docs/errors/err_ngrok_26035) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26036](/docs/errors/err_ngrok_26036) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26037](/docs/errors/err_ngrok_26037) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26038](/docs/errors/err_ngrok_26038) | Certificate obtain failed: `ERR` | | [ERR\_NGROK\_26039](/docs/errors/err_ngrok_26039) | DCV check failed: no address records at domain apex | | [ERR\_NGROK\_26040](/docs/errors/err_ngrok_26040) | DCV check failed: domain apex does not resolve to ngrok | | [ERR\_NGROK\_26041](/docs/errors/err_ngrok_26041) | DCV check failed: authoritative nameserver unreachable | | [ERR\_NGROK\_27001](/docs/errors/err_ngrok_27001) | This account does not have access to AI Gateway API Keys. | | [ERR\_NGROK\_27002](/docs/errors/err_ngrok_27002) | The specified endpoint, `ENDPOINT_ID`, was not found. | | [ERR\_NGROK\_27003](/docs/errors/err_ngrok_27003) | The specified endpoint, `ENDPOINT_ID`, does not have AI Gateway action enabled. | | [ERR\_NGROK\_27004](/docs/errors/err_ngrok_27004) | The description field cannot be longer than 255 characters. was `REQLEN` | | [ERR\_NGROK\_27005](/docs/errors/err_ngrok_27005) | The metadata field cannot be longer than 4096 characters. was `REQLEN` | | [ERR\_NGROK\_27006](/docs/errors/err_ngrok_27006) | The `MISSING` field is required. | | [ERR\_NGROK\_27007](/docs/errors/err_ngrok_27007) | The provided API key is invalid or does not exist. Verify the key is correct and has not been deleted. Keys begin with ng\_. | | [ERR\_NGROK\_27008](/docs/errors/err_ngrok_27008) | This API key is scoped to a different gateway. Each key can only be used with the endpoint it was created for. Check the key's endpoint in the dashboard or create a new key for this gateway. | | [ERR\_NGROK\_27009](/docs/errors/err_ngrok_27009) | Failed to validate API key. Please try again later. | | [ERR\_NGROK\_27201](/docs/errors/err_ngrok_27201) | Authentication failed: `REASON` | | [ERR\_NGROK\_27202](/docs/errors/err_ngrok_27202) | No keys available for provider "`PROVIDERID`". | | [ERR\_NGROK\_27203](/docs/errors/err_ngrok_27203) | Cannot use ngrok-managed key for unknown model "`MODELID`": pricing required for billing. | | [ERR\_NGROK\_27204](/docs/errors/err_ngrok_27204) | Unsupported API surface "`PATH`". | | [ERR\_NGROK\_27205](/docs/errors/err_ngrok_27205) | API surface "`SURFACE`" is not implemented. | | [ERR\_NGROK\_27206](/docs/errors/err_ngrok_27206) | Credit check failed: `REASON` | | [ERR\_NGROK\_27207](/docs/errors/err_ngrok_27207) | Upstream request timed out. | | [ERR\_NGROK\_27208](/docs/errors/err_ngrok_27208) | All retries exhausted, last error: `LASTERROR` | | [ERR\_NGROK\_27210](/docs/errors/err_ngrok_27210) | Upstream provider rejected the request: `REASON` | | [ERR\_NGROK\_27211](/docs/errors/err_ngrok_27211) | Upstream provider rejected the API key. | | [ERR\_NGROK\_27212](/docs/errors/err_ngrok_27212) | Upstream provider denied access. | | [ERR\_NGROK\_27213](/docs/errors/err_ngrok_27213) | Upstream provider returned not found. | | [ERR\_NGROK\_27214](/docs/errors/err_ngrok_27214) | Upstream provider request timed out. | | [ERR\_NGROK\_27215](/docs/errors/err_ngrok_27215) | Request payload exceeded upstream provider limits. | | [ERR\_NGROK\_27216](/docs/errors/err_ngrok_27216) | Upstream provider could not process the request: `REASON` | | [ERR\_NGROK\_27217](/docs/errors/err_ngrok_27217) | Upstream provider rate limit exceeded. | | [ERR\_NGROK\_27218](/docs/errors/err_ngrok_27218) | Upstream provider returned an internal server error. | | [ERR\_NGROK\_27219](/docs/errors/err_ngrok_27219) | Upstream provider returned a bad gateway error. | | [ERR\_NGROK\_27220](/docs/errors/err_ngrok_27220) | Upstream provider is unavailable. | | [ERR\_NGROK\_27221](/docs/errors/err_ngrok_27221) | Upstream provider gateway timed out. | | [ERR\_NGROK\_27222](/docs/errors/err_ngrok_27222) | Upstream provider returned an unexpected status: `REASON` | | [ERR\_NGROK\_27223](/docs/errors/err_ngrok_27223) | Network error contacting upstream provider: `REASON` | | [ERR\_NGROK\_27224](/docs/errors/err_ngrok_27224) | Unknown model "`MODELID`".`SUGGESTION` | | [ERR\_NGROK\_27225](/docs/errors/err_ngrok_27225) | Unknown provider "`PROVIDERID`". | | [ERR\_NGROK\_27226](/docs/errors/err_ngrok_27226) | Provider "`PROVIDERID`" does not support API surface "`SURFACE`". | | [ERR\_NGROK\_27227](/docs/errors/err_ngrok_27227) | No backend available: `REASON` | | [ERR\_NGROK\_27228](/docs/errors/err_ngrok_27228) | Invalid request: `REASON` | | [ERR\_NGROK\_27229](/docs/errors/err_ngrok_27229) | Invalid model address "`ADDRESS`". | | [ERR\_NGROK\_27230](/docs/errors/err_ngrok_27230) | "`MODEL`" is not a known model, and "`PROVIDER`" is not a recognized provider. | | [ERR\_NGROK\_27231](/docs/errors/err_ngrok_27231) | Provider "`PROVIDERID`" is not allowed by the access policy. | | [ERR\_NGROK\_27232](/docs/errors/err_ngrok_27232) | Model "`MODELID`" is not allowed by the access policy. | | [ERR\_NGROK\_27233](/docs/errors/err_ngrok_27233) | ngrok has no catalog entry for "`MODELID`" on provider "`PROVIDERID`", so an ngrok-managed key can't serve it. Add your own provider key to use it. | | [ERR\_NGROK\_27234](/docs/errors/err_ngrok_27234) | Upstream provider returned a non-streaming response (content-type `CONTENTTYPE`) despite a streaming request. | | [ERR\_NGROK\_27235](/docs/errors/err_ngrok_27235) | Upstream provider failed to respond. | | [ERR\_NGROK\_27236](/docs/errors/err_ngrok_27236) | Model "`MODELID`" is not priced for managed billing yet for provider "`PROVIDERID`"; add your own provider key to use it. | | [ERR\_NGROK\_27237](/docs/errors/err_ngrok_27237) | Request rejected: a routing loop was detected. Double check your custom provider URLs do not cause an infinite proxy loop. | | [ERR\_NGROK\_27238](/docs/errors/err_ngrok_27238) | An ngrok-managed key can't serve this request, because ngrok can't bill `FEATURE` for "`MODELID`" on provider "`PROVIDERID`". Add your own provider key to use it. | | [ERR\_NGROK\_27239](/docs/errors/err_ngrok_27239) | ngrok can't serve "`MODELID`" on provider "`PROVIDERID`" with an ngrok-managed key right now. Add your own provider key to use it. | | [ERR\_NGROK\_27240](/docs/errors/err_ngrok_27240) | Your account has no `FILES` for provider "`PROVIDERID`". An ngrok-managed key can only use files your account uploaded. Upload the file first, or add your own provider key. | | [ERR\_NGROK\_27241](/docs/errors/err_ngrok_27241) | The `HEADER` header is required on Files API requests. Set it to one of: `PROVIDERS`. | | [ERR\_NGROK\_27242](/docs/errors/err_ngrok_27242) | Provider "`PROVIDERID`" does not support the Files API. Supported providers: `PROVIDERS`. | | [ERR\_NGROK\_27243](/docs/errors/err_ngrok_27243) | The Files API "`PARAMETER`" parameter must be a whole number from 1 to `MAXIMUM`. Ask for a smaller page, then follow the cursor the response returns to read the rest. | | [ERR\_NGROK\_27244](/docs/errors/err_ngrok_27244) | Your account has no file "`FILEID`" for provider "`PROVIDERID`". | | [ERR\_NGROK\_27245](/docs/errors/err_ngrok_27245) | The Files API cannot apply "`PARAMETER`" to a listing served by an ngrok-managed key for provider "`PROVIDERID`". Drop the parameter, or add your own provider key. | | [ERR\_NGROK\_27246](/docs/errors/err_ngrok_27246) | Your account already holds `USEDBYTES` of the `LIMITBYTES` an ngrok-managed key allows for provider "`PROVIDERID`". Delete files you no longer need, or add your own provider key. | | [ERR\_NGROK\_27247](/docs/errors/err_ngrok_27247) | A Files API upload for provider "`PROVIDERID`" must be a multipart/form-data request. | | [ERR\_NGROK\_27248](/docs/errors/err_ngrok_27248) | A Files API upload can be at most `LIMIT`. Send a smaller file, or upload it to your provider account directly. | | [ERR\_NGROK\_27249](/docs/errors/err_ngrok_27249) | ngrok doesn't offer "`MODELID`" on provider "`PROVIDERID`" with an ngrok-managed key. Add your own provider key to use it. | | [ERR\_NGROK\_27250](/docs/errors/err_ngrok_27250) | ngrok doesn't store interactions yet, so "store" can't be true. Omit it or set it to false to send a stateless request. | | [ERR\_NGROK\_27501](/docs/errors/err_ngrok_27501) | The name field cannot be longer than 255 characters. was `REQLEN` | | [ERR\_NGROK\_27502](/docs/errors/err_ngrok_27502) | The description field cannot be longer than 255 characters. was `REQLEN` | | [ERR\_NGROK\_27503](/docs/errors/err_ngrok_27503) | The metadata field cannot be longer than 4096 characters. was `REQLEN` | | [ERR\_NGROK\_27504](/docs/errors/err_ngrok_27504) | This account does not have access to AI Gateway Provider Keys. | | [ERR\_NGROK\_27505](/docs/errors/err_ngrok_27505) | AI Gateway provider key encryption is not configured for this deployment | | [ERR\_NGROK\_27506](/docs/errors/err_ngrok_27506) | Cannot create more than `MAXKEYS` provider keys for provider "`PROVIDERID`" on a single managed key. | | [ERR\_NGROK\_27507](/docs/errors/err_ngrok_27507) | The value field is required and cannot be empty. | | [ERR\_NGROK\_27508](/docs/errors/err_ngrok_27508) | The provider\_id "`PROVIDERID`" is not a supported AI provider. | | [ERR\_NGROK\_27509](/docs/errors/err_ngrok_27509) | The specified AI Gateway API Key, `AIGKID`, was not found. | | [ERR\_NGROK\_27510](/docs/errors/err_ngrok_27510) | The `MISSING` field is required. | | [ERR\_NGROK\_27511](/docs/errors/err_ngrok_27511) | This provider key cannot be deleted because it is referenced by a router rule. Remove those references first. | | [ERR\_NGROK\_27601](/docs/errors/err_ngrok_27601) | AI Gateway is currently in pre-release and not yet available to your account. | | [ERR\_NGROK\_27602](/docs/errors/err_ngrok_27602) | The `MISSING` field is required. | | [ERR\_NGROK\_27603](/docs/errors/err_ngrok_27603) | The name field cannot be longer than 255 characters. was `REQLEN` | | [ERR\_NGROK\_27604](/docs/errors/err_ngrok_27604) | The metadata field cannot be longer than 4096 characters. was `REQLEN` | | [ERR\_NGROK\_27605](/docs/errors/err_ngrok_27605) | Invalid router configuration: `REASON`. | | [ERR\_NGROK\_27606](/docs/errors/err_ngrok_27606) | `REASON` | | [ERR\_NGROK\_27607](/docs/errors/err_ngrok_27607) | Resource already exists. | | [ERR\_NGROK\_27609](/docs/errors/err_ngrok_27609) | Request canceled. | | [ERR\_NGROK\_27610](/docs/errors/err_ngrok_27610) | Request deadline exceeded. | | [ERR\_NGROK\_27611](/docs/errors/err_ngrok_27611) | `RESOURCE` not found. | | [ERR\_NGROK\_27612](/docs/errors/err_ngrok_27612) | Operation not implemented: `OPERATION` | | [ERR\_NGROK\_27613](/docs/errors/err_ngrok_27613) | Upstream service error. | | [ERR\_NGROK\_27614](/docs/errors/err_ngrok_27614) | Method not allowed. | | [ERR\_NGROK\_27615](/docs/errors/err_ngrok_27615) | The resource is still in use and cannot be deleted. | | [ERR\_NGROK\_27616](/docs/errors/err_ngrok_27616) | A credit purchase is already in progress. Wait for it to finish before starting another. | | [ERR\_NGROK\_27617](/docs/errors/err_ngrok_27617) | The idempotency key was already used for a different request. Use a new key. | | [ERR\_NGROK\_27701](/docs/errors/err_ngrok_27701) | The name field cannot be longer than 255 characters. was `REQLEN` | | [ERR\_NGROK\_27702](/docs/errors/err_ngrok_27702) | The metadata field cannot be longer than 4096 characters. was `REQLEN` | | [ERR\_NGROK\_27703](/docs/errors/err_ngrok_27703) | The base\_url field cannot be longer than 2048 characters. was `REQLEN` | | [ERR\_NGROK\_27704](/docs/errors/err_ngrok_27704) | The `MISSING` field is required. | | [ERR\_NGROK\_27705](/docs/errors/err_ngrok_27705) | The provider\_id "`PROVIDERID`" is reserved by a built-in ngrok provider and cannot be used for a user-defined provider. | | [ERR\_NGROK\_27706](/docs/errors/err_ngrok_27706) | The provider\_id field cannot be longer than 255 characters. was `REQLEN` | | [ERR\_NGROK\_27707](/docs/errors/err_ngrok_27707) | The API surface format "`FORMAT`" is not a supported ngrok format. | | [ERR\_NGROK\_27708](/docs/errors/err_ngrok_27708) | The API surface "`SURFACE`" is not a supported ngrok surface. | | [ERR\_NGROK\_27709](/docs/errors/err_ngrok_27709) | The model\_id field cannot be longer than 255 characters. was `REQLEN` | | [ERR\_NGROK\_27710](/docs/errors/err_ngrok_27710) | This provider cannot be deleted because it is referenced by a provider key or an access key configuration. Remove those references first. | | [ERR\_NGROK\_27711](/docs/errors/err_ngrok_27711) | The model\_id "`MODELID`" is listed more than once for this provider. | | [ERR\_NGROK\_27712](/docs/errors/err_ngrok_27712) | A provider with provider\_id "`PROVIDERID`" already exists for this account. | | [ERR\_NGROK\_27713](/docs/errors/err_ngrok_27713) | The provider\_model\_id field cannot be longer than 255 characters. was `REQLEN` | | [ERR\_NGROK\_27714](/docs/errors/err_ngrok_27714) | An unsupported\_params entry cannot be longer than 255 characters. was `REQLEN` | | [ERR\_NGROK\_27715](/docs/errors/err_ngrok_27715) | The unsupported\_params entry "`PARAM`" is listed more than once for this model. | | [ERR\_NGROK\_27716](/docs/errors/err_ngrok_27716) | The unsupported\_params array cannot contain more than 100 entries. was `REQLEN` | | [ERR\_NGROK\_27717](/docs/errors/err_ngrok_27717) | The unsupported\_params array contains an empty string; every entry must be a non-empty param name. | | [ERR\_NGROK\_27718](/docs/errors/err_ngrok_27718) | The base\_url host "`HOST`" is not allowed. | | [ERR\_NGROK\_28000](/docs/errors/err_ngrok_28000) | Pod identity metadata too large: `REASON` | | [ERR\_NGROK\_28001](/docs/errors/err_ngrok_28001) | Failed to retrieve pod identity metadata: ensure pod has proper annotations and service account configuration | | [ERR\_NGROK\_29000](/docs/errors/err_ngrok_29000) | Your account is limited to `MAX` personal access tokens. | | [ERR\_NGROK\_29001](/docs/errors/err_ngrok_29001) | You don't have permission to perform this action. Your credentials are missing the `SCOPE` scope. | | [ERR\_NGROK\_AI\_GATEWAY\_REQUEST\_BODY\_TOO\_LONG](/docs/errors/err_ngrok_ai_gateway_request_body_too_long) | Request body is too long. Max `LIMIT`. | | [ERR\_NGROK\_BILLING\_CARD\_MANDATE\_IN\_PROGRESS](/docs/errors/err_ngrok_billing_card_mandate_in_progress) | Your bank is still approving this card for automatic payments. This takes a few minutes. You can complete the purchase once it finishes. | | [ERR\_NGROK\_BILLING\_CARD\_MANDATE\_REFUSED](/docs/errors/err_ngrok_billing_card_mandate_refused) | Your bank refused to approve this card for automatic payments. Use a different card. | | [ERR\_NGROK\_IAM\_ACCESS\_TOKEN\_OWNER\_ROLE\_REQUIRED](/docs/errors/err_ngrok_iam_access_token_owner_role_required) | The token owner has no role that grants access. Give the owner a role first. | | [ERR\_NGROK\_IAM\_SERVICE\_USER\_PERMISSIONS\_USE\_ROLES](/docs/errors/err_ngrok_iam_service_user_permissions_use_roles) | You can't set a service user's permissions directly. Change its roles instead. | | [ERR\_NGROK\_IAM\_SERVICE\_USER\_ROLE\_CONFLICT](/docs/errors/err_ngrok_iam_service_user_role_conflict) | A service user can hold only one of the roles 'developer' and 'developer-readonly'. | | [ERR\_NGROK\_IAM\_SERVICE\_USER\_ROLE\_NOT\_ALLOWED](/docs/errors/err_ngrok_iam_service_user_role_not_allowed) | A service user can't hold the role `ROLE`. Allowed roles: developer, developer-readonly, team-manager. | | [ERR\_NGROK\_SHIP\_APP\_BUILD\_CONFIG\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_app_build_config_not_allowed) | The 'build\_config' field applies only to repository apps. | | [ERR\_NGROK\_SHIP\_APP\_ENVIRONMENTS\_OR\_PR\_PREVIEWS\_REQUIRED](/docs/errors/err_ngrok_ship_app_environments_or_pr_previews_required) | The request must include at least one environment or 'pr\_previews'. | | [ERR\_NGROK\_SHIP\_APP\_ENVIRONMENTS\_REQUIRED](/docs/errors/err_ngrok_ship_app_environments_required) | The 'environments' list must include at least one environment. | | [ERR\_NGROK\_SHIP\_APP\_ENVIRONMENT\_BRANCH\_REQUIRED](/docs/errors/err_ngrok_ship_app_environment_branch_required) | The 'environments\[`INDEX`].branch' field must not be empty. | | [ERR\_NGROK\_SHIP\_APP\_ENVIRONMENT\_NAME\_DUPLICATED](/docs/errors/err_ngrok_ship_app_environment_name_duplicated) | Environment name `NAME` is used more than once in 'environments'. | | [ERR\_NGROK\_SHIP\_APP\_ENVIRONMENT\_NAME\_REQUIRED](/docs/errors/err_ngrok_ship_app_environment_name_required) | The 'environments\[`INDEX`].name' field is required. No name derives from branch `BRANCH`. | | [ERR\_NGROK\_SHIP\_APP\_ENVIRONMENT\_TRACKING\_REF\_REQUIRED](/docs/errors/err_ngrok_ship_app_environment_tracking_ref_required) | Environment `NAME` must have a 'tracking\_ref'. | | [ERR\_NGROK\_SHIP\_APP\_ENVIRONMENT\_URL\_DUPLICATED](/docs/errors/err_ngrok_ship_app_environment_url_duplicated) | Environment URL `URL` is used more than once in 'environments'. | | [ERR\_NGROK\_SHIP\_APP\_IMAGE\_REPOSITORY\_REQUIRED](/docs/errors/err_ngrok_ship_app_image_repository_required) | The 'image\_repository' field must not be empty. | | [ERR\_NGROK\_SHIP\_APP\_NAME\_ALREADY\_EXISTS](/docs/errors/err_ngrok_ship_app_name_already_exists) | An app named "`NAME`" already exists on this account. | | [ERR\_NGROK\_SHIP\_APP\_NAME\_BAD\_START\_OR\_END](/docs/errors/err_ngrok_ship_app_name_bad_start_or_end) | App name `NAME` must not start or end with `CHAR`. | | [ERR\_NGROK\_SHIP\_APP\_NAME\_ILLEGAL\_CHARACTER](/docs/errors/err_ngrok_ship_app_name_illegal_character) | App name `NAME` contains the illegal character `CHAR`. Allowed: lowercase letters, digits, '-', and '\_'. | | [ERR\_NGROK\_SHIP\_APP\_NAME\_NOT\_FOUND](/docs/errors/err_ngrok_ship_app_name_not_found) | No app named `NAME` exists on this account. | | [ERR\_NGROK\_SHIP\_APP\_NAME\_REQUIRED](/docs/errors/err_ngrok_ship_app_name_required) | App name must not be empty. | | [ERR\_NGROK\_SHIP\_APP\_NAME\_TOO\_LONG](/docs/errors/err_ngrok_ship_app_name_too_long) | App name is too long. Max 255 bytes, got `LENGTH` bytes. | | [ERR\_NGROK\_SHIP\_APP\_NOT\_FOUND](/docs/errors/err_ngrok_ship_app_not_found) | No app with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_APP\_PULL\_CREDENTIAL\_NOT\_FOUND](/docs/errors/err_ngrok_ship_app_pull_credential_not_found) | No registry credential with the ID `ID` exists on this account. Check 'pull\_credential\_id'. | | [ERR\_NGROK\_SHIP\_APP\_PULL\_CREDENTIAL\_REGISTRY\_CONFLICT](/docs/errors/err_ngrok_ship_app_pull_credential_registry_conflict) | Image repository `REPOSITORY` is not on `HOST`, the registry the credential signs in to. | | [ERR\_NGROK\_SHIP\_APP\_PULL\_CREDENTIAL\_REQUIRED](/docs/errors/err_ngrok_ship_app_pull_credential_required) | A private repository that publishes to ghcr.io needs a 'pull\_credential\_id'. Runners cannot pull the image without one. | | [ERR\_NGROK\_SHIP\_APP\_REF\_REQUIRED](/docs/errors/err_ngrok_ship_app_ref_required) | A ship ref is 'ship/' followed by the app name. | | [ERR\_NGROK\_SHIP\_APP\_REPOSITORY\_CONNECTION\_UNAVAILABLE](/docs/errors/err_ngrok_ship_app_repository_connection_unavailable) | The ngrok GitHub app is `STATE` for this repository. Reinstall or reactivate it on GitHub. | | [ERR\_NGROK\_SHIP\_APP\_REPOSITORY\_NOT\_CONFIGURED](/docs/errors/err_ngrok_ship_app_repository_not_configured) | App `NAME` has no connected repository. Connect a GitHub repository before you add a continuous delivery. | | [ERR\_NGROK\_SHIP\_APP\_REPOSITORY\_WORKFLOW\_ALREADY\_EXISTS](/docs/errors/err_ngrok_ship_app_repository_workflow_already_exists) | Workflow `PATH` in this repository is already connected to another app. | | [ERR\_NGROK\_SHIP\_BUILD\_CONFIG\_BUILD\_COMMAND\_TOO\_LONG](/docs/errors/err_ngrok_ship_build_config_build_command_too_long) | The 'build\_config.build\_command' field is too long. Max 4096 bytes, got `LENGTH` bytes. | | [ERR\_NGROK\_SHIP\_BUILD\_CONFIG\_COMMANDS\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_build_config_commands_not_allowed) | The 'build\_command' and 'run\_command' fields apply only to automatic builds. | | [ERR\_NGROK\_SHIP\_BUILD\_CONFIG\_DOCKERFILE\_PATH\_FILE\_REQUIRED](/docs/errors/err_ngrok_ship_build_config_dockerfile_path_file_required) | The 'dockerfile\_path' value `VALUE` must name a file. | | [ERR\_NGROK\_SHIP\_BUILD\_CONFIG\_DOCKERFILE\_PATH\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_build_config_dockerfile_path_not_allowed) | The 'dockerfile\_path' field applies only to Dockerfile builds. | | [ERR\_NGROK\_SHIP\_BUILD\_CONFIG\_PATH\_BAD\_START\_OR\_END](/docs/errors/err_ngrok_ship_build_config_path_bad_start_or_end) | The `FIELD` value `VALUE` must not start or end with '/'. | | [ERR\_NGROK\_SHIP\_BUILD\_CONFIG\_PATH\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_build_config_path_not_allowed) | The `FIELD` value `VALUE` leaves the repository. '..' segments are not allowed. | | [ERR\_NGROK\_SHIP\_BUILD\_CONFIG\_PATH\_REQUIRED](/docs/errors/err_ngrok_ship_build_config_path_required) | The `FIELD` field must not be empty. | | [ERR\_NGROK\_SHIP\_BUILD\_CONFIG\_PROVIDER\_UNSUPPORTED](/docs/errors/err_ngrok_ship_build_config_provider_unsupported) | Build provider `PROVIDER` is not supported. Allowed: 'dockerfile' and 'automatic'. | | [ERR\_NGROK\_SHIP\_BUILD\_CONFIG\_RUN\_COMMAND\_TOO\_LONG](/docs/errors/err_ngrok_ship_build_config_run_command_too_long) | The 'build\_config.run\_command' field is too long. Max 4096 bytes, got `LENGTH` bytes. | | [ERR\_NGROK\_SHIP\_CHATBOT\_ACCOUNT\_CONNECTION\_ALREADY\_EXISTS](/docs/errors/err_ngrok_ship_chatbot_account_connection_already_exists) | This ngrok account is already connected to a Slack workspace. Disconnect it first. | | [ERR\_NGROK\_SHIP\_CHATBOT\_CHANNEL\_NAME\_REQUIRED](/docs/errors/err_ngrok_ship_chatbot_channel_name_required) | `PROVIDER` returned no name for channel `CHANNEL`. Subscribe from a named channel. | | [ERR\_NGROK\_SHIP\_CHATBOT\_CHANNEL\_READ\_UNAVAILABLE](/docs/errors/err_ngrok_ship_chatbot_channel_read_unavailable) | We could not read channel `CHANNEL` from `PROVIDER`: `REASON` | | [ERR\_NGROK\_SHIP\_CHATBOT\_ENTITLEMENT\_DENIED](/docs/errors/err_ngrok_ship_chatbot_entitlement_denied) | Your plan doesn't include the ngrok chat agent. Upgrade to connect a Slack workspace. | | [ERR\_NGROK\_SHIP\_CHATBOT\_INSTALLATION\_ENABLED\_REQUIRED](/docs/errors/err_ngrok_ship_chatbot_installation_enabled_required) | The chat agent is paused for this workspace. Resume it in the dashboard before you subscribe a channel. | | [ERR\_NGROK\_SHIP\_CHATBOT\_INSTALLATION\_NOT\_FOUND](/docs/errors/err_ngrok_ship_chatbot_installation_not_found) | No chat agent installation with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_CHATBOT\_INSTALLER\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_chatbot_installer_not_allowed) | Only a Slack workspace admin or owner can connect ngrok to this workspace. Ask an admin to reconnect the Slack integration, then try again. | | [ERR\_NGROK\_SHIP\_CHATBOT\_INTEGRATION\_NOT\_CONFIGURED](/docs/errors/err_ngrok_ship_chatbot_integration_not_configured) | This Slack integration is not connected. Reconnect it, then enable the chatbot again. | | [ERR\_NGROK\_SHIP\_CHATBOT\_INTEGRATION\_PROVIDER\_UNSUPPORTED](/docs/errors/err_ngrok_ship_chatbot_integration_provider_unsupported) | Integration `ID` is not a chat platform integration. Its provider is `PROVIDER`. | | [ERR\_NGROK\_SHIP\_CHATBOT\_PROVIDER\_CREDENTIAL\_REFUSED](/docs/errors/err_ngrok_ship_chatbot_provider_credential_refused) | `PROVIDER` rejected the integration's credential (`REASON`). Reconnect the `PROVIDER` integration. | | [ERR\_NGROK\_SHIP\_CHATBOT\_PROVIDER\_UNAVAILABLE](/docs/errors/err_ngrok_ship_chatbot_provider_unavailable) | We could not reach `PROVIDER` to verify the integration: `REASON` | | [ERR\_NGROK\_SHIP\_CHATBOT\_SUBSCRIPTION\_FILTER\_INVALID](/docs/errors/err_ngrok_ship_chatbot_subscription_filter_invalid) | Invalid subscription filter: `DETAIL`. | | [ERR\_NGROK\_SHIP\_CHATBOT\_SUBSCRIPTION\_NOT\_FOUND](/docs/errors/err_ngrok_ship_chatbot_subscription_not_found) | Subscription `ID` not found. | | [ERR\_NGROK\_SHIP\_CHATBOT\_SUBSCRIPTION\_SUBJECT\_UNSUPPORTED](/docs/errors/err_ngrok_ship_chatbot_subscription_subject_unsupported) | `REF` is not something I can subscribe to. Known namespaces: `NAMESPACES`. | | [ERR\_NGROK\_SHIP\_CHATBOT\_WORKSPACE\_CONNECTION\_ALREADY\_EXISTS](/docs/errors/err_ngrok_ship_chatbot_workspace_connection_already_exists) | This ngrok account has already connected this Slack workspace. | | [ERR\_NGROK\_SHIP\_CHECK\_LIMIT\_EXCEEDED](/docs/errors/err_ngrok_ship_check_limit_exceeded) | Too many health checks. Max 10, got `COUNT`. | | [ERR\_NGROK\_SHIP\_CHECK\_NAME\_BAD\_START](/docs/errors/err_ngrok_ship_check_name_bad_start) | Health check name `NAME` must start with a lowercase letter. | | [ERR\_NGROK\_SHIP\_CHECK\_NAME\_DUPLICATED](/docs/errors/err_ngrok_ship_check_name_duplicated) | Health check name `NAME` is used more than once. | | [ERR\_NGROK\_SHIP\_CHECK\_NAME\_ILLEGAL\_CHARACTER](/docs/errors/err_ngrok_ship_check_name_illegal_character) | Health check name `NAME` contains the illegal character `CHAR`. Allowed: lowercase letters, digits, '-', and '\_'. | | [ERR\_NGROK\_SHIP\_CHECK\_NAME\_REQUIRED](/docs/errors/err_ngrok_ship_check_name_required) | The 'checks\[`INDEX`].name' field must not be empty. | | [ERR\_NGROK\_SHIP\_CHECK\_NAME\_TOO\_LONG](/docs/errors/err_ngrok_ship_check_name_too_long) | The 'checks\[`INDEX`].name' field is too long. Max 63 bytes, got `LENGTH` bytes. | | [ERR\_NGROK\_SHIP\_CHECK\_PATH\_BAD\_START](/docs/errors/err_ngrok_ship_check_path_bad_start) | Health check `NAME` path `PATH` must start with '/'. | | [ERR\_NGROK\_SHIP\_CHECK\_PATH\_ILLEGAL\_CHARACTER](/docs/errors/err_ngrok_ship_check_path_illegal_character) | Health check `NAME` path `PATH` must not contain '?' or '#'. | | [ERR\_NGROK\_SHIP\_CHECK\_PATH\_REQUIRED](/docs/errors/err_ngrok_ship_check_path_required) | Health check `NAME` must have a 'path'. | | [ERR\_NGROK\_SHIP\_CHECK\_PATH\_TOO\_LONG](/docs/errors/err_ngrok_ship_check_path_too_long) | Health check `NAME` path is too long. Max 255 bytes, got `LENGTH` bytes. | | [ERR\_NGROK\_SHIP\_CHECK\_PORT\_OUT\_OF\_RANGE](/docs/errors/err_ngrok_ship_check_port_out_of_range) | Health check `NAME` port must be 1 to 65535, or 0 for the default. Got `PORT`. | | [ERR\_NGROK\_SHIP\_COMPUTE\_INSTANCE\_OFFERING\_ID\_REQUIRED](/docs/errors/err_ngrok_ship_compute_instance_offering_id_required) | The 'offering\_id' field is required. | | [ERR\_NGROK\_SHIP\_CONTAINER\_IMAGE\_NOT\_FOUND](/docs/errors/err_ngrok_ship_container_image_not_found) | Image `IMAGE` was not found at `HOST`, or the credential cannot see it. | | [ERR\_NGROK\_SHIP\_CONTAINER\_IMAGE\_PLATFORM\_REQUIRED](/docs/errors/err_ngrok_ship_container_image_platform_required) | Image `IMAGE` lists no runnable platform. | | [ERR\_NGROK\_SHIP\_CONTAINER\_IMAGE\_PULL\_CREDENTIAL\_REQUIRED](/docs/errors/err_ngrok_ship_container_image_pull_credential_required) | Registry `HOST` requires a credential to read `IMAGE`. | | [ERR\_NGROK\_SHIP\_CONTAINER\_IMAGE\_PULL\_DENIED](/docs/errors/err_ngrok_ship_container_image_pull_denied) | Registry `HOST` rejected the pull credential for `IMAGE` (HTTP `STATUS`). | | [ERR\_NGROK\_SHIP\_CONTAINER\_IMAGE\_REFERENCE\_INVALID](/docs/errors/err_ngrok_ship_container_image_reference_invalid) | The image reference `IMAGE` is not valid: `REASON` | | [ERR\_NGROK\_SHIP\_CONTAINER\_IMAGE\_REFERENCE\_REQUIRED](/docs/errors/err_ngrok_ship_container_image_reference_required) | The 'image\_ref' field must not be empty. | | [ERR\_NGROK\_SHIP\_CONTAINER\_IMAGE\_REGISTRY\_ADDRESS\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_container_image_registry_address_not_allowed) | Registry `HOST` resolves to a private or reserved address. ngrok cannot pull from it. | | [ERR\_NGROK\_SHIP\_CONTAINER\_IMAGE\_REGISTRY\_UNAVAILABLE](/docs/errors/err_ngrok_ship_container_image_registry_unavailable) | Registry `HOST` did not answer for `IMAGE`: `REASON` | | [ERR\_NGROK\_SHIP\_CONTAINER\_IMAGE\_TAG\_NOT\_FOUND](/docs/errors/err_ngrok_ship_container_image_tag_not_found) | Image `REPOSITORY` has no tag or digest `REFERENCE` at `HOST`. | | [ERR\_NGROK\_SHIP\_CONTINUOUS\_DELIVERY\_NOT\_FOUND](/docs/errors/err_ngrok_ship_continuous_delivery_not_found) | No continuous delivery with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_CONTINUOUS\_DELIVERY\_PREVIEW\_TRACKING\_REF\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_continuous_delivery_preview_tracking_ref_not_allowed) | A pull request preview tracks its pull request's branch. The 'tracking\_ref' field cannot change. | | [ERR\_NGROK\_SHIP\_CONTINUOUS\_DELIVERY\_TRACKING\_REF\_NOT\_FOUND](/docs/errors/err_ngrok_ship_continuous_delivery_tracking_ref_not_found) | Branch `BRANCH` does not exist in the repository, or the ngrok GitHub app cannot see it. | | [ERR\_NGROK\_SHIP\_CONTINUOUS\_DELIVERY\_TRACKING\_REF\_REQUIRED](/docs/errors/err_ngrok_ship_continuous_delivery_tracking_ref_required) | The 'tracking\_ref' field must not be empty. | | [ERR\_NGROK\_SHIP\_CPU\_MILLICORES\_OUT\_OF\_RANGE](/docs/errors/err_ngrok_ship_cpu_millicores_out_of_range) | The 'cpu\_millicores' field must be 0 or more, got `VALUE`. 0 uses the default. | | [ERR\_NGROK\_SHIP\_DEPLOYMENT\_NOT\_FOUND](/docs/errors/err_ngrok_ship_deployment_not_found) | No deployment with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_DOCKERFILE\_FROM\_REQUIRED](/docs/errors/err_ngrok_ship_dockerfile_from_required) | Dockerfile `PATH` on branch `BRANCH` has no FROM instruction. | | [ERR\_NGROK\_SHIP\_DOCKERFILE\_NOT\_FOUND](/docs/errors/err_ngrok_ship_dockerfile_not_found) | Dockerfile `PATH` is missing or empty on branch `BRANCH`. | | [ERR\_NGROK\_SHIP\_DOCKERFILE\_READ\_REFUSED](/docs/errors/err_ngrok_ship_dockerfile_read_refused) | GitHub refused to read Dockerfile `PATH` from branch `BRANCH` (HTTP `STATUS`). Check the ngrok GitHub app's access to the repository. | | [ERR\_NGROK\_SHIP\_DOCKERFILE\_READ\_UNAVAILABLE](/docs/errors/err_ngrok_ship_dockerfile_read_unavailable) | GitHub failed to return Dockerfile `PATH` from branch `BRANCH` (HTTP `STATUS`). | | [ERR\_NGROK\_SHIP\_DOMAIN\_APEX\_UNSUPPORTED](/docs/errors/err_ngrok_ship_domain_apex_unsupported) | `PROVIDER` cannot point a root domain at ngrok. It serves no ALIAS, ANAME, or flattened CNAME record. | | [ERR\_NGROK\_SHIP\_DOMAIN\_AUTOMATION\_NOT\_CONFIGURED](/docs/errors/err_ngrok_ship_domain_automation_not_configured) | Automated DNS setup is not configured in this environment. | | [ERR\_NGROK\_SHIP\_DOMAIN\_AUTOMATION\_PROVIDER\_UNSUPPORTED](/docs/errors/err_ngrok_ship_domain_automation_provider_unsupported) | ngrok cannot create DNS records at this provider yet. | | [ERR\_NGROK\_SHIP\_DOMAIN\_CERT\_ID\_REQUIRED](/docs/errors/err_ngrok_ship_domain_cert_id_required) | The 'cert\_id' field must not be empty when 'cert' is 'uploaded'. | | [ERR\_NGROK\_SHIP\_DOMAIN\_DNS\_RECORDS\_UNSUPPORTED](/docs/errors/err_ngrok_ship_domain_dns_records_unsupported) | This domain needs no DNS records. | | [ERR\_NGROK\_SHIP\_DOMAIN\_INTEGRATION\_CREDENTIAL\_REFUSED](/docs/errors/err_ngrok_ship_domain_integration_credential_refused) | The `PROVIDER` integration is not connected or its credential was revoked. Reconnect it in the dashboard. | | [ERR\_NGROK\_SHIP\_DOMAIN\_MANAGED\_CERT\_REQUIRED](/docs/errors/err_ngrok_ship_domain_managed_cert_required) | This domain does not use a managed certificate. | | [ERR\_NGROK\_SHIP\_DOMAIN\_NAME\_HOST\_REQUIRED](/docs/errors/err_ngrok_ship_domain_name_host_required) | Domain name has no hostname. ngrok reads only the host part of a pasted URL. | | [ERR\_NGROK\_SHIP\_DOMAIN\_NAME\_REQUIRED](/docs/errors/err_ngrok_ship_domain_name_required) | Domain name must not be empty. | | [ERR\_NGROK\_SHIP\_DOMAIN\_PROVIDER\_ACCESS\_DENIED](/docs/errors/err_ngrok_ship_domain_provider_access_denied) | `PROVIDER` refused the integration's credential for `ZONE` (HTTP `STATUS`). Check the API key's permissions. | | [ERR\_NGROK\_SHIP\_DOMAIN\_PROVIDER\_RATE\_LIMITED](/docs/errors/err_ngrok_ship_domain_provider_rate_limited) | `PROVIDER` is rate limiting DNS API calls for `ZONE`. | | [ERR\_NGROK\_SHIP\_DOMAIN\_PROVIDER\_ZONE\_NOT\_FOUND](/docs/errors/err_ngrok_ship_domain_provider_zone_not_found) | `PROVIDER` does not have `ZONE`, or its API access is turned off for that domain. | | [ERR\_NGROK\_SHIP\_DOMAIN\_ZONE\_NOT\_FOUND](/docs/errors/err_ngrok_ship_domain_zone_not_found) | We could not find the DNS zone for this domain. | | [ERR\_NGROK\_SHIP\_DOMAIN\_ZONE\_READ\_UNAVAILABLE](/docs/errors/err_ngrok_ship_domain_zone_read_unavailable) | We could not read `ZONE` at `PROVIDER`: `REASON` | | [ERR\_NGROK\_SHIP\_ENDPOINT\_TEMPLATE\_NAME\_BAD\_START](/docs/errors/err_ngrok_ship_endpoint_template_name_bad_start) | Endpoint template name `NAME` must start with a lowercase letter. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_TEMPLATE\_NAME\_DUPLICATED](/docs/errors/err_ngrok_ship_endpoint_template_name_duplicated) | Endpoint template name `NAME` is used more than once. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_TEMPLATE\_NAME\_ILLEGAL\_CHARACTER](/docs/errors/err_ngrok_ship_endpoint_template_name_illegal_character) | Endpoint template name `NAME` contains the illegal character `CHAR`. Allowed: lowercase letters, digits, and '\_'. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_TEMPLATE\_NAME\_REQUIRED](/docs/errors/err_ngrok_ship_endpoint_template_name_required) | The 'endpoint\_templates\[`INDEX`].name' field must not be empty. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_TEMPLATE\_NAME\_TOO\_LONG](/docs/errors/err_ngrok_ship_endpoint_template_name_too_long) | The 'endpoint\_templates\[`INDEX`].name' field is too long. Max 63 bytes, got `LENGTH` bytes. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_TEMPLATE\_PRIMARY\_REQUIRED](/docs/errors/err_ngrok_ship_endpoint_template_primary_required) | The 'endpoint\_templates' list must include a template named 'primary'. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_URL\_DUPLICATED](/docs/errors/err_ngrok_ship_endpoint_url_duplicated) | Endpoint URL `URL` is used more than once in 'endpoint\_templates'. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_URL\_HOST\_REQUIRED](/docs/errors/err_ngrok_ship_endpoint_url_host_required) | The `FIELD` value `VALUE` has no hostname. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_URL\_INVALID](/docs/errors/err_ngrok_ship_endpoint_url_invalid) | The `FIELD` value is not a valid URL: `REASON` | | [ERR\_NGROK\_SHIP\_ENDPOINT\_URL\_PORT\_REQUIRED](/docs/errors/err_ngrok_ship_endpoint_url_port_required) | The `FIELD` value `VALUE` must include a port for the `SCHEME` scheme. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_URL\_PRIVATE\_HOSTNAME\_LABEL\_LIMIT\_EXCEEDED](/docs/errors/err_ngrok_ship_endpoint_url_private_hostname_label_limit_exceeded) | The `FIELD` hostname `HOST` has too many labels. Max `LIMIT` under this private suffix, got `GOT`. Use hyphens instead of dots. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_URL\_PRIVATE\_HOSTNAME\_NAME\_REQUIRED](/docs/errors/err_ngrok_ship_endpoint_url_private_hostname_name_required) | The `FIELD` hostname `HOST` has no name in front of its private suffix. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_URL\_REQUIRED](/docs/errors/err_ngrok_ship_endpoint_url_required) | The `FIELD` field must not be empty. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_URL\_SCHEME\_REQUIRED](/docs/errors/err_ngrok_ship_endpoint_url_scheme_required) | The `FIELD` value `VALUE` has no scheme. Allowed: https\://, tls\://, or tcp\://. | | [ERR\_NGROK\_SHIP\_ENDPOINT\_URL\_SCHEME\_UNSUPPORTED](/docs/errors/err_ngrok_ship_endpoint_url_scheme_unsupported) | The `FIELD` value `VALUE` uses the unsupported scheme `SCHEME`. Allowed: https\://, tls\://, or tcp\://. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_ARCHIVED\_ENABLE\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_environment_archived_enable_not_allowed) | Environment `ID` is archived. Unarchive it before you enable it. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_CONTINUOUS\_DELIVERY\_NOT\_CONFIGURED](/docs/errors/err_ngrok_ship_environment_continuous_delivery_not_configured) | Environment `ID` has no continuous delivery, so it cannot be rolled back. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_NAME\_ALREADY\_EXISTS](/docs/errors/err_ngrok_ship_environment_name_already_exists) | An environment named "`NAME`" already exists on this app. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_NAME\_BAD\_START\_OR\_END](/docs/errors/err_ngrok_ship_environment_name_bad_start_or_end) | Environment name `NAME` must not start or end with `CHAR`. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_NAME\_ILLEGAL\_CHARACTER](/docs/errors/err_ngrok_ship_environment_name_illegal_character) | Environment name `NAME` contains the illegal character `CHAR`. Allowed: lowercase letters, digits, '-', and '\_'. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_NAME\_REQUIRED](/docs/errors/err_ngrok_ship_environment_name_required) | Environment name must not be empty. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_NAME\_TOO\_LONG](/docs/errors/err_ngrok_ship_environment_name_too_long) | Environment name is too long. Max 255 bytes, got `LENGTH` bytes. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_NOT\_FOUND](/docs/errors/err_ngrok_ship_environment_not_found) | No environment with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_POOL\_IDS\_REQUIRED](/docs/errors/err_ngrok_ship_environment_pool_ids_required) | The 'pool\_ids' list must include at least one pool. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_POOL\_ID\_INVALID](/docs/errors/err_ngrok_ship_environment_pool_id_invalid) | The 'pool\_ids' entry `VALUE` is not a pool ID: `REASON` | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_POOL\_ID\_NOT\_FOUND](/docs/errors/err_ngrok_ship_environment_pool_id_not_found) | No pool with the ID `ID` exists on this account. Check 'pool\_ids'. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_PREVIEW\_NAME\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_environment_preview_name_not_allowed) | Environment `ID` is a pull request preview. Its name is fixed to the pull request number. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_ROLLOUT\_IN\_PROGRESS](/docs/errors/err_ngrok_ship_environment_rollout_in_progress) | Environment `ID` already has a rollout in progress. Wait for it to finish. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_STATE\_CONFLICT](/docs/errors/err_ngrok_ship_environment_state_conflict) | Environment `ID` is `STATE`. Only an enabled environment can be `ACTION`. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_VAR\_KEY\_BAD\_START](/docs/errors/err_ngrok_ship_environment_var_key_bad_start) | Environment variable `KEY` must not start with a digit. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_VAR\_KEY\_ILLEGAL\_CHARACTER](/docs/errors/err_ngrok_ship_environment_var_key_illegal_character) | Environment variable `KEY` contains the illegal character `CHAR`. Allowed: letters, digits, and '\_'. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_VAR\_KEY\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_environment_var_key_not_allowed) | Environment variable `KEY` is reserved. ngrok sets 'PORT' and the 'PORT\_' variables from the endpoint templates. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_VAR\_KEY\_REQUIRED](/docs/errors/err_ngrok_ship_environment_var_key_required) | Environment variable names must not be empty. | | [ERR\_NGROK\_SHIP\_ENVIRONMENT\_VAR\_VALUE\_EXPRESSION\_INVALID](/docs/errors/err_ngrok_ship_environment_var_value_expression_invalid) | Environment variable `KEY` has an invalid expression: `REASON` | | [ERR\_NGROK\_SHIP\_GATEWAY\_CONFIG\_INVALID](/docs/errors/err_ngrok_ship_gateway_config_invalid) | The gateway configuration is invalid: `REASON` | | [ERR\_NGROK\_SHIP\_GATEWAY\_CONFIG\_REQUIRED](/docs/errors/err_ngrok_ship_gateway_config_required) | The `FIELD` field is required. | | [ERR\_NGROK\_SHIP\_GATEWAY\_CONFIG\_UNSUPPORTED](/docs/errors/err_ngrok_ship_gateway_config_unsupported) | A `SCHEME` endpoint can't use these gateway settings: `SETTINGS`. Remove them, or use an https\:// URL. | | [ERR\_NGROK\_SHIP\_GITHUB\_AUTHORIZATION\_REQUIRED](/docs/errors/err_ngrok_ship_github_authorization_required) | Your GitHub authorization has expired or was revoked. Reconnect GitHub. | | [ERR\_NGROK\_SHIP\_GITHUB\_INSTALLATION\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_github_installation_not_allowed) | Your GitHub user cannot see GitHub installation `INSTALLATIONID`. Configure the ngrok GitHub app's access, or reconnect GitHub. | | [ERR\_NGROK\_SHIP\_GITHUB\_RATE\_LIMITED](/docs/errors/err_ngrok_ship_github_rate_limited) | GitHub is rate limiting requests for repository `REPO` (HTTP `STATUS`). | | [ERR\_NGROK\_SHIP\_GITHUB\_READ\_UNAVAILABLE](/docs/errors/err_ngrok_ship_github_read_unavailable) | GitHub did not return `RESOURCE`: `REASON` | | [ERR\_NGROK\_SHIP\_GITHUB\_REPOSITORY\_ACCESS\_REFUSED](/docs/errors/err_ngrok_ship_github_repository_access_refused) | GitHub refused the ngrok GitHub app's request for repository `REPO` (HTTP 403). Check the app's permissions and repository access. | | [ERR\_NGROK\_SHIP\_GITHUB\_REPOSITORY\_CONFLICT](/docs/errors/err_ngrok_ship_github_repository_conflict) | GitHub reported a conflict for repository `REPO`: `REASON` | | [ERR\_NGROK\_SHIP\_GITHUB\_REPOSITORY\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_github_repository_not_allowed) | Your GitHub user cannot see repository `REPOID` through installation `INSTALLATIONID`. | | [ERR\_NGROK\_SHIP\_GITHUB\_REPOSITORY\_NOT\_FOUND](/docs/errors/err_ngrok_ship_github_repository_not_found) | Repository `REPO` was not found on GitHub, or the ngrok GitHub app has no access to it. | | [ERR\_NGROK\_SHIP\_GITHUB\_REPOSITORY\_SELECTION\_REQUIRED](/docs/errors/err_ngrok_ship_github_repository_selection_required) | The 'installation\_id' and 'repo\_id' fields must not be 0. | | [ERR\_NGROK\_SHIP\_GITHUB\_UNAVAILABLE](/docs/errors/err_ngrok_ship_github_unavailable) | GitHub did not answer a request for repository `REPO` (HTTP `STATUS`). | | [ERR\_NGROK\_SHIP\_INTEGRATION\_AUTHORIZATION\_REQUIRED](/docs/errors/err_ngrok_ship_integration_authorization_required) | Integration `ID` has not been authorized at the provider yet. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_AWS\_ROLE\_ARN\_REQUIRED](/docs/errors/err_ngrok_ship_integration_aws_role_arn_required) | The 'aws.role\_arn' field must not be empty. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_CLIENT\_CREDENTIALS\_REQUIRED](/docs/errors/err_ngrok_ship_integration_client_credentials_required) | The 'client\_credentials.client\_id' and 'client\_credentials.client\_secret' fields must not be empty. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_CLIENT\_ID\_REQUIRED](/docs/errors/err_ngrok_ship_integration_client_id_required) | The 'client\_credentials\_reference.client\_id' field must not be empty. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_CREDENTIAL\_FIELD\_UNSUPPORTED](/docs/errors/err_ngrok_ship_integration_credential_field_unsupported) | Provider `PROVIDERID` has no credential field `NAME`. Allowed: `FIELDS`. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_CREDENTIAL\_INPUT\_UNSUPPORTED](/docs/errors/err_ngrok_ship_integration_credential_input_unsupported) | Provider `PROVIDERID` does not take a `INPUT` credential. It takes `EXPECTED`. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_CREDENTIAL\_REFERENCE\_UNSUPPORTED](/docs/errors/err_ngrok_ship_integration_credential_reference_unsupported) | Provider `PROVIDERID` needs `COUNT` credential values (`FIELDS`). A 'static\_token\_reference' holds one. Send them in 'static\_token\_value'. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_CREDENTIAL\_REQUIRED](/docs/errors/err_ngrok_ship_integration_credential_required) | Provider `PROVIDERID` needs a credential. Send `EXPECTED`. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_CREDENTIAL\_VALUE\_DUPLICATED](/docs/errors/err_ngrok_ship_integration_credential_value_duplicated) | Credential value `NAME` is sent more than once. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_CREDENTIAL\_VALUE\_REQUIRED](/docs/errors/err_ngrok_ship_integration_credential_value_required) | Credential value `NAME` must not be empty. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_ENABLED\_REQUIRED](/docs/errors/err_ngrok_ship_integration_enabled_required) | Integration `ID` is disabled. Enable it first. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_MEMBER\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_integration_member_not_allowed) | Integration `ID` belongs to another member of this account. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_NOT\_FOUND](/docs/errors/err_ngrok_ship_integration_not_found) | No integration with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_PROVIDER\_ID\_UNSUPPORTED](/docs/errors/err_ngrok_ship_integration_provider_id_unsupported) | The 'provider\_id' value `PROVIDERID` is not a provider ngrok supports in this environment. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_PROVIDER\_NOT\_FOUND](/docs/errors/err_ngrok_ship_integration_provider_not_found) | No integration provider with the ID `PROVIDERID` exists. | | [ERR\_NGROK\_SHIP\_INTEGRATION\_REVOCATION\_REFUSED](/docs/errors/err_ngrok_ship_integration_revocation_refused) | `PROVIDER` refused to revoke the authorization (HTTP `STATUS`). | | [ERR\_NGROK\_SHIP\_INTEGRATION\_REVOCATION\_UNAVAILABLE](/docs/errors/err_ngrok_ship_integration_revocation_unavailable) | `PROVIDER` did not answer the revocation request: `REASON` | | [ERR\_NGROK\_SHIP\_INTEGRATION\_REVOCATION\_UNSUPPORTED](/docs/errors/err_ngrok_ship_integration_revocation_unsupported) | ngrok cannot revoke a `PROVIDERID` credential at the provider. Revoke it in the provider console, then delete the integration. | | [ERR\_NGROK\_SHIP\_MEMORY\_MIB\_OUT\_OF\_RANGE](/docs/errors/err_ngrok_ship_memory_mib_out_of_range) | The 'memory\_mib' field must be 0 or more, got `VALUE`. 0 uses the default. | | [ERR\_NGROK\_SHIP\_POOL\_AWS\_REGION\_UNSUPPORTED](/docs/errors/err_ngrok_ship_pool_aws_region_unsupported) | The 'region' value `REGION` is not an AWS region code like 'us-east-1'. | | [ERR\_NGROK\_SHIP\_POOL\_CLUSTER\_NAME\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_pool_cluster_name_not_allowed) | The 'cluster\_name' field must be empty for an 'aws\_ecs\_managed' pool. ngrok names the cluster after the pool ID. | | [ERR\_NGROK\_SHIP\_POOL\_CLUSTER\_NOT\_FOUND](/docs/errors/err_ngrok_ship_pool_cluster_not_found) | The pool's Kubernetes cluster no longer exists at `PROVIDER`. | | [ERR\_NGROK\_SHIP\_POOL\_COREWEAVE\_NODE\_POOL\_COUNT\_OUT\_OF\_RANGE](/docs/errors/err_ngrok_ship_pool_coreweave_node_pool_count_out_of_range) | The 'node\_pool.count' field must be at least 1, got `COUNT`. | | [ERR\_NGROK\_SHIP\_POOL\_DETAILS\_FIELD\_REQUIRED](/docs/errors/err_ngrok_ship_pool_details_field_required) | The `FIELD` field must not be empty. | | [ERR\_NGROK\_SHIP\_POOL\_DETAILS\_LIST\_REQUIRED](/docs/errors/err_ngrok_ship_pool_details_list_required) | The `FIELD` list must have at least one entry. | | [ERR\_NGROK\_SHIP\_POOL\_DETAILS\_REQUIRED](/docs/errors/err_ngrok_ship_pool_details_required) | A `KIND` pool needs `KIND` details. The request has none, or has details for another kind. | | [ERR\_NGROK\_SHIP\_POOL\_ECS\_CLUSTER\_NAME\_ILLEGAL\_CHARACTER](/docs/errors/err_ngrok_ship_pool_ecs_cluster_name_illegal_character) | The 'cluster\_name' value `NAME` contains the illegal character `CHAR`. Allowed: letters, digits, '-', and '\_'. | | [ERR\_NGROK\_SHIP\_POOL\_ECS\_CLUSTER\_NAME\_TOO\_LONG](/docs/errors/err_ngrok_ship_pool_ecs_cluster_name_too_long) | The 'cluster\_name' field is too long. Max 255 bytes, got `LENGTH` bytes. | | [ERR\_NGROK\_SHIP\_POOL\_EXECUTION\_ROLE\_ARN\_INVALID](/docs/errors/err_ngrok_ship_pool_execution_role_arn_invalid) | The 'execution\_role\_arn' value is not an IAM role ARN: `REASON` | | [ERR\_NGROK\_SHIP\_POOL\_EXECUTION\_ROLE\_NAME\_BAD\_START](/docs/errors/err_ngrok_ship_pool_execution_role_name_bad_start) | The 'execution\_role\_arn' role name `NAME` must start with 'ngrok-ship-'. The recommended permissions policy grants iam:PassRole only on that prefix. | | [ERR\_NGROK\_SHIP\_POOL\_INTEGRATION\_AUTHORIZATION\_REQUIRED](/docs/errors/err_ngrok_ship_pool_integration_authorization_required) | Integration `ID` is not authorized yet. Finish its setup before you create a pool. | | [ERR\_NGROK\_SHIP\_POOL\_INTEGRATION\_CREDENTIAL\_REFUSED](/docs/errors/err_ngrok_ship_pool_integration_credential_refused) | `PROVIDER` did not accept the integration's credential. Reconnect the integration. | | [ERR\_NGROK\_SHIP\_POOL\_INTEGRATION\_ENABLED\_REQUIRED](/docs/errors/err_ngrok_ship_pool_integration_enabled_required) | Integration `ID` is disabled. Enable it before you create a pool. | | [ERR\_NGROK\_SHIP\_POOL\_INTEGRATION\_ID\_REQUIRED](/docs/errors/err_ngrok_ship_pool_integration_id_required) | The 'integration\_id' field must not be empty. A `KIND` pool is provisioned through an integration. | | [ERR\_NGROK\_SHIP\_POOL\_INTEGRATION\_PROVIDER\_CONFLICT](/docs/errors/err_ngrok_ship_pool_integration_provider_conflict) | Integration `ID` is for `PROVIDER`. A `KIND` pool needs a `WANTED` integration. | | [ERR\_NGROK\_SHIP\_POOL\_INTEGRATION\_UNSUPPORTED](/docs/errors/err_ngrok_ship_pool_integration_unsupported) | Pool kind `KIND` does not take an 'integration\_id'. | | [ERR\_NGROK\_SHIP\_POOL\_JOIN\_KEY\_ALREADY\_EXISTS](/docs/errors/err_ngrok_ship_pool_join_key_already_exists) | A pool with this join key already exists on this account. | | [ERR\_NGROK\_SHIP\_POOL\_JOIN\_KEY\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_pool_join_key_not_allowed) | The 'join\_key' field must not start with 'cp\_'. That prefix is reserved for pool IDs. | | [ERR\_NGROK\_SHIP\_POOL\_KIND\_NOT\_CONFIGURED](/docs/errors/err_ngrok_ship_pool_kind_not_configured) | Pool kind `KIND` is not available in this environment. | | [ERR\_NGROK\_SHIP\_POOL\_KIND\_UNSUPPORTED](/docs/errors/err_ngrok_ship_pool_kind_unsupported) | Pool kind `KIND` is not supported. | | [ERR\_NGROK\_SHIP\_POOL\_LAMBDA\_SSH\_KEY\_CREATE\_UNAVAILABLE](/docs/errors/err_ngrok_ship_pool_lambda_ssh_key_create_unavailable) | Lambda Cloud did not answer the request to create the SSH key: `REASON` | | [ERR\_NGROK\_SHIP\_POOL\_LAMBDA\_SSH\_KEY\_NAME\_REQUIRED](/docs/errors/err_ngrok_ship_pool_lambda_ssh_key_name_required) | SSH key name must not be empty. | | [ERR\_NGROK\_SHIP\_POOL\_LAMBDA\_SSH\_KEY\_REFUSED](/docs/errors/err_ngrok_ship_pool_lambda_ssh_key_refused) | Lambda Cloud refused the SSH key (HTTP `STATUS`): `REASON` | | [ERR\_NGROK\_SHIP\_POOL\_NODE\_POOL\_COUNT\_OUT\_OF\_RANGE](/docs/errors/err_ngrok_ship_pool_node_pool_count_out_of_range) | The 'node\_pools\[`INDEX`].count' field must be 1 to 10, got `COUNT`. | | [ERR\_NGROK\_SHIP\_POOL\_NODE\_POOL\_FIELD\_REQUIRED](/docs/errors/err_ngrok_ship_pool_node_pool_field_required) | The 'node\_pools\[`INDEX`].`FIELD` field must not be empty. | | [ERR\_NGROK\_SHIP\_POOL\_NODE\_POOL\_OS\_ID\_ILLEGAL\_CHARACTER](/docs/errors/err_ngrok_ship_pool_node_pool_os_id_illegal_character) | The 'node\_pools\[`INDEX`].os\_id' value `VALUE` must be a whole number. | | [ERR\_NGROK\_SHIP\_POOL\_NODE\_POOL\_ZONE\_CONFLICT](/docs/errors/err_ngrok_ship_pool_node_pool_zone_conflict) | The 'node\_pools\[`INDEX`].zone' value `ZONE` is not in region `REGION`. | | [ERR\_NGROK\_SHIP\_POOL\_NOT\_FOUND](/docs/errors/err_ngrok_ship_pool_not_found) | No compute pool with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_POOL\_PROVIDER\_READ\_UNAVAILABLE](/docs/errors/err_ngrok_ship_pool_provider_read_unavailable) | `PROVIDER` did not answer the request to `OPERATION`: `REASON` | | [ERR\_NGROK\_SHIP\_POOL\_RUNPOD\_COMPUTE\_TYPE\_UNSUPPORTED](/docs/errors/err_ngrok_ship_pool_runpod_compute_type_unsupported) | The 'compute\_type' value `VALUE` is not supported. Allowed: 'GPU' or 'CPU'. | | [ERR\_NGROK\_SHIP\_POOL\_RUNPOD\_CPU\_FLAVOR\_NOT\_FOUND](/docs/errors/err_ngrok_ship_pool_runpod_cpu_flavor_not_found) | RunPod has no CPU flavor with the ID `ID`. | | [ERR\_NGROK\_SHIP\_POOL\_RUNPOD\_DATA\_CENTER\_NOT\_FOUND](/docs/errors/err_ngrok_ship_pool_runpod_data_center_not_found) | RunPod has no data center with the ID `ID`. | | [ERR\_NGROK\_SHIP\_POOL\_RUNPOD\_GPU\_TYPE\_NOT\_FOUND](/docs/errors/err_ngrok_ship_pool_runpod_gpu_type_not_found) | RunPod has no GPU type with the ID `ID`. | | [ERR\_NGROK\_SHIP\_POOL\_SCALEWAY\_REGION\_UNSUPPORTED](/docs/errors/err_ngrok_ship_pool_scaleway_region_unsupported) | The 'region' value `REGION` is not a Scaleway region. Allowed: `REGIONS`. | | [ERR\_NGROK\_SHIP\_POOL\_SCALEWAY\_ZONE\_UNSUPPORTED](/docs/errors/err_ngrok_ship_pool_scaleway_zone_unsupported) | The 'zone' value `ZONE` is not a Scaleway instance zone. Allowed: `ZONES`. | | [ERR\_NGROK\_SHIP\_PORT\_OUT\_OF\_RANGE](/docs/errors/err_ngrok_ship_port_out_of_range) | The 'port' field must be 1 to 65535, got `PORT`. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_APP\_CONFLICT](/docs/errors/err_ngrok_ship_publish_target_app_conflict) | Publish target `ID` is used by `COUNT` apps: `NAMES`. Point them at another target first. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_BUILTIN\_DELETE\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_publish_target_builtin_delete_not_allowed) | The built-in publish target cannot be deleted. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_DOCKER\_HUB\_LOGIN\_REFUSED](/docs/errors/err_ngrok_ship_publish_target_docker_hub_login_refused) | Docker Hub rejected the push credential of publish target `ID` (HTTP `STATUS`). | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_DOCKER\_HUB\_REPOSITORY\_REFUSED](/docs/errors/err_ngrok_ship_publish_target_docker_hub_repository_refused) | Docker Hub refused to create repository `REPOSITORY` (HTTP `STATUS`). Check that the push credential can write to namespace `NAMESPACE`. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_DOCKER\_HUB\_UNAVAILABLE](/docs/errors/err_ngrok_ship_publish_target_docker_hub_unavailable) | Docker Hub did not answer while ngrok prepared repository `REPOSITORY`: `REASON` | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_HOST\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_publish_target_host_not_allowed) | The 'host' field must be empty for a builtin publish target. ngrok manages its host. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_HOST\_UNSUPPORTED](/docs/errors/err_ngrok_ship_publish_target_host_unsupported) | A `KIND` publish target always publishes to `FIXEDHOST`. The 'host' value `HOST` is not allowed. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_KIND\_UNSUPPORTED](/docs/errors/err_ngrok_ship_publish_target_kind_unsupported) | The 'kind' value `KIND` is not supported. Allowed: 'builtin', 'ghcr', 'dockerhub', and 'custom'. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_NOT\_FOUND](/docs/errors/err_ngrok_ship_publish_target_not_found) | No publish target with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_PUSH\_CREDENTIAL\_NOT\_FOUND](/docs/errors/err_ngrok_ship_publish_target_push_credential_not_found) | No registry credential with the ID `ID` exists on this account. Check 'push\_credential\_id'. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_PUSH\_CREDENTIAL\_REQUIRED](/docs/errors/err_ngrok_ship_publish_target_push_credential_required) | A `KIND` publish target needs a 'push\_credential\_id'. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_REPOSITORY\_PREFIX\_BAD\_START](/docs/errors/err_ngrok_ship_publish_target_repository_prefix_bad_start) | Docker Hub namespace `PREFIX` must start with a lowercase letter or digit. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_REPOSITORY\_PREFIX\_ILLEGAL\_CHARACTER](/docs/errors/err_ngrok_ship_publish_target_repository_prefix_illegal_character) | Docker Hub namespace `PREFIX` contains the illegal character `CHAR`. Allowed: lowercase letters, digits, '-', and '\_'. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_REPOSITORY\_PREFIX\_LENGTH\_OUT\_OF\_RANGE](/docs/errors/err_ngrok_ship_publish_target_repository_prefix_length_out_of_range) | Docker Hub namespace must be 2 to 255 bytes, got `LENGTH` bytes. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_REPOSITORY\_PREFIX\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_publish_target_repository_prefix_not_allowed) | The 'repository\_prefix' field must be empty for a `KIND` publish target. ngrok computes the repository name. | | [ERR\_NGROK\_SHIP\_PUBLISH\_TARGET\_REPOSITORY\_PREFIX\_REQUIRED](/docs/errors/err_ngrok_ship_publish_target_repository_prefix_required) | The 'repository\_prefix' field must not be empty for a `KIND` publish target. | | [ERR\_NGROK\_SHIP\_PULL\_REQUEST\_TEMPLATE\_NOT\_FOUND](/docs/errors/err_ngrok_ship_pull_request_template_not_found) | No pull request template with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_REGISTRY\_CREDENTIAL\_ALREADY\_EXISTS](/docs/errors/err_ngrok_ship_registry_credential_already_exists) | A credential for `USERNAME` at `HOST` already exists on this account: `ID`. Update it to rotate the password. | | [ERR\_NGROK\_SHIP\_REGISTRY\_CREDENTIAL\_APP\_CONFLICT](/docs/errors/err_ngrok_ship_registry_credential_app_conflict) | Credential `ID` is the pull credential of `COUNT` apps: `NAMES`. Point them at another credential first. | | [ERR\_NGROK\_SHIP\_REGISTRY\_CREDENTIAL\_NOT\_FOUND](/docs/errors/err_ngrok_ship_registry_credential_not_found) | No registry credential with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_REGISTRY\_CREDENTIAL\_PASSWORD\_REQUIRED](/docs/errors/err_ngrok_ship_registry_credential_password_required) | The 'password' field must not be empty. | | [ERR\_NGROK\_SHIP\_REGISTRY\_CREDENTIAL\_PUBLISH\_TARGET\_CONFLICT](/docs/errors/err_ngrok_ship_registry_credential_publish_target_conflict) | Credential `ID` is the push credential of `COUNT` publish targets. Point them at another credential first. | | [ERR\_NGROK\_SHIP\_REGISTRY\_CREDENTIAL\_REFUSED](/docs/errors/err_ngrok_ship_registry_credential_refused) | Registry `HOST` rejected the credential for user `USERNAME`. | | [ERR\_NGROK\_SHIP\_REGISTRY\_CREDENTIAL\_USERNAME\_REQUIRED](/docs/errors/err_ngrok_ship_registry_credential_username_required) | The 'username' field must not be empty. | | [ERR\_NGROK\_SHIP\_REGISTRY\_HOST\_ILLEGAL\_CHARACTER](/docs/errors/err_ngrok_ship_registry_host_illegal_character) | The 'host' value `HOST` contains the illegal character `CHAR`. Use a hostname with an optional port, and no scheme or path. | | [ERR\_NGROK\_SHIP\_REGISTRY\_HOST\_INVALID](/docs/errors/err_ngrok_ship_registry_host_invalid) | The 'host' value `HOST` is not a registry host: `REASON` | | [ERR\_NGROK\_SHIP\_REGISTRY\_HOST\_REQUIRED](/docs/errors/err_ngrok_ship_registry_host_required) | The 'host' field must not be empty. | | [ERR\_NGROK\_SHIP\_RELEASE\_FEED\_CURSOR\_INVALID](/docs/errors/err_ngrok_ship_release_feed_cursor_invalid) | The 'cursor' value is not a cursor this feed returned: `REASON` | | [ERR\_NGROK\_SHIP\_RELEASE\_NOT\_FOUND](/docs/errors/err_ngrok_ship_release_not_found) | No release with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_REPLICA\_COUNT\_OUT\_OF\_RANGE](/docs/errors/err_ngrok_ship_replica_count_out_of_range) | The 'replica\_count' field must be at least 1, got `COUNT`. | | [ERR\_NGROK\_SHIP\_REPLICA\_ID\_REQUIRED](/docs/errors/err_ngrok_ship_replica_id_required) | The 'replica\_id' field is required. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_AWS\_ACCESS\_DENIED](/docs/errors/err_ngrok_ship_replica_logs_aws_access_denied) | The AWS integration role is not permitted to read replica logs. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_AWS\_UNAVAILABLE](/docs/errors/err_ngrok_ship_replica_logs_aws_unavailable) | AWS did not answer the replica log read: `REASON` | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_CLOUDWATCH\_LOG\_GROUP\_UNAVAILABLE](/docs/errors/err_ngrok_ship_replica_logs_cloudwatch_log_group_unavailable) | The replica's CloudWatch log group is unavailable. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_CURSOR\_INVALID](/docs/errors/err_ngrok_ship_replica_logs_cursor_invalid) | `REASON` | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_CURSOR\_REQUIRED](/docs/errors/err_ngrok_ship_replica_logs_cursor_required) | The 'cursor' field must not be empty when it is the selected position. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_ECS\_DEPLOYMENT\_UNAVAILABLE](/docs/errors/err_ngrok_ship_replica_logs_ecs_deployment_unavailable) | The replica's current ECS deployment is not available yet. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_INTEGRATION\_CREDENTIAL\_REFUSED](/docs/errors/err_ngrok_ship_replica_logs_integration_credential_refused) | `PROVIDER` did not accept the integration's credential, so replica logs cannot be read. Reconnect the integration. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_KUBERNETES\_POD\_UNAVAILABLE](/docs/errors/err_ngrok_ship_replica_logs_kubernetes_pod_unavailable) | The replica's current Kubernetes Pod is not available yet. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_KUBERNETES\_RBAC\_DENIED](/docs/errors/err_ngrok_ship_replica_logs_kubernetes_rbac_denied) | Kubernetes RBAC does not permit reading replica Pod logs. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_KUBERNETES\_UNAVAILABLE](/docs/errors/err_ngrok_ship_replica_logs_kubernetes_unavailable) | The replica's Kubernetes cluster did not answer the log read: `REASON` | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_LIMIT\_OUT\_OF\_RANGE](/docs/errors/err_ngrok_ship_replica_logs_limit_out_of_range) | The 'limit' field must be 1 to `MAX`, got `LIMIT`. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_PLACEMENT\_REQUIRED](/docs/errors/err_ngrok_ship_replica_logs_placement_required) | The replica has not been placed on a compute pool yet, so it has no logs. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_PREVIOUS\_CONTAINER\_NOT\_FOUND](/docs/errors/err_ngrok_ship_replica_logs_previous_container_not_found) | The replica has no terminated container to read logs from. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_REMOTE\_KUBERNETES\_UNSUPPORTED](/docs/errors/err_ngrok_ship_replica_logs_remote_kubernetes_unsupported) | The runner does not provide access to remote Kubernetes logs. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_REPLICA\_GONE](/docs/errors/err_ngrok_ship_replica_logs_replica_gone) | The replica's current `RESOURCE` no longer exists. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_RUNNER\_ACCESS\_UNAVAILABLE](/docs/errors/err_ngrok_ship_replica_logs_runner_access_unavailable) | The replica's runner has not reported its Kubernetes access as ready. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_RUNPOD\_ACCESS\_DENIED](/docs/errors/err_ngrok_ship_replica_logs_runpod_access_denied) | The RunPod integration is not permitted to read replica logs. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_RUNPOD\_RATE\_LIMITED](/docs/errors/err_ngrok_ship_replica_logs_runpod_rate_limited) | RunPod is temporarily rate limiting replica log reads. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_RUNPOD\_RESOURCE\_CONFLICT](/docs/errors/err_ngrok_ship_replica_logs_runpod_resource_conflict) | More than one current RunPod resource matches this replica. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_RUNPOD\_UNAVAILABLE](/docs/errors/err_ngrok_ship_replica_logs_runpod_unavailable) | RunPod did not answer the replica log read: `REASON` | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_TAIL\_LINES\_LIMIT\_EXCEEDED](/docs/errors/err_ngrok_ship_replica_logs_tail_lines_limit_exceeded) | The 'tail\_lines' value `TAILLINES` must not exceed the 'limit' value `LIMIT`. | | [ERR\_NGROK\_SHIP\_REPLICA\_LOGS\_TAIL\_LINES\_OUT\_OF\_RANGE](/docs/errors/err_ngrok_ship_replica_logs_tail_lines_out_of_range) | The 'tail\_lines' field must be 0 to `MAX`, got `TAILLINES`. | | [ERR\_NGROK\_SHIP\_REPLICA\_NOT\_FOUND](/docs/errors/err_ngrok_ship_replica_not_found) | No replica with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_ROLLBACK\_DEPLOYMENT\_ID\_REQUIRED](/docs/errors/err_ngrok_ship_rollback_deployment_id_required) | The 'deployment\_id' field must not be empty. | | [ERR\_NGROK\_SHIP\_ROLLBACK\_DEPLOYMENT\_NOT\_FOUND](/docs/errors/err_ngrok_ship_rollback_deployment_not_found) | No deployment with the ID `ID` exists in this environment. | | [ERR\_NGROK\_SHIP\_ROLLBACK\_RELEASE\_NOT\_FOUND](/docs/errors/err_ngrok_ship_rollback_release_not_found) | Deployment `ID` has no release to roll back to. | | [ERR\_NGROK\_SHIP\_ROLLOUT\_NOT\_FOUND](/docs/errors/err_ngrok_ship_rollout_not_found) | No rollout with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_RUNNER\_NOT\_FOUND](/docs/errors/err_ngrok_ship_runner_not_found) | No runner with the ID `ID` exists on this account. | | [ERR\_NGROK\_SHIP\_SCHEDULING\_POLICY\_INVALID](/docs/errors/err_ngrok_ship_scheduling_policy_invalid) | The 'scheduling\_policy' expression fails to compile: `REASON` | | [ERR\_NGROK\_SHIP\_START\_COMMAND\_NOT\_ALLOWED](/docs/errors/err_ngrok_ship_start_command_not_allowed) | The 'start\_command' field is not supported for repository apps. Set the run command in the app's build settings. | | [ERR\_NGROK\_TRAFFIC\_INSPECT\_CONNECTION\_NOT\_FOUND](/docs/errors/err_ngrok_traffic_inspect_connection_not_found) | This connection was not found. Traffic Inspector keeps connections only for your plan's retention period. | # FAQ Source: https://ngrok.com/docs/faq/index Frequently asked questions about ngrok including status page, payment methods, security, and platform features. ## Does ngrok have an official status page? Yes, the official ngrok status page is [status.ngrok.com](https://status.ngrok.com). ## Why am I required to add a payment method to use TCP endpoints on the free tier? Will I be charged? In an effort to combat abuse on the platform, a valid payment method must be [added to your account](https://dashboard.ngrok.com/settings#id-verification) before you can use TCP endpoints on the free tier. This card will not be charged. If you remove this card from your account, you will lose access to TCP endpoints until you add another payment method. Valid payment methods include credit or debit cards. Prepaid cards may not be used to enable TCP endpoints. ## Is ngrok a virus? **No, ngrok is not a virus.** ngrok is beloved for its ease of use and powerful capability. Unfortunately, those same attributes also make it an attractive target for bad actors trying to phish credentials or create back doors into private networks. ngrok proactively monitors and bans any accounts identified that are involved with these attacks, and also works with 3rd parties that report malware and abuse via [abuse@ngrok.com](mailto:abuse@ngrok.com) and the [abuse APIs](/docs/api-reference/abusereports/create). Because ngrok is sometimes used by bad actors for attacks, it is occasionally flagged by some antivirus companies as malware or a potentially unwanted application (PAU). ngrok actively monitors and reaches out to these companies and attempts to obtain the correct classification in their system and be added to the allow lists. If you receive a notice from your antivirus software when installing ngrok software, be sure to verify the following: * Official binaries can be downloaded from the [ngrok Downloads](https://ngrok.com/download) page, and the binaries themselves are hosted at `bin.equinox.io`. If you installed ngrok from another source, you should be careful. * There are no open source versions of the ngrok Agent and the source code is not available. If you have downloaded anything claiming to be ngrok agent source code or built the agent from source, it is most likely malicious. * All binaries are signed using ngrok certificates. You can verify the integrity of the application by checking that the binary is signed by an ngrok LLC or ngrok, Inc. authority. See [these instructions from Microsoft](https://support.microsoft.com/en-us/office/view-digital-signature-and-certificate-details-76ba00cb-1e58-42aa-8717-0caee76bb3cf) for verifying the digital signatures in Windows binaries. ### Does ngrok own `bin.equinox.io`? Equinox is 100% owned by ngrok (if you visit equinox.io you'll see it redirect to [https://ngrok.com/downloads](https://ngrok.com/downloads)). ngrok has exclusive access to its source code and its operational infrastructure. No other software is distributed via equinox. ### How do I verify the ngrok binary is signed? You can get a binary hash for every ngrok executable distributed (both current and historical) from [https://dl.equinox.io/ngrok/ngrok-v3/stable/archive](https://dl.equinox.io/ngrok/ngrok-v3/stable/archive). All of those SHAs are correct. ### How does ngrok secure the distribution of binaries? When ngrok releases new versions of the ngrok agent via equinox, those releases are also signed in multiple ways: * Each MacOS and Windows release is signed with code-signing certificates that the operating system verifies. You can verify that those certificates state that ngrok is the publisher. * The released executables are also signed with a private key that only the release pipeline has access to that are used for ngrok's optional self-update mechanism. If you ask ngrok to update to a new version (via `ngrok update`) it cryptographically verifies the new version was signed with a trusted CA that is packaged into its executable. * Uploading new releases to equinox is done in the same CI/CD pipeline that also has isolated access to the authentication keys which are used to upload those executables to the equinox service. Here is an example verification flow for macOS using the 3.8.0 binary: ```bash theme={null} ❯ codesign -d -vvv ~/Downloads/ngrok Executable=/Users/username/Downloads/binaries/ngrok Identifier=darwin_amd64 Format=Mach-O thin (x86_64) CodeDirectory v=20500 size=209752 flags=0x10000(runtime) hashes=6549+2 location=embedded Hash type=sha256 size=32 CandidateCDHash sha256=9babe84803ac6c05874966cf0934c116b00b6b97 CandidateCDHashFull sha256=9babe84803ac6c05874966cf0934c116b00b6b97b1daa6ceb6362b8f2e9c63a2 Hash choices=sha256 CMSDigest=9babe84803ac6c05874966cf0934c116b00b6b97b1daa6ceb6362b8f2e9c63a2 CMSDigestType=2 CDHash=9babe84803ac6c05874966cf0934c116b00b6b97 Signature size=8963 Authority=Developer ID Application: ngrok LLC (TEX8MHRDQ9) Authority=Developer ID Certification Authority Authority=Apple Root CA Timestamp=Mar 14, 2024 at 3:18:31 PM Info.plist=not bound TeamIdentifier=TEX8MHRDQ9 Runtime Version=11.0.0 Sealed Resources=none Internal requirements count=1 size=172 ``` Most antivirus tools have an option to report false positive classifications for their software. You can usually find that link in the software itself or on their support site. [VirusTotal has a large list](https://docs.virustotal.com/docs/false-positive-contacts) of contacts for reporting false positives. Reporting ngrok is greatly appreciated—this helps ensure ngrok has the correct classification in their systems. ## Does ngrok have built-in protections, rate-limits, or safeguards that help prevent unexpected traffic? Yes. All ngrok customer's Endpoints and other resources are protected by our proprietary firewall. In addition, there are [additional measures](/docs/gateway/domains/ddos-protection#additional-measures) that can be taken to help protect your Endpoints against bad-actors. ## Can I choose a different port to receive traffic on my ngrok domains? No. ngrok accepts traffic on port 80 for HTTP endpoints, 443 for HTTPS endpoints and port 443 as well for TLS endpoints. This behavior is not configurable. TCP endpoints use the port of the TCP address that was assigned when you acquired the TCP address. [This port is assigned and not configurable](/docs/gateway/domains/tcp-addresses/#address-assignment). ## Will the ngrok Agent work if my network changes? Yes. The ngrok agent is robust to all network changes. If you switch Wi-Fi networks or your modem restarts and you have a dynamic IP that changes, the ngrok agent will automatically detect this condition and re-establish connectivity with the ngrok cloud service. You don't need to take any manual action to restart or reconfigure the agent. ## What information is stored about my use of ngrok? Please see the [Terms of Service](https://ngrok.com/tos) and [Data Processing Agreement](https://ngrok.com/dpa) for more information. ## Does ngrok support CORS? Yes, applications exposed via ngrok's HTTP endpoints will work with CORS. Your application must serve the appropriate CORS headers, or you may use ngrok's [response headers module](/docs/gateway/traffic-policy/actions/add-headers/) to add the appropriate CORS headers. Setting the correct CORS headers is non-trivial, but for very simple examples you may try: ``` ngrok http 80 --response-header-add "Access-Control-Allow-Origin: *" ``` If you use any of ngrok's authentication modules, CORS preflight options requests are blocked by default. You may override this behavior using [Traffic Policy](/docs/gateway/traffic-policy/) expressions to skip auth enforcement if the request is a CORS preflight request. Basic Auth (`--basic-auth`) does not yet support configuring options passthrough. ## What's the status of ngrok v1? The original open source ngrok agent 1.x is [available on GitHub](https://github.com/inconshreveable/ngrok) but is no longer developed, supported or maintained. The latest ngrok agent is available on the [ngrok downloads page](https://download.ngrok.com). ## How do you pronounce ngrok? *en-grok* ## How long has ngrok been around? The first prototype for ngrok was committed on [March 20th, 2013](https://github.com/inconshreveable/ngrok/commit/8f4795ecac7f92c6b5a8c8970c65f26e5315fe4e). ## Why is my vendor asking me to install ngrok for 'site-to-site connectivity'? Your vendor wants to create a secure persistent connection between your network and theirs, which allows them to access and take action on your services and data. For details about what ngrok is, how to install the ngrok agent, and how to work with your vendor to implement adequate security practices, see the [end customer installation guide](/docs/gateway/site-to-site-connectivity/end-customers/). # Agent CLI TLS Termination Source: https://ngrok.com/docs/gateway/agent/agent-tls-termination Terminate TLS at the ngrok Agent CLI rather than at ngrok's network, so traffic stays encrypted end to end all the way to your upstream service. Agent TLS termination enables you to secure your traffic with end-to-end encryption without needing to reconfigure your server. If your service doesn't support TLS termination, you can still use Agent TLS termination with [Zero-Knowledge TLS](/docs/gateway/domains/tls-termination/#end-to-end-encryption). The following instructions will guide you through setting up Agent TLS termination with ngrok. ## What you'll need * Ensure you have [openssl](https://docs.openiam.com/docs-4.2.1.3/appendix/2-openssl) installed. ## 1. Generate a cert and key pair The following command: 1. Generates a new certificate signing request (CSR) for a 4096-bit RSA key pair. * The key is saved to `your-key.key`. 2. Creates a self-signed certificate which: * Is in x509 format and uses the SHA-256 hash algorithm. * Is valid for 365 days. * Is saved to `your-cert.crt`. ```bash theme={null} openssl req -new -newkey rsa:4096 -x509 -sha256 -days 365 -noenc -out your-cert.crt -keyout your-key.key ``` ## 2. Configure your endpoint You can configure your endpoint with an [agent configuration file](/docs/gateway/agent/config/). To create a new configuration file with your generated cert and key pair, run the following command: ```bash theme={null} ngrok config add-authtoken you-authtoken ``` Your generated configuration file should resemble the following: ```yaml theme={null} version: 3 agent: authtoken: your-authtoken endpoints: - name: demo-tls url: tls://your-domain.ngrok.app upstream: url: 12345 agent_tls_termination: server_certificate: /path/to/your-cert.crt server_private_key: /path/to/your-key.key ``` You can run `ngrok config edit` to open the configuration file in your default text editor. [Learn more about the `ngrok config` command](/docs/gateway/agent/cli/#ngrok-config). ### Optional: require client certificates (mTLS) at the agent To make the agent authenticate clients as part of the TLS handshake, add one or more Certificate Authorities to the `agent_tls_termination` block with `mutual_tls_certificate_authorities`. The agent will reject any client whose certificate isn't signed by one of the listed CAs. ```yaml theme={null} version: 3 agent: authtoken: your-authtoken endpoints: - name: demo-tls url: tls://your-domain.ngrok.app upstream: url: 12345 agent_tls_termination: server_certificate: /path/to/your-cert.crt server_private_key: /path/to/your-key.key mutual_tls_certificate_authorities: - /path/to/your-ca-cert.crt ``` See [`agent_tls_termination.mutual_tls_certificate_authorities`](/docs/gateway/agent/config/v3#agent_tls_termination-mutual_tls_certificate_authorities) for the full reference. This is different from mTLS terminated at the ngrok edge via [the `terminate-tls` Traffic Policy action](/docs/gateway/domains/mtls-termination/). With agent-side mTLS, the client certificate is validated by your agent; traffic between the ngrok edge and your agent is still carried over the agent's secure TLS connection to ngrok, but client-certificate authentication is not performed on that hop. ## 3. Start your endpoint Next, use `ngrok start endpoint_name_here` in the terminal to start an endpoint using the settings in your agent configuration file, as shown below: ```bash theme={null} ngrok start demo-tls ``` ## 4. Start your upstream server Start an upstream server on the specified port (for example, `12345`) to handle incoming requests. The following example uses Python, but you can use any language or framework depending on your requirements. ```python theme={null} import socket host='127.0.0.1' port=12345 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((host, port)) print(f"Server started on {host}:{port}") server.listen(1) while True: client, client_address = server.accept() print(f"Connection established with {client_address}") client.sendall(b"hello, world!") client.close() print(f"Connection with {client_address} closed") ``` ## 5. Try connecting to your endpoint The following example uses `openssl s_client` to initiate an SSL/TLS client connection to your upstream server without a certificate. ```bash theme={null} openssl s_client -quiet -connect your-domain.ngrok.app:443 -verify_return_error ``` ```bash theme={null} Connecting to 2600:1f16:d83:1200::6e:0 depth=0 C=AU, ST=Some-State, O=Internet Widgets Pty Ltd, CN=your-domain.ngrok.app verify error:num=18:self-signed certificate 408FB6F801000000:error:0A000086:SSL routines:tls_post_process_server_certificate:certificate verify failed:ssl/statem/statem_clnt.c:2093: ``` The following example uses `openssl s_client` to initiate an SSL/TLS client connection to your upstream server with a certificate. ```bash theme={null} openssl s_client -quiet -connect your-domain.ngrok.app:443 -verify_return_error -CAfile your-cert.crt ``` ```bash theme={null} Connecting to 2600:1f16:d83:1200::6e:0 depth=0 C=AU, ST=Some-State, O=Internet Widgets Pty Ltd, CN=your-domain.ngrok.app verify return:1 hello, world!% ``` The extra arguments added to the command suppress most of the output so that only the data exchanged with the server will be displayed. # ngrok Agent CLI API Source: https://ngrok.com/docs/gateway/agent/api The ngrok Agent CLI exposes a local HTTP API for collecting status and metrics, capturing and replaying requests, and starting or stopping endpoints while it runs. ## Overview The ngrok agent exposes an HTTP API that enables you to: * Collect status and metrics information * Collect and replay captured requests * Start and stop endpoints dynamically If you need to programmatically control the ngrok agent, consider using one of the [Agent SDKs](/docs/agent-sdks/) instead of the API. ## Base URL and authentication | | | | - | - | | Base URL | `http://127.0.0.1:4040/api` | | Authentication | **None** | The ngrok agent API is exposed as part of ngrok's local web inspection interface. Because it's served on a local interface, the API has no authentication. The Base URL will change if you override `web_addr` in your configuration file. ### Access the root API resource of a running ngrok Agent ```sh theme={null} curl http://localhost:4040/api/ ``` ## Supported content types Request parameters must be encoded to the API using `application/json`. Ensure that your client sets the request's `Content-Type` header appropriately. All responses returned by the API are `application/json`. ## Versioning and API stability The ngrok agent API guarantees that breaking changes to the API will never be made unless the caller explicitly opts in to a newer version. The mechanism by which a caller opts into a new version of the API will be determined in the future when it becomes necessary. Examples of non-breaking changes to the API that will not be opt-in include: * The addition of new resources * The addition of new methods to existing resources * The addition of new fields on existing resource representations * Bug fixes that change the API to match documented behavior ## List tunnels This API is deprecated. Use the [list endpoints API](#list-endpoints) instead. Returns a list of running tunnels with status and metrics information. ### Request `GET/api/tunnels` ### Parameters | | | | - | - | | `tunnels` | List of all running tunnels. See the [Tunnel detail](#tunnel-detail) resource for docs on the parameters of each tunnel object. | ### Example response ``` { "tunnels": [ { "name": "command_line", "uri": "/api/tunnels/command_line", "public_url": "https://d95211d2.ngrok.app", "proto": "https", "config": { "addr": "localhost:80", "inspect": true }, "metrics": { "conns": { "count": 0, "gauge": 0, "rate1": 0, "rate5": 0, "rate15": 0, "p50": 0, "p90": 0, "p95": 0, "p99": 0 }, "http": { "count": 0, "rate1": 0, "rate5": 0, "rate15": 0, "p50": 0, "p90": 0, "p95": 0, "p99": 0 } } } ], "uri": "/api/tunnels" } ``` ## Start tunnel Dynamically starts a new tunnel on the ngrok agent. The request body parameters are the same as those you would use to define the tunnel in the configuration file. ### Request `POST/api/tunnels` ### Parameters Parameter names and behaviors are identical to those those defined in the configuration file. Use the [tunnel definitions](/docs/gateway/agent/config/v2/#tunnel-configurations) section as a reference for configuration parameters and their behaviors. ### Example request body ``` { "addr": "22", "proto": "tcp", "name": "ssh" } ``` ### Response `201 Created` status code with a response body describing the started tunnel. See the [Tunnel detail](#tunnel-detail) resource for docs on the parameters of the response object. ### Example response ``` { "name": "", "uri": "/api/tunnels/", "public_url": "tcp://0.tcp.ngrok.io:53476", "proto": "tcp", "config": { "addr": "localhost:22", "inspect": false }, "metrics": { "conns": { "count": 0, "gauge": 0, "rate1": 0, "rate5": 0, "rate15": 0, "p50": 0, "p90": 0, "p95": 0, "p99": 0 }, "http": { "count": 0, "rate1": 0, "rate5": 0, "rate15": 0, "p50": 0, "p90": 0, "p95": 0, "p99": 0 } } } ``` ## Tunnel detail Get status and metrics about the named running tunnel. ### Request `GET/api/tunnels/:name` ### Example response ``` { "name": "command_line", "uri": "/api/tunnels/command_line", "public_url": "https://ac294125.ngrok.app", "proto": "https", "config": { "addr": "localhost:80", "inspect": true }, "metrics": { "conns": { "count": 0, "gauge": 0, "rate1": 0, "rate5": 0, "rate15": 0, "p50": 0, "p90": 0, "p95": 0, "p99": 0 }, "http": { "count": 0, "rate1": 0, "rate5": 0, "rate15": 0, "p50": 0, "p90": 0, "p95": 0, "p99": 0 } } } ``` ## Stop tunnel Stop a running tunnel. ### Request `DELETE/api/tunnels/:name` ### Response `204 No Content` status code with an empty body. ## List endpoints Returns a list of running endpoints with status and metrics information. ### Request `GET/api/endpoints` ### Response `200 OK` status code with a JSON response containing an array of endpoint objects. ### Example response ``` { "endpoints": [ { "name": "command_line", "uri": "/api/endpoints/command_line", "url": "https://871e40f2d4c2.ngrok.paid.lan", "upstream": { "url": "http://localhost:8000", "protocol": "https" }, "inspect": true, "traffic_policy": "on_http_request:\n - actions:\n - type: custom-response\n config:\n status_code: 200\n body: Hello, World!\n", "pooling_enabled": true } ] } ``` ## Endpoint detail Get status and metrics about the named running endpoint. ### Request `GET/api/endpoints/:name` ### Response `200 OK` status code with endpoint JSON for success. `404 Not Found` if the provided name doesn't correspond to an endpoint. ### Example response ``` { "name": "command_line", "uri": "/api/endpoints/command_line", "url": "https://871e40f2d4c2.ngrok.paid.lan", "upstream": { "url": "http://localhost:8000", "protocol": "https" }, "inspect": true, "traffic_policy": "on_http_request:\n - actions:\n - type: custom-response\n config:\n status_code: 200\n body: Hello, World!\n", "pooling_enabled": true, "metrics": { "conns": { "count": 0, "gauge": 0, "rate1": 0, "rate5": 0, "rate15": 0, "p50": 0, "p90": 0, "p95": 0, "p99": 0 }, "http": { "count": 0, "rate1": 0, "rate5": 0, "rate15": 0, "p50": 0, "p90": 0, "p95": 0, "p99": 0 } } } ``` ## Create endpoint Dynamically creates a new endpoint on the ngrok agent. ### Request `POST/api/endpoints` ### Response `201 Created` on success. `400 Bad Request` for malformed payloads. ### Example request body ``` { "name": "my_awesome_tunnel", "url": "https://iamawesome.ngrok.paid.lan", "upstream": { "url": "8001" }, "pooling_enabled": true } ``` ## Update endpoint Updates an existing endpoint or creates a new one if it doesn't exist. ### Request `PUT/api/endpoints/:name` ### Response `200 OK` if an existing endpoint is updated. `201 Created` if an endpoint is created. `400 Bad Request` for malformed payloads. ### Example request body ``` { "name": "command_line", "uri": "/api/endpoints/command_line", "url": "https://871e40f2d4c2.ngrok.paid.lan", "upstream": { "url": "http://localhost:8000", "protocol": "https" }, "inspect": true, "traffic_policy": "on_http_request:\n - actions:\n - type: custom-response\n config:\n status_code: 200\n body: Hello, World!\n", "pooling_enabled": true } ``` ## Delete endpoint Stop a running endpoint. ### Request `DELETE/api/endpoints/:name` ### Response `204 No Content` for successful delete. `404 Not Found` if trying to delete a nonexistent endpoint. ## List captured requests Returns a list of all HTTP requests captured for inspection. This will only return requests that are still in memory—ngrok evicts captured requests when their memory usage exceeds `inspect_db_size`. ### Request `GET/api/requests/http` ### Query parameters | | | | - | - | | `limit` | Maximum number of requests to return. | | `tunnel_name` | Filter requests only for the given tunnel name. | ### Example request ```sh theme={null} curl http://localhost:4040/api/requests/http?limit=50 ``` ### Response | | | | - | - | | `requests` | List of captured requests. See the [Captured Request Detail](#captured-request-detail) resource for docs on the request objects. | ### Example response ``` { "uri": "/api/requests/http", "requests": [ { "uri": "/api/requests/http/548fb5c700000002", "id": "548fb5c700000002", "tunnel_name": "command_line (http)", "remote_addr": "192.168.100.25", "start": "2014-12-15T20:32:07-08:00", "duration": 3893202, "request": { "method": "GET", "proto": "HTTP/1.1", "headers": { "Accept": [ "*/*" ], "Accept-Encoding": [ "gzip, deflate, sdch" ], "Accept-Language": [ "en-US,en;q=0.8" ], "Connection": [ "keep-alive" ], "User-Agent": [ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36" ], "X-Original-Host": [ "c159663f.ngrok.app" ] }, "uri": "/favicon.ico", "raw": "" }, "response": { "status": "502 Bad Gateway", "status_code": 502, "proto": "HTTP/1.1", "headers": { "Content-Length": [ "1716" ] }, "raw": "" } } ] } ``` ## Replay captured request Replays a request against the local endpoint of a tunnel. ### Request `POST/api/requests/http` ### Parameters | | | | - | - | | `id` | ID of request to replay. | | `tunnel_name` | Name of the tunnel to play the request against. If unspecified, the request is played against the same tunnel it was recorded on. | ### Example request ```sh theme={null} curl -H "Content-Type: application/json" -d '{"id": "548fb5c700000002"}' http://localhost:4040/api/requests/http ``` ### Response `204 No Content` status code with an empty body. ## Delete captured requests Deletes all captured requests. ### Request `DELETE/api/requests/http` ### Response `204 No Content` status code with no response body. ## Captured request detail Returns metadata and raw bytes of a captured request. The raw data is base64-encoded in the JSON response. The `response` value may be `null` if the local server hasn't responded to a request yet. ### Request `GET/api/requests/http/:request_id` ### Example response ``` { "uri": "/api/requests/http/548fb5c700000002", "id": "548fb5c700000002", "tunnel_name": "command_line (http)", "remote_addr": "192.168.100.25", "start": "2014-12-15T20:32:07-08:00", "duration": 3893202, "request": { "method": "GET", "proto": "HTTP/1.1", "headers": { "Accept": [ "*/*" ], "Accept-Encoding": [ "gzip, deflate, sdch" ], "Accept-Language": [ "en-US,en;q=0.8" ], "Connection": [ "keep-alive" ], "User-Agent": [ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36" ], "X-Original-Host": [ "c159663f.ngrok.app" ] }, "uri": "/favicon.ico", "raw": "" }, "response": { "status": "502 Bad Gateway", "status_code": 502, "proto": "HTTP/1.1", "headers": { "Content-Length": [ "1716" ] }, "raw": "" } } ``` ## Agent status Get status and metrics about the running agent. ### Request `GET /api/status` ### Example request ```sh theme={null} curl http://localhost:4040/api/status ``` ### Response `200 OK` status code with a JSON response describing the status of the agent. ### Example response ``` { "status": "online", "agent_version": "3.9.0", "session": { "legs": [ { "region": "au", "latency": "87ms" } ] }, "uri": "/api/status" } ``` # Agent API Keys Source: https://ngrok.com/docs/gateway/agent/api-keys Add an API key to the agent so you can query and manage domains, endpoints, and other account resources from the command line. The ngrok agent contains a [complete CLI for the ngrok API](/docs/gateway/agent/cli-api/). To use it, [create an API key on your ngrok dashboard](https://dashboard.ngrok.com/api-keys) and then run: ```bash theme={null} ngrok config add-api-key ``` After you add the API key, try querying your list of domains or online endpoints: ```bash theme={null} ngrok api endpoints list ngrok api reserved-domains list ``` # Agent Authtokens Source: https://ngrok.com/docs/gateway/agent/authtokens Authenticate an agent with your ngrok account using an authtoken, and use ACLs to scope which endpoints and resources that credential is allowed to use. The ngrok agent authenticates with an authtoken. [Your authtoken is available on the ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). Add your authtoken to the ngrok agent with the following command: ```bash theme={null} ngrok config add-authtoken ``` This command updates the `authtoken` property in your [ngrok configuration file](/docs/gateway/agent/config). Use a separate authtoken for each agent you configure. You can [provision additional authtokens on your ngrok dashboard](https://dashboard.ngrok.com/authtokens) or [via API](/docs/gateway/agent/config/v3/#authtoken). Separate authtokens isolate the security risk if an authtoken is compromised. It also allows you to configure ACLs on a per agent basis. When you provision a new authtoken, the full token is only displayed once. As a security feature, ngrok does not store a recoverable representation of the token. ## Authtoken ACLs Authtoken ACLs restrict what actions an ngrok agent connecting with that authtoken is allowed to take. You may define multiple ACLs. Authtokens with no ACLs may take all actions. The following ACLs are supported: | Example | Description | | - | - | | `bind:foo.ngrok.app` | The agent may only create an endpoint on `foo.ngrok.app` | | `bind:*.example.com` | The agent may only create endpoints on subdomains of `example.com` | | `bind:foo=bar` | The agent may create a labeled endpoint with the label `foo=bar` | | `bind:app=*` | The agent may create labeled endpoints with labels like `app=x` or `app=y` | | `bind:*` | The agent may listen on all endpoints | # ngrok Agent Changelog Source: https://ngrok.com/docs/gateway/agent/changelog Release notes for every version of the ngrok agent, covering new features, fixes, and the breaking changes between major versions. ## v3 ### ngrok Agent 3.39.11 \[2026-08-12] * Moved to ngrok-go v2.1.1 ### ngrok Agent 3.39.10 \[2026-07-22] * Corrected stale `dashboard.ngrok.com` links shown in `ngrok --help` and CLI error output. ### ngrok Agent 3.39.9 \[2026-06-23] * Only includes internal release pipeline changes ### ngrok Agent 3.39.8 \[2026-06-16] * Remove broad filesystem write virtualization permission for ngrok on the Microsoft App Store ### ngrok Agent 3.39.7 \[2026-06-09] * Update libraries and Go version to patch most recent round of CVEs * Add new `crl_url` configuration option to allow using a custom CRL ### ngrok Agent 3.39.6 \[2026-06-01] * Updated OpenTelemetry dependencies to patch security vulnerabilities ### ngrok Agent 3.39.5 \[2026-05-26] * Internal improvements to the build process, no functional changes ### ngrok Agent 3.39.3 \[2026-05-21] * Patch various CVEs ### ngrok Agent 3.39.2 \[2026-05-15] * Various security improvements ### ngrok Agent 3.39.1 \[2026-04-28] * Fix crash on startup for Windows Store builds ### ngrok Agent 3.39.0 \[2026-04-27] * Added support for AI Gateway provider keys ### ngrok Agent 3.38.0 \[2026-04-17] * Upgraded internal dependencies ### ngrok Agent 3.37.6 \[2026-04-09] * Internal improvements to the build process, no functional changes ### ngrok Agent 3.37.3 \[2026-03-26] * Internal improvements to the build process, no functional changes ### ngrok Agent 3.37.2 \[2026-03-10] * Fix a regression in macOS configuration paths ### ngrok Agent 3.37.1 \[2026-03-06] * Disable updates for Windows App Store builds * Fix config file location for Microsoft Store installs * Add `ai-gateway-api-keys` CLI commands ### ngrok Agent 3.36.1 \[2026-02-12] * Fix for upstream.proxy\_protocol field in V3 configuration ### ngrok Agent 3.36.0 \[2026-02-03] * Change agent self update link (was: `update.equinox.io`, is now: `update.ngrok-agent.com`). * Deprecated flags that are made obsolete by Traffic Policies. Such flags can still be used, but aren't shown in `--help` output and will display deprecation warnings if used. * Hid the deprecated `ngrok tunnel` command from `--help` output. Please use `ngrok http`, `ngrok tcp`, or `ngrok tls` instead. ### ngrok Agent 3.35.0 \[2026-01-12] * Honor custom DNS if provided. ### ngrok Agent 3.34.1 \[2025-12-11] * Fix a bug with the `*-file` and `*-url` flags that was overwriting flag values with nil ### ngrok Agent 3.34.0 \[2025-12-05] * Deprecate the `--terminate-at` command-line option ### ngrok Agent 3.33.1 \[2025-11-25] * Fix a bug where inspector is filled with error logs when not in use ### ngrok Agent 3.33.0 \[2025-11-12] * Fix a bug that could result in pooling being disabled via the API or Dashboard ### ngrok Agent 3.32.0 \[2025-10-23] * Allow local\_storage config option to disable local inspector * Allow setting RLIMIT\_NOFILE when running as a systemd service ### ngrok Agent 3.31.0 \[2025-10-20] * Rename Bot users to Service users. * Update billing urls. * Can now use vault name or id when creating a secret. ### ngrok Agent 3.30.0 \[2025-09-23] * A new agent HTTP API, `/api/endpoints`, has been introduced that corresponds to the [agent config version 3 format](/docs/gateway/agent/config/v3/) * Endpoints and tunnels deleted via the agent API will be properly reflected in the Dashboard ### ngrok Agent 3.29.0 \[2025-09-17] * Blocked creating tunnels with duplicate names via the agent HTTP API ### ngrok Agent 3.28.0 \[2025-09-11] * Updated Docker build base * Restored support for `PROXY` in TCP/TLS tunnels ### ngrok Agent 3.27.0 \[2025-08-28] * Optimized agent ui. * Added get-secrets-by-vault path to vaults api 🤫 ### ngrok Agent 3.26.0 \[2025-08-07] * Added support for supplying a Traffic Policy to endpoints created with `ngrok http` and `ngrok api` using the `--traffic-policy-url` flag ### ngrok Agent 3.25.1 \[2025-08-01] * Allow specifying a file for secrets, rather than requiring them to be passed on the command line ### ngrok Agent 3.25.0 \[2025-07-24] * Debian Bookworm support added. * This release is functionally identical to 3.24.0. ### ngrok Agent 3.24.0 \[2025-07-17] This version includes critical security fixes for users of mTLS that exists in agent version 3.19 through 3.23. ngrok **strongly** encourages anyone who uses mTLS to upgrade immediately. * Allow for configuring maximum and minimum TLS versions * Allow enforcing mTLS certificate checks ### ngrok Agent 3.23.3 \[2025-06-26] * Updates and improvements to `--help` text ### ngrok Agent 3.23.2 \[2025-06-20] * Improve Windows diagnostics ### ngrok Agent 3.23.1 \[2025-06-10] * Correct names for vaults and secrets API commands. ### ngrok Agent 3.23.0 \[2025-06-05] * Add API command support for vaults and secrets. * Add audit events to vaults and secrets. ### ngrok Agent 3.22.1 \[2025-03-20] * Fix CLI help/descriptions for endpoints create command. ### ngrok Agent 3.22.0 \[2025-03-20] * Fixed a bug where TCP KeepAlive support could cause a crash on old versions of Windows ### ngrok Agent 3.21.0 \[2025-03-13] * Add TCP KeepAlive configuration option. ### ngrok Agent 3.20.0 \[2025-02-20] * Add bindings to v3 configs. ### ngrok Agent 3.19.1 - \[2024-1-23] * Improve ux of CLI errors. * Correct error scheme on errors. ### ngrok Agent 3.19.0 - \[2024-12-19] * Add tls termination to v3 agent configs. ### ngrok Agent 3.18.4 - \[2024-11-11] * Fixed a bug causing the `--url` flag require an explicit scheme. ### ngrok Agent 3.18.3 - \[2024-11-07] * Fixed agent diagnose return codes. ### ngrok Agent 3.18.2 - \[2024-10-31] * Fixed ngrok diagnose --region all report. * Fixed scheme proto defaulting. * Added 128 character limit to usernames for agent basic auth. ### ngrok Agent 3.18.1 - \[2024-10-24] * Adds binary execution path to the host system metrics collected. * Fixed a bug where 4 character time zones were not handled properly in the console UI. ### ngrok Agent 3.18.0 - \[2024-10-17] * Added `--name` field for endpoints and tunnels when created from the agent config or CLI. ### ngrok Agent 3.17.0 - \[2024-10-10] * Added endpoint create/update/delete subcommands to the ngrok api command. * Added support for policy phases like `on_tcp_connect`, `on_http_request` and `on_http_response`. * Fixed a bug where TLS was incorrectly passed as the endpoint proto rather than HTTP(S). ### ngrok Agent 3.16.1 - \[2024-10-03] * Added windows arm64 agent binary. ### ngrok Agent 3.16.0 - \[2024-09-09] * Releasing the features originally released by 3.15.0 * Added `--url` agent CLI flag for creation of HTTP(S), TLS, and TCP protocol endpoints. * Deprecated `--domain`, `--scheme`, and `--remote-addr` agent CLI flags, which have been replaced by `--url`. * Added `--metadata` and `--description` agent CLI flags when using `--url`. * Added `endpoints:` as a new field used for [endpoint creation](/docs/gateway/agent/config/v3/). * A new `agent:` field has been added to the agent configuration format for V3. * v3 supports *both* tunnels and endpoints. Tunnels are now considered deprecated when using config v3. * Some agent configuration fields have been renamed in v3. * v2 is still supported, but doesn't support the new `endpoints:` , or `agent:` fields. * Added support for [endpoint fields](/docs/api-reference/endpoints/list) in ngrok's `api`. * Update `config` commands to support new configuration version 3, including `upgrade`, `add-authtoken`, and `add-api-key` ### ngrok Agent 3.15.1 - \[2024-08-29] * Restores the functionality and compatibility for version 3.14.0 due to bugs in the previous release. An updated version with the changes from 3.15.0 will be released early next week. ### \[UNSTABLE] ngrok Agent 3.15.0 - \[2024-08-29] * Added `--url` agent CLI flag for creation of HTTP(S), TLS, and TCP protocol endpoints. * Deprecated `--domain`, `--scheme`, and `--remote-addr` agent CLI flags, which have been replaced by `--url`. * Added `--metadata` and `--description` agent CLI flags when using `--url`. * Added `endpoints:` as a new field used for [endpoint creation](/docs/gateway/agent/config/v3/). * A new `agent:` field has been added to the agent configuration format for V3. * v3 supports *both* tunnels and endpoints. Tunnels are now considered deprecated when using config v3. * Some agent configuration fields have been renamed in v3. * v2 is still supported, but doesn't support the new `endpoints:` , or `agent:` fields. * Added support for [endpoint fields](/docs/api-reference/endpoints/list) in ngrok's `api`. ### ngrok Agent 3.14.1 - \[2024-08-22] * Resolved an issue with mTLS detection. ### ngrok Agent 3.14.0 - \[2024-08-01] * Added support for `traffic_policy` field in agent config for Traffic Policy configuration. * Deprecated `policy` * Added `EndpointTrafficPolicy` module to Edge API. ### ngrok Agent 3.13.0 - \[2024-07-15] * In some cases, adds additional headers on error responses to ngrok that can be used to customize the content. ### ngrok Agent 3.12.1 - \[2024-07-11] * Fixed a bug in `ngrok diagnose` that would cause a panic if a server IP and the agent had TLS connectivity issues. * Added `--traffic-policy-file` flag that accepts Traffic Policy configuration for HTTP, TCP, or TLS traffic. * Deprecated `--policy-file` flag ### ngrok Agent 3.12.0 - \[2024-06-27] * Errors now have links to ngrok error page. ### ngrok Agent 3.11.0 - \[2024-06-13] * `ngrok http` now has timestamps for incoming requests. ### ngrok Agent 3.10.1 - \[2024-06-06] * Requests to TLS endpoints using `proto: tls` and `terminate_at: agent` succeed after the agent session goes through a reconnection. ### ngrok Agent 3.10.0 - \[2024-05-23] * **ACTION MAY BE REQUIRED**: The domain used for Certificate Revocation List (CRL) checks is now `crl.ngrok-agent.com` to align it with the domain used for session connections. This may require changes to your firewall or proxy settings to allow this outbound connection on port 80, or setting `crl_noverify: true` in the agent config file. * Added CLI API support for [Service Users](/docs/iam/service-users/). ### ngrok Agent 3.9.0 - \[2024-04-24] * Added a `/api/status` endpoint to the local agent API that returns the agent's current status * The agent will check the Certificate Revocation List (CRL) on Session initiation. If a custom Connect URL is being used that has an invalid CRL in the chain, the `crl_noverify: true` option can be used to override this check, if desired * Certificate Revocation List (CRL) checking for certain Let's Encrypt certificate chains has been fixed ### ngrok Agent 3.8.0 - \[2024-03-14] * Added `json` support to Traffic Policy configuration * Moved to ngrok-go v1.9.1 ### ngrok Agent 3.7.0 - \[2024-03-07] * Fixed an issue where `ngrok diagnose --region all` would return an error for `eu-lon-1` * Fixed a typo in default usage output * Moved to `ngrok-go` v1.9.0 ### ngrok Agent 3.6.0 - \[2024-02-06] * Added `--policy-file` flag that accepts Traffic Policy configuration for HTTP, TCP, or TLS traffic. * Added support for `policy` field in agent config for Traffic Policy configuration. * Added concise help text when `ngrok` is run without any args * Removed support for Windows 7 and Windows 8 (support for Windows 7 and 8 was dropped in [Go 1.21](https://tip.golang.org/doc/go1.21#windows)) ### ngrok Agent 3.5.0 - \[2023-12-01] * The `--region` flag has been deprecated, ngrok automatically chooses the region with lowest latency * The upstream tls configs have been renamed, with backwards compatibility: * `--verify-upstream-tls` is now `--upstream-tls-verify` * `--upstream-ca-path` is now `--upstream-tls-verify-cas` * An option for `--app-protocol` of the upstream, which can be set to `http2`, has been added * The `StopTunnel` message is now handled by the agent ### ngrok Agent 3.4.0 - \[2023-11-03] * Added support for upstream TLS verification when forwarding to `https://` or `tls://` addresses. * `--verify-upstream-tls` enables TLS verification of server TLS certificates * `--upstream-ca-path` sets the certificate authority used to verify upstream server certificates ### ngrok Agent 3.3.5 - \[2023-09-26] * Added support for the User Agent Filter module (now deprecated by Traffic Policy) that allows or denies traffic to HTTPS endpoints based on incoming user agents. * Added `--ua-filter-allow` and `--ua-filter-deny` flags that accept a list of regular expression strings ### ngrok Agent 3.3.4 - \[2023-08-18] * Fixed a bug where the agent running as a service would log to syslog even when another log destination was specified * Fixed a bug where the agent could incorrectly ignore `dns_resolver_ips` ### ngrok Agent 3.3.3 - \[2023-08-11] * Added new `us-cal-1` region which will reduce latency for users in the western half of North America ### ngrok Agent 3.3.2 - \[2023-07-27] * Added support for the [Datadog event destination](/docs/integrations/event-destinations/datadog-event-destination) in `ngrok api` ### ngrok Agent 3.3.1 - \[2023-06-05] * Fixed a bug where colons in request header and response header values were not allowed (for example, `--request-header-add "Access-Control-Allow-Origin:https://developer.mozilla.org"`) * Fixed a bug introduced in v3.3.0 where `--terminate-at edge` wasn't using the correct TLS certificate ### ngrok Agent 3.3.0 - \[2023-05-09] * Added new default tunnel ingress names: the agent now connects to [`connect.ngrok-agent.com`](/docs/gateway/security-dev-productivity/securing-your-tunnels/#blocking-non-corporate-accounts) when starting a session * Improved `ngrok diagnose` output to check that the DNS entry for `localhost` resolves * Added the command `ngrok config add-server-addr` for configuring custom Connect URLs * Re-wrote the tunnel and session backend to use the `ngrok-go` library ### ngrok Agent 3.2.2 - \[2023-03-27] * Fixed a bug introduced in v3.2.1 with tab complete for command line flags ### ngrok Agent 3.2.1 - \[2023-03-13] * Deprecated the `--subdomain` and `--hostname` flags. * Updated all `--subdomain` and `--hostname` examples to use `--domain`. * Fixed a bug where the agent didn't resolve local DNS correctly on macOS arm64 * Allow specifying ngrok `--region=auto` to pick the closest region (defaults to auto) * Support for the `NGROK_API_KEY` environment variable when using the ngrok api subcommand * `--log-format=json` now results in more output being formatted as json ### ngrok Agent 3.1.1 - \[2023-01-13] * Expanded diagnosis coverage when running `ngrok diagnose` to include testing against all regions and additional debug information of the underlying system. * Updated `--config` option to be accepted in any position with cli command. * Fixed `ngrok config add-authtoken ` to also save the default version if it doesn't exist in the config file. * Fixed rare race condition where agent would crash unexpectedly. * Added DNS rebinding protection which includes [`web_allow_hosts`](/docs/gateway/agent/config/v3/#web-allow-hosts) configuration. ### ngrok Agent 3.1.0 - \[2022-09-14] * Started signing Windows executables with a code signing certificate. Includes polish for Windows executable properties like description and icon. * Updated the root CA recognized by new agent versions. Improves incident recovery and mitigation options. ### ngrok Agent 3.0.7 - \[2022-08-17] * Added `--region` flag to the `ngrok diagnose` command. Including support for `--region all` to diagnose all regions. * Ignore the `--config` flag when passed to a command that doesn't support it. ### ngrok Agent 3.0.6 - \[2022-06-30] * Fixed issues with replay not displaying requests in the ngrok Agent web UI, console UI, and API. * Fixed null pointer issues with `ngrok diagnose` command. ### ngrok Agent 3.0.5 - \[2022-06-22] * Fixed a formatting issue in the inspector web UI for the header navigation. * Updated the start tunnel API to allow starting labeled tunnels using a list of strings as labels. ### ngrok Agent 3.0.4 - \[2022-05-27] * Added the ability to provide messages to users via the agent console. * `ngrok config edit` will now allow you to edit an invalid configuration file. * Updated the display of the latency number in the console UI to be more human readable. ### ngrok Agent 3.0.3 - \[2022-04-26] * Fixed a bug on Windows where running the agent from explorer didn't open a cmd prompt. ### ngrok Agent 3.0.2 - \[2022-04-11] * Fixed a bug that wasn't allowing users to start tunnels via the API. ### ngrok Agent 3.0.1 - \[2022-04-04] * Added `ngrok config edit` command to open the config file in your default editor. * Removed invalid `--proxy-proto` flag from the `ngrok start` command. * Added a migration to the `ngrok config upgrade` command to convert basic auth config parameters. * The ngrok agent API will now error if invalid values are passed to it. Previously it ignored them. ### ngrok Agent 3.0.0 - \[2022-03-28] For more information about upgrading from previous versions of the agent to v3.0, see the [upgrade guide](/docs/gateway/agent/upgrade-v2-v3/). * Fixed an issue where an agent would not reconnect after removing a reserved domain from your account. * Added `ngrok completion` to enable autocomplete for the cli. * Added `proxy_proto` option for enabling and specifying the [HAProxy's PROXY protocol version](https://www.haproxy.com/blog/haproxy/proxy-protocol/). * Added the ability to set the `connect_interface` for the agent. * Renamed `bind_tls` to `schemes`. * The agent UI now display ping time. * Added the `ngrok diagnose` command to the agent for troubleshooting connection issues. * Added the `ngrok config` command for managing the configuration file. * Added the ability to call the ngrok API from the agent using `ngrok api`. * Added the `ngrok service` command to manage the ngrok agent as a service. * All ngrok agent configuration files must now include a version number to indicate the format. * Added the ability to start Labeled Tunnels to the agent that work with the new Tunnel Group backend. * Renamed the "Clear" button to "Clear Requests" in the inspect UI to make it clear as to what it's doing. * Fixed an issue where running `ngrok authtoken` would make changes to your tunnel definitions. * The inspect UI will now pretty-print content types ending in +json. * Fixed an issue where the inspect UI was adding unwanted HTML tags to JSON bodies when replayed. * The ngrok agent now logs to stdout by default when `console_ui: false`. * Command line arguments must use single hyphen for single letter options, and double hyphen for longer names. * The ngrok agent now fails when there are keys in the config file it doesn't understand. Previously it ignored unknown options. * Using the `--host-header rewrite` option puts the original host header value into the `X-Forwarded-Host` header field instead of the `X-Original-Host` header in an attempt to be more standard. * Combined `http_proxy` and `socks5_proxy` configuration options into a single option ( ## v2 ### ngrok Agent 2.3.x If asked to forward to port 443, ngrok will now automatically forward HTTPS traffic instead of HTTP. This change would only affect you if you previously ran a server accepting unencrypted HTTP on port 443. To work around it, specify an explicit http URL if you need the old behavior: `ngrok http http://localhost:443`. If run under sudo, the ngrok agent previously consulted the sudo-ing user's home directory file when looking for its default configuration file. It now consults the home directory of the assumed user. To workaround this, you may specify an explicit configuration file location with the `-config` option. ### ngrok Agent 2.2.x The ngrok agent API no longer accepts `application/x-www-form-urlencoded` request bodies. In practice, this only affects the `/api/requests/http/:id` endpoint because posting to the `/api/tunnels` endpoint with this type of request body previously caused ngrok to crash. This change was made to help protect against maliciously crafted web pages that could cause a user to inadvertently interact with their local ngrok API. ### ngrok Agent 2.1.x Behavior changes for `http` and `tls` tunnels defined in the configuration file or started via the API that don't have a `subdomain` or `hostname` property. ```yaml theme={null} tunnels: webapp: proto: http addr: 80 ``` Given this example tunnel configuration, behavior will change in the following ways. ##### Old behavior Starts a tunnel using the name of the tunnel as the subdomain resulting in the URL `http://webapp.ngrok.app` ##### New behavior Starts a tunnel with a random subdomain, for example a URL like `http://d95211d2.ngrok.app` ##### How to keep the old behavior Add a `domain` property with the same name as the tunnel: ```yaml theme={null} tunnels: webapp: proto: http addr: 80 domain: webapp.ngrok.app ``` # ngrok Agent Command Line Interface (CLI) Source: https://ngrok.com/docs/gateway/agent/cli # ngrok Agent Command Line Interface (CLI) The ngrok agent CLI offers built-in commands for interfacing with the ngrok API. These API commands have been separated into the [ngrok agent CLI API page](/docs/gateway/agent/cli-api) for convenience. For more information about the ngrok API and interfacing with it directly, see [the ngrok api page](/docs/gateway/agent/api). ## ngrok ngrok puts network services, apps and APIs online. ngrok exposes local networked services behinds NATs and firewalls to the public internet over a secure tunnel. Share local websites, build/test webhook consumers and self-host personal services. Detailed help for each command is available by adding '--help' to any command or with the 'ngrok help' command. Open [https://dashboard.ngrok.com/traffic-inspector](https://dashboard.ngrok.com/traffic-inspector) to inspect traffic through your endpoints. ### Usage ```sh theme={null} ngrok [command] [flags] ``` ### Example ```sh theme={null} # forward http traffic from assigned public URL to local port 80 ngrok http 80 # choose a URL instead of random assignment ngrok http 8080 --url https://baz.ngrok.dev # manipulate traffic to your endpoint with a traffic policy file ngrok http 8080 --url https://baz.ngrok.dev --traffic-policy-file tp.yml # forward to another address on the network instead of a local port ngrok http foo.local:3000 # forward to a local https server ngrok http https://localhost:8443 # forward TCP traffic (e.g. ssh) with an assigned public address ngrok tcp 22 # load balance (in two terminals) ngrok http 80 --url https://api.example.com --pooling-enabled ngrok http 80 --url https://api.example.com --pooling-enabled # privately connect a service to the ngrok cloud. # then forward traffic from other endpoints in your account to it ngrok http 80 --url https://svc.internal ngrok tcp 5432 --url tcp://postgres.internal:5432 # make your endpoint url addressable only from k8s clusters # where you've installed the ngrok k8s operator ngrok http 80 --url https://example.namespace --binding kubernetes # start multiple endpoints defined in the config file ngrok start foo bar baz ``` ### SubCommands | Command | Description | | - | - | | [api](#ngrok-api) | use ngrok agent as an api client | | [completion](#ngrok-completion) | generates shell completion code for bash or zsh | | [config](#ngrok-config) | update or migrate ngrok's configuration file | | [credits](#ngrok-credits) | prints author and licensing information | | [diagnose](#ngrok-diagnose) | diagnose connection issues | | [http](#ngrok-http) | start an HTTP tunnel | | [service](#ngrok-service) | run and control an ngrok service on a target operating system | | [start](#ngrok-start) | start endpoints or tunnels by name from the configuration file | | [tcp](#ngrok-tcp) | start a TCP tunnel | | [tls](#ngrok-tls) | start a TLS tunnel | | [update](#ngrok-update) | update ngrok to the latest version | | [version](#ngrok-version) | print the version string | ### Flags | Flag | Description | | - | - | | `--metadata` | opaque user-defined metadata for the tunnel session | ## ngrok completion The `ngrok completion` command generates shell tab completion code for Bash or Zsh. This requires bash-completion or zsh-completions packages to be enabled in your shell. You can add it to your current session with the command ``` . <(ngrok completion) ``` To enable them each time you start a new session, add the following to your `.bashrc` or `.zshrc` files: ``` if command -v ngrok &>/dev/null; then eval "$(ngrok completion)" fi ``` Once you add this to your profile, you'll need to `source ~/.bashrc` or `source ~/.zshrc` to enable it for your current session. ### Usage ```sh theme={null} ngrok completion [flags] ``` ### Example ```sh theme={null} ``` ### Flags | Flag | Description | | - | - | | `--config` | path to config files; they are merged if multiple | ## ngrok config The config command gives a quick way to create or update ngrok's configuration file. Use 'add-authtoken' or 'add-api-key' to set the corresponding properties. Use 'check' to test a configuration file for validity. If you have an old configuration file, you can also use 'upgrade' to automatically migrate to the latest version. ### SubCommands | Command | Description | | - | - | | [add-api-key](#ngrok-config-add-api-key) | save api key to configuration file | | [add-authtoken](#ngrok-config-add-authtoken) | save authtoken to configuration file | | [add-connect-url](#ngrok-config-add-connect-url) | adds the connect URL (connect\_url) to configuration file for custom agent ingress | | [add-server-addr](#ngrok-config-add-server-addr) | alias of add-connect-url | | [check](#ngrok-config-check) | check configuration file | | [edit](#ngrok-config-edit) | edit configuration file | | [upgrade](#ngrok-config-upgrade) | auto-upgrade configuration file | ### Flags | Flag | Description | | - | - | | `--config` | path to config files; they are merged if multiple | ## ngrok config add-api-key The add-api-key command modifies your configuration file to include the specified api key. The API key can be generated in the [API section of the ngrok dashboard](https://dashboard.ngrok.com/api). ### Usage ```sh theme={null} ngrok config add-api-key TOKEN [flags] ``` ### Example ```sh theme={null} ngrok config add-api-key 1roPsn7AascHeO18mHcxRD3xT76_3ww7C9CDLYNgcdSYsscCB ``` ### Flags | Flag | Description | | - | - | | `--config` | save in this config file | ## ngrok config add-authtoken The add-authtoken command modifies your configuration file to include the specified authtoken. ngrok requires that you sign up for an account to use many advanced service features. In order to associate your client with an account, it must pass a secret token to ngrok when it starts up. Instead of passing this authtoken on every invocation, you may use this command to save it into your configuration file so that your client always authenticates you properly. Additionally, this command saves the default config version, providing a working config file out of the box. You can find your authtoken in the [getting started section of the ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). The ngrok service requires that you [sign up for an account](https://dashboard.ngrok.com/) to connect with an agent. Some advanced service features require a paid account. In order to associate your agent with an account, it must pass a secret token to the ngrok service when it starts up. Instead of passing this authtoken on every invocation, you may use this command to save it into your configuration file so that your agent always authenticates you properly. ### Usage ```sh theme={null} ngrok config add-authtoken TOKEN [flags] ``` ### Example ```sh theme={null} ngrok config add-authtoken 1rlHSX3HqrqmOWZdeJ6bIv8rfuo_4cmS1QswRGyxcQD8NOukF ``` ### Flags | Flag | Description | | - | - | | `--config` | save in this config file | ## ngrok config add-connect-url The add-connect-url command modifies your configuration file to include the specified connect URL (connect\_url) used for custom agent ingress. ### Usage ```sh theme={null} ngrok config add-connect-url agent.example.com:443 [flags] ``` ### Example ```sh theme={null} ngrok config add-connect-url agent.example.com:443 ``` ### Flags | Flag | Description | | - | - | | `--config` | save in this config file | ## ngrok config add-server-addr The ngrok config add-server-addr command updates the server address (server\_addr) in the configuration file. This is useful when your account is using Custom Agent Ingress and you need to configure the server\_addr to point to your new ingress domain. *See Also*: [`server_addr`](/docs/gateway/agent/config/#server-addr) [Custom Agent Ingress](/docs/gateway/agent/connect-url/) ### Usage ```sh theme={null} ngrok config add-server-addr agent.example.com:443 [flags] ``` ### Example ```sh theme={null} ngrok config add-server-addr agent.example.com:443 ``` ### Flags | Flag | Description | | - | - | | `--config` | save in this config file | ## ngrok config check Checks a configuration file for validity/correctness. ### Usage ```sh theme={null} ngrok config check [flags] ``` ### Flags | Flag | Description | | - | - | | `--config` | check this config file | ## ngrok config edit Opens the configuration file in an editor defined by the EDITOR environment variable, defaulting to nano or Notepad depending on OS. ### Usage ```sh theme={null} ngrok config edit [flags] ``` ### Flags | Flag | Description | | - | - | | `--config` | check this config file | ## ngrok config upgrade Upgrade a configuration file to a version. A backup file will be created with your original configuration file in the same directory. You can optionally pass a version to upgrade to. If the configuration file version is missing, the upgrade command will add it. It also applies all automatic transformations when upgrading versions. Attempting to downgrade will result in an error. By default this command will apply the transformations and display the final file. Use --dry-run to preview changes before applying. By default this command will not move any configuration files to their new default location. Use --relocate to move the config file to the default location. ### Usage ```sh theme={null} ngrok config upgrade [version] [flags] ``` ### Flags | Flag | Description | | - | - | | `--config` | upgrade this config file | | `--dry-run` | preview the proposed changes | | `--relocate` | relocates the config file to the default location | ## ngrok credits Displays program credits and license information. ### Usage ```sh theme={null} ngrok credits [flags] ``` ### Flags | Flag | Description | | - | - | | `--config` | path to config files; they are merged if multiple | ## ngrok diagnose Run tests to diagnose issues establishing connectivity to the ngrok cloud service. Exits 0 if connectivity is OK, otherwise 1. OPTIONS: \--config strings path to config files; they are merged if multiple -h, --help help for diagnose -6, --ipv6 Enable testing of IPV6 addresses -r, --region string ngrok server region \[auto, us, eu, au, ap, sa, jp, in, us-cal-1, eu-lon-1, all] -w, --write-report string Write a JSON report ### Usage ```sh theme={null} ngrok diagnose [flags] ``` ### Flags | Flag | Description | | - | - | | `--6`, `--ipv6` | Enable testing of IPV6 addresses | | `--r`, `--region` | ngrok server region \[auto, us, eu, au, ap, sa, jp, in, us-cal-1, eu-lon-1, all] | | `--w`, `--write-report` | Write a JSON report | | `--config` | path to config files; they are merged if multiple | ## ngrok http Forward HTTP traffic from an ngrok endpoint URL to a local port, address or URL. The ngrok agent creates secure TLS connections to the ngrok cloud gateway over which it creates an endpoint with a URL and forwards traffic received on that URL over the TLS connections to the agent and then on to your local service. Specify a Traffic Policy to configure the ngrok cloud service to apply auth, routing, security or other handling of the incoming HTTP traffic. If you are not forwarding traffic to an http(s) server, use `ngrok tcp` or `ngrok tls` instead. ### Usage ```sh theme={null} ngrok http [address:port | port] [flags] ``` ### Example ```sh theme={null} # forward traffic from a randomly assigned URL to port 8080 ngrok http 8080 # forward to another host on the network with a host:port address ngrok http servername.local:9000 # forward to an https server ngrok http https://localhost:8443 # choose your endpoint URL instead of it being assigned ngrok http 8080 --url htps://bar.ngrok.dev 80 # choose an endpoint URL on your own domain (requires a DNS CNAME) ngrok http 3000 --url https://api.example.com # add Traffic Policy config for auth, security, routing + more ngrok http 80 --url https://example.ngrok.app --traffic-policy-file tp.yml # load balance between endpoints (run in two terminals) ngrok http 8081 --url https://your-api.ngrok.app --pooling-enabled ngrok http 8080 --url https://your-api.ngrok.app --pooling-enabled # forward to a file URL to serve files from a directory ngrok http file:///var/log ``` ### Flags | Flag | Description | | - | - | | `--app-protocol` (deprecated) | use traffic policy instead | | `--authtoken` | ngrok.com authtoken identifying a user | | `--basic-auth` (deprecated) | use traffic policy instead | | `--binding` | ingress binding for an agent endpoint | | `--cidr-allow` (deprecated) | use traffic policy instead | | `--cidr-deny` (deprecated) | use traffic policy instead | | `--circuit-breaker` (deprecated) | use traffic policy instead | | `--compression` (deprecated) | use traffic policy instead | | `--description` | user editable description about this endpoint | | `--domain` (deprecated) | use --url instead | | `--host-header` (deprecated) | use traffic policy instead | | `--hostname` (deprecated) | use --domain instead | | `--inspect` | enable/disable http introspection (default true) | | `--metadata` | metadata about this endpoint | | `--mutual-tls-cas` (deprecated) | use traffic policy instead | | `--name` | user supplied name for this endpoint | | `--oauth` (deprecated) | use traffic policy instead | | `--oauth-allow-domain` (deprecated) | use traffic policy instead | | `--oauth-allow-email` (deprecated) | use traffic policy instead | | `--oauth-client-id` (deprecated) | use traffic policy instead | | `--oauth-client-secret` (deprecated) | use traffic policy instead | | `--oauth-scope` (deprecated) | use traffic policy instead | | `--oidc` (deprecated) | use traffic policy instead | | `--oidc-client-id` (deprecated) | use traffic policy instead | | `--oidc-client-secret` (deprecated) | use traffic policy instead | | `--oidc-scope` (deprecated) | use traffic policy instead | | `--policy-file` (deprecated) | use --traffic-policy-file instead | | `--pooling-enabled` | whether this endpoint can be pooled | | `--proxy-proto` (deprecated) | use --upstream-proxy-protocol instead | | `--region` (deprecated) | ngrok automatically chooses the region with lowest latency | | `--request-header-add` (deprecated) | use traffic policy instead | | `--request-header-remove` (deprecated) | use traffic policy instead | | `--response-header-add` (deprecated) | use traffic policy instead | | `--response-header-remove` (deprecated) | use traffic policy instead | | `--scheme` (deprecated) | use --url instead | | `--subdomain` (deprecated) | use --domain instead | | `--traffic-policy-file` | path to traffic policy configuration YAML or JSON file, e.g. './path/to/policy.yml' | | `--traffic-policy-url` | URL to traffic policy configuration YAML or JSON file, e.g. '[https://example.com/policy.yml](https://example.com/policy.yml)' | | `--ua-filter-allow` (deprecated) | use traffic policy instead | | `--ua-filter-deny` (deprecated) | use traffic policy instead | | `--upstream-protocol` (deprecated) | use traffic policy instead | | `--upstream-proxy-protocol` | version of proxy proto to use with this tunnel, empty if not unused | | `--upstream-tls-verify` | enables TLS verification of server TLS certificates | | `--upstream-tls-verify-cas` | path to CA cert to use to verify server certs | | `--url` | host endpoint on a URL | | `--verify-webhook` (deprecated) | use traffic policy instead | | `--verify-webhook-secret` (deprecated) | use traffic policy instead | | `--websocket-tcp-converter` (deprecated) | use traffic policy instead | | `--config` | path to config files; they are merged if multiple | ## ngrok service The service command manages installation and execution of ngrok as an operating system service on Windows, OS X and Linux systems. The service command takes a single argument which must be 'start', 'stop', 'restart', 'install', or 'uninstall'. When you choose 'install', you must specify the config flag which will define where the installed ngrok service looks for its configuration file. When the ngrok service runs, it has the same behavior as if it were invoked from the command line with the command: "ngrok start --all". For more information about running ngrok as a service, check out the [ngrok service section in the secure tunnels documentation](/docs/gateway/agent/service#running-ngrok-in-the-background) ### Usage ```sh theme={null} ngrok service [flags] ``` ### Example ```sh theme={null} ngrok service install --config=C:\ngrok.yml ngrok service start ngrok service stop ``` ### Flags | Flag | Description | | - | - | | `--config` | path to config files; they are merged if multiple | ## ngrok start start - start endpoints by name from the configuration file ### Usage ```sh theme={null} ngrok start [flags] ``` ### Example ```sh theme={null} ngrok start dev # start tunnel named 'dev' in the configuration file ngrok start web blog # start tunnels named 'web' and 'blog' ngrok start --all # start all tunnels defined in the config file ``` ### Flags | Flag | Description | | - | - | | `--all` | start all endpoints or tunnels in the configuration file | | `--authtoken` | ngrok.com authtoken identifying a user | | `--none` | start running no endpoints or tunnels | | `--region` (deprecated) | ngrok automatically chooses the region with lowest latency | | `--config` | path to config files; they are merged if multiple | ## ngrok tcp Starts a tunnel which forwards all TCP traffic on a public port to a local address. This is extremely useful for exposing services that run non-HTTP traffic (ssh, sip, rdp, game servers, etc). A TCP tunnel binds a public address on the remote ngrok server. Any services which require a stable public address should use the \--url option. ngrok requires that you reserve a TCP tunnel address for your account before you can use it. TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. ### Usage ```sh theme={null} ngrok tcp [address:port | port] [flags] ``` ### Example ```sh theme={null} # forward a port to your local ssh server ngrok tcp 22 # expose an RDP server on a specific public address that you reserved ngrok tcp --url=1.tcp.ngrok.io:27210 3389 ``` ### Flags | Flag | Description | | - | - | | `--authtoken` | ngrok.com authtoken identifying a user | | `--binding` | ingress binding for an agent endpoint | | `--cidr-allow` | reject connections that do not match the given CIDRs | | `--cidr-deny` | reject connections that match the given CIDRs | | `--description` | user editable description about this endpoint | | `--metadata` | metadata about this endpoint | | `--name` | user supplied name for this endpoint | | `--policy-file` (deprecated) | use --traffic-policy-file instead | | `--pooling-enabled` | whether this endpoint can be pooled | | `--proxy-proto` (deprecated) | use --upstream-proxy-protocol instead | | `--region` (deprecated) | ngrok automatically chooses the region with lowest latency | | `--remote-addr` (deprecated) | use --url instead | | `--traffic-policy-file` | path to traffic policy configuration YAML or JSON file, e.g. './path/to/policy.yml' | | `--traffic-policy-url` | URL to traffic policy configuration YAML or JSON file, e.g. '[https://example.com/policy.yml](https://example.com/policy.yml)' | | `--upstream-proxy-protocol` | version of proxy proto to use with this tunnel, empty if not unused | | `--url` | host endpoint on a URL | | `--config` | path to config files; they are merged if multiple | ## ngrok tls tls - create a TLS endpoint and forward traffic from it to a local server Starts a tunnel listening for TLS traffic on port 443 with a specific hostname. The TLS SNI (Server Name Indication) extension field in the TLS connection is inspected to determine which tunnel it matches. The ngrok server does not terminate TLS connections forwarded with this command. Any underlying protocol may be used. You may optionally specify a TLS key/cert pair which will be used to terminate the traffic client-side before it is forwarded. If not specified, the traffic will be forwarded still encrypted. Using this command is only recommended with the --url option. Other uses will work, but will always result in certificate mismatch warnings. ### Usage ```sh theme={null} ngrok tls [address:port | port] [flags] ``` ### Example ```sh theme={null} # forward TLS traffic for example.com to port 443 (requires CNAME) ngrok tls --url=example.com 443 # forward TLS traffic on subdomain (mismatch certificate warning) ngrok tls 1234 # terminate TLS traffic for t.co before forwarding ngrok tls --url=t.co --crt=/path/to/t.co.crt --key=/path/to/t.co.key 443 ``` ### Flags | Flag | Description | | - | - | | `--authtoken` | ngrok.com authtoken identifying a user | | `--binding` | ingress bindings for an agent endpoint | | `--cidr-allow` | reject connections that do not match the given CIDRs | | `--cidr-deny` | reject connections that match the given CIDRs | | `--crt` | path to a TLS certificate for TLS termination | | `--description` | user editable description about this endpoint | | `--domain` (deprecated) | use --url instead | | `--hostname` (deprecated) | use --domain instead | | `--key` | path to a TLS key for TLS termination | | `--metadata` | metadata about this endpoint | | `--mutual-tls-cas` | path to TLS certificate authority to verify client certs in mutual tls | | `--name` | user supplied name for this endpoint | | `--policy-file` (deprecated) | use --traffic-policy-file instead | | `--pooling-enabled` | whether this endpoint can be pooled | | `--proxy-proto` (deprecated) | use --upstream-proxy-protocol instead | | `--region` (deprecated) | ngrok automatically chooses the region with lowest latency | | `--subdomain` (deprecated) | use --domain instead | | `--terminate-at` (deprecated) | this flag is deprecated and will be removed in a future release; use terminate-tls via Traffic Policy or agent\_tls\_termination via agent configuration instead | | `--traffic-policy-file` | path to traffic policy configuration YAML or JSON file, e.g. './path/to/policy.yml' | | `--traffic-policy-url` | URL to traffic policy configuration YAML or JSON file, e.g. '[https://example.com/policy.yml](https://example.com/policy.yml)' | | `--upstream-proxy-protocol` | version of proxy proto to use with this tunnel, empty if not unused | | `--upstream-tls-verify` | enables TLS verification of server TLS certificates | | `--upstream-tls-verify-cas` | path to CA cert to use to verify server certs | | `--url` | host endpoint on a URL | | `--config` | path to config files; they are merged if multiple | ## ngrok update This command checks the ngrok cloud service for a newer version of the ngrok agent. If a newer version is available, it will download it and replace the ngrok binary with the new version after cryptographically verifying the update is signed by ngrok. In order to update successfully, the ngrok binary must be in a directory that is writable by your current user. If you placed ngrok in a system path, you may need to run this with root or Administrator privileges. Do not run this command if you installed ngrok with a package manager (i.e. homebrew, chocolatey, apt, snap, etc). Instead, update ngrok by using your package manager's update command. ### Usage ```sh theme={null} ngrok update [flags] ``` ### Example ```sh theme={null} ngrok update # update ngrok to the latest stable version ngrok update --channel=beta # update ngrok to the latest beta version ``` ### Flags | Flag | Description | | - | - | | `--channel` | update channel (stable, beta, unstable) | | `--config` | path to config files; they are merged if multiple | ## ngrok version print the version string ### Usage ```sh theme={null} ngrok version [flags] ``` ### Flags | Flag | Description | | - | - | | `--config` | path to config files; they are merged if multiple | # ngrok Agent CLI API Commands Source: https://ngrok.com/docs/gateway/agent/cli-api With the ngrok agent CLI, you can use built-in commands to interact with the ngrok API. For more information about the ngrok API and interfacing with it directly, see [the ngrok api page](/docs/api). If you want to programmatically control the ngrok agent, the [Agent SDKs](/docs/agent-sdks/) are usually a more flexible and powerful choice. ## ngrok api The api command provides access to ngrok's API. You can use the API through one of the api subcommmands. All api subcommands require an API key. You can configure it either through a command flag (--api-key) or add it in ngrok's configuration file (api\_key). You can get the initial API key at [https://dashboard.ngrok.com/api-keys](https://dashboard.ngrok.com/api-keys). Additional keys can be created through 'ngrok api api-keys create' subcommand. ### SubCommands | Command | Description | | - | - | | [abuse-reports](#ngrok-api-abuse-reports) | Abuse Reports allow you to submit take-down requests for URLs hosted by ngrok that... | | [agent-ingresses](#ngrok-api-agent-ingresses) | | | [api-keys](#ngrok-api-api-keys) | API Keys are used to authenticate to the ngrok API ([https://ngrok](https://ngrok) | | [application-sessions](#ngrok-api-application-sessions) | | | [application-users](#ngrok-api-application-users) | | | [backends](#ngrok-api-backends) | | | [bot-users](#ngrok-api-bot-users) | | | [certificate-authorities](#ngrok-api-certificate-authorities) | Certificate Authorities are x509 certificates that are used to sign other x509... | | [credentials](#ngrok-api-credentials) | Tunnel Credentials are ngrok agent authtokens | | [edge-modules](#ngrok-api-edge-modules) | | | [edges](#ngrok-api-edges) | | | [endpoint-configurations](#ngrok-api-endpoint-configurations) | Endpoint Configurations are a reusable group of modules that encapsulate how traffic... | | [endpoints](#ngrok-api-endpoints) | Endpoints provides an API for querying the endpoint objects which define what tunnel... | | [event-destinations](#ngrok-api-event-destinations) | | | [event-sources](#ngrok-api-event-sources) | | | [event-subscriptions](#ngrok-api-event-subscriptions) | | | [ip-policies](#ngrok-api-ip-policies) | IP Policies are reusable groups of CIDR ranges with an allow or deny action | | [ip-policy-rules](#ngrok-api-ip-policy-rules) | IP Policy Rules are the IPv4 or IPv6 CIDRs entries that make up an IP Policy. | | [ip-restrictions](#ngrok-api-ip-restrictions) | An IP restriction is a restriction placed on the CIDRs that are allowed to initiate... | | [kubernetes-operators](#ngrok-api-kubernetes-operators) | KubernetesOperators is used by the Kubernetes Operator to register and manage its own... | | [networks](#ngrok-api-networks) | Networks provide private address space for an account. | | [personal-access-tokens](#ngrok-api-personal-access-tokens) | Personal Access Tokens (PATs) are bearer credentials that authenticate the ngrok agent... | | [pointcfg-module](#ngrok-api-pointcfg-module) | | | [reserved-addrs](#ngrok-api-reserved-addrs) | Reserved Addresses are TCP addresses that can be used to listen for traffic | | [reserved-domains](#ngrok-api-reserved-domains) | Reserved Domains are hostnames that you can listen for traffic on | | [root](#ngrok-api-root) | | | [secrets](#ngrok-api-secrets) | Secrets is an api service for securely storing and managing sensitive data such as... | | [service-users](#ngrok-api-service-users) | | | [ssh-certificate-authorities](#ngrok-api-ssh-certificate-authorities) | An SSH Certificate Authority is a pair of an SSH Certificate and its private key that... | | [ssh-credentials](#ngrok-api-ssh-credentials) | SSH Credentials are SSH public keys that can be used to start SSH tunnels via the... | | [ssh-host-certificates](#ngrok-api-ssh-host-certificates) | SSH Host Certificates along with the corresponding private key allows an SSH server to... | | [ssh-user-certificates](#ngrok-api-ssh-user-certificates) | SSH User Certificates are presented by SSH clients when connecting to an SSH server to... | | [tls-certificates](#ngrok-api-tls-certificates) | TLS Certificates are pairs of x509 certificates and their matching private key that... | | [tunnel-sessions](#ngrok-api-tunnel-sessions) | Tunnel Sessions represent instances of ngrok agents or SSH reverse tunnel sessions... | | [tunnels](#ngrok-api-tunnels) | Tunnels provide endpoints to access services exposed by a running ngrok agent tunnel... | | [vaults](#ngrok-api-vaults) | Vaults is an api service for securely storing and managing sensitive data such as... | ### Flags | Flag | Description | | - | - | | `--config` | path to config files; they are merged if multiple | ## ngrok api abuse-reports Abuse Reports allow you to submit take-down requests for URLs hosted by ngrok that violate ngrok's terms of service. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-abuse-reports-create) | Creates a new abuse report which will be... | | [get](#ngrok-api-abuse-reports-get) | Get the detailed status of abuse report by ID. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api abuse-reports create Creates a new abuse report which will be reviewed by our system and abuse response team. This API is only available to authorized accounts. Contact [abuse@ngrok.com](mailto:abuse@ngrok.com) to request access ### Usage ```sh theme={null} ngrok api abuse-reports create [flags] ``` ### Flags | Flag | Description | | - | - | | `--metadata` | arbitrary user-defined data about this abuse report. Optional, max 4096 bytes. | | `--urls` | a list of URLs containing suspected abusive content | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api abuse-reports get Get the detailed status of abuse report by ID. ### Usage ```sh theme={null} ngrok api abuse-reports get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api agent-ingresses ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-agent-ingresses-create) | Create a new Agent Ingress | | [delete](#ngrok-api-agent-ingresses-delete) | Delete an Agent Ingress by ID | | [get](#ngrok-api-agent-ingresses-get) | Get the details of an Agent Ingress by ID. | | [list](#ngrok-api-agent-ingresses-list) | List all Agent Ingresses owned by this account | | [update](#ngrok-api-agent-ingresses-update) | Update attributes of an Agent Ingress by ID. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api agent-ingresses create Create a new Agent Ingress. The ngrok agent can be configured to connect to ngrok via the new set of addresses on the returned Agent Ingress. ### Usage ```sh theme={null} ngrok api agent-ingresses create [flags] ``` ### Flags | Flag | Description | | - | - | | `--certificate-management-policy.authority` | certificate authority to request certificates from. The only supported value is letsencrypt. | | `--certificate-management-policy.private-key-type` | type of private key to use when requesting certificates. Defaults to rsa, can be either rsa or ecdsa. | | `--description` | human-readable description of the use of this Agent Ingress. optional, max 255 bytes. | | `--domain` | the domain that you own to be used as the base domain name to generate regional agent ingress domains. | | `--metadata` | arbitrary user-defined machine-readable data of this Agent Ingress. optional, max 4096 bytes | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api agent-ingresses delete Delete an Agent Ingress by ID ### Usage ```sh theme={null} ngrok api agent-ingresses delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api agent-ingresses get Get the details of an Agent Ingress by ID. ### Usage ```sh theme={null} ngrok api agent-ingresses get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api agent-ingresses list List all Agent Ingresses owned by this account ### Usage ```sh theme={null} ngrok api agent-ingresses list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api agent-ingresses update Update attributes of an Agent Ingress by ID. ### Usage ```sh theme={null} ngrok api agent-ingresses update [flags] ``` ### Flags | Flag | Description | | - | - | | `--certificate-management-policy.authority` | certificate authority to request certificates from. The only supported value is letsencrypt. | | `--certificate-management-policy.private-key-type` | type of private key to use when requesting certificates. Defaults to rsa, can be either rsa or ecdsa. | | `--description` | human-readable description of the use of this Agent Ingress. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this Agent Ingress. optional, max 4096 bytes | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api api-keys API Keys are used to authenticate to the ngrok API ([https://ngrok.com/docs/api#authentication](https://ngrok.com/docs/api#authentication)). You may use the API itself to provision and manage API Keys but you'll need to provision your first API key from the API Keys page ([https://dashboard.ngrok.com/api-keys](https://dashboard.ngrok.com/api-keys)) on your ngrok.com dashboard. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-api-keys-create) | Create a new API key | | [delete](#ngrok-api-api-keys-delete) | Delete an API key by ID | | [get](#ngrok-api-api-keys-get) | Get the details of an API key by ID. | | [list](#ngrok-api-api-keys-list) | List all API keys owned by this account | | [update](#ngrok-api-api-keys-update) | Update attributes of an API key by ID. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api api-keys create Create a new API key. The generated API key can be used to authenticate to the ngrok API. ### Usage ```sh theme={null} ngrok api api-keys create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of what uses the API key to authenticate. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined data of this API key. optional, max 4096 bytes | | `--owner-email` | If supplied at credential creation, ownership will be assigned to the specified User. Only admins may specify an owner other than themselves. Both owner\_id and owner\_email may not be specified. | | `--owner-id` | If supplied at credential creation, ownership will be assigned to the specified User or Bot. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Bot. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api api-keys delete Delete an API key by ID ### Usage ```sh theme={null} ngrok api api-keys delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api api-keys get Get the details of an API key by ID. ### Usage ```sh theme={null} ngrok api api-keys get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api api-keys list List all API keys owned by this account ### Usage ```sh theme={null} ngrok api api-keys list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api api-keys update Update attributes of an API key by ID. ### Usage ```sh theme={null} ngrok api api-keys update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of what uses the API key to authenticate. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined data of this API key. optional, max 4096 bytes | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api application-sessions ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-application-sessions-delete) | Delete an application session by ID. | | [get](#ngrok-api-application-sessions-get) | Get an application session by ID. | | [list](#ngrok-api-application-sessions-list) | List all application sessions for this account. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api application-sessions delete Delete an application session by ID. ### Usage ```sh theme={null} ngrok api application-sessions delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api application-sessions get Get an application session by ID. ### Usage ```sh theme={null} ngrok api application-sessions get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api application-sessions list List all application sessions for this account. ### Usage ```sh theme={null} ngrok api application-sessions list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api application-users ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-application-users-delete) | Delete an application user by ID. | | [get](#ngrok-api-application-users-get) | Get an application user by ID. | | [list](#ngrok-api-application-users-list) | List all application users for this account. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api application-users delete Delete an application user by ID. ### Usage ```sh theme={null} ngrok api application-users delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api application-users get Get an application user by ID. ### Usage ```sh theme={null} ngrok api application-users get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api application-users list List all application users for this account. ### Usage ```sh theme={null} ngrok api application-users list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends ### SubCommands | Command | Description | | - | - | | [failover](#ngrok-api-backends-failover) | A Failover backend defines failover behavior within a list of referenced backends | | [http-response](#ngrok-api-backends-http-response) | | | [static-address](#ngrok-api-backends-static-address) | A static backend sends traffic to a TCP address (hostname and port) that is reachable... | | [tunnel-group](#ngrok-api-backends-tunnel-group) | A Tunnel Group Backend balances traffic among all online tunnels that match a label... | | [weighted](#ngrok-api-backends-weighted) | A Weighted Backend balances traffic among the referenced backends | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends failover A Failover backend defines failover behavior within a list of referenced backends. Traffic is sent to the first backend in the list. If that backend is offline or no connection can be established, ngrok attempts to connect to the next backend in the list until one is successful. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-backends-failover-create) | Create a new Failover backend | | [delete](#ngrok-api-backends-failover-delete) | Delete a Failover backend by ID. | | [get](#ngrok-api-backends-failover-get) | Get detailed information about a Failover... | | [list](#ngrok-api-backends-failover-list) | List all Failover backends on this account | | [update](#ngrok-api-backends-failover-update) | Update Failover backend by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends failover create Create a new Failover backend ### Usage ```sh theme={null} ngrok api backends failover create [flags] ``` ### Flags | Flag | Description | | - | - | | `--backends` | the ids of the child backends in order | | `--description` | human-readable description of this backend. Optional | | `--metadata` | arbitrary user-defined machine-readable data of this backend. Optional | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends failover delete Delete a Failover backend by ID. ### Usage ```sh theme={null} ngrok api backends failover delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends failover get Get detailed information about a Failover backend by ID ### Usage ```sh theme={null} ngrok api backends failover get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends failover list List all Failover backends on this account ### Usage ```sh theme={null} ngrok api backends failover list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends failover update Update Failover backend by ID ### Usage ```sh theme={null} ngrok api backends failover update [flags] ``` ### Flags | Flag | Description | | - | - | | `--backends` | the ids of the child backends in order | | `--description` | human-readable description of this backend. Optional | | `--metadata` | arbitrary user-defined machine-readable data of this backend. Optional | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends http-response ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-backends-http-response-create) | | | [delete](#ngrok-api-backends-http-response-delete) | | | [get](#ngrok-api-backends-http-response-get) | | | [list](#ngrok-api-backends-http-response-list) | | | [update](#ngrok-api-backends-http-response-update) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends http-response create ### Usage ```sh theme={null} ngrok api backends http-response create [flags] ``` ### Flags | Flag | Description | | - | - | | `--body` | body to return as fixed content | | `--description` | human-readable description of this backend. Optional | | `--headers` | headers to return | | `--metadata` | arbitrary user-defined machine-readable data of this backend. Optional | | `--status-code` | status code to return | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends http-response delete ### Usage ```sh theme={null} ngrok api backends http-response delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends http-response get ### Usage ```sh theme={null} ngrok api backends http-response get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends http-response list ### Usage ```sh theme={null} ngrok api backends http-response list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends http-response update ### Usage ```sh theme={null} ngrok api backends http-response update [flags] ``` ### Flags | Flag | Description | | - | - | | `--body` | body to return as fixed content | | `--description` | human-readable description of this backend. Optional | | `--headers` | headers to return | | `--metadata` | arbitrary user-defined machine-readable data of this backend. Optional | | `--status-code` | status code to return | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends static-address A static backend sends traffic to a TCP address (hostname and port) that is reachable on the public internet. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-backends-static-address-create) | Create a new static backend | | [delete](#ngrok-api-backends-static-address-delete) | Delete a static backend by ID. | | [get](#ngrok-api-backends-static-address-get) | Get detailed information about a static... | | [list](#ngrok-api-backends-static-address-list) | List all static backends on this account | | [update](#ngrok-api-backends-static-address-update) | Update static backend by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends static-address create Create a new static backend ### Usage ```sh theme={null} ngrok api backends static-address create [flags] ``` ### Flags | Flag | Description | | - | - | | `--address` | the address to forward to | | `--description` | human-readable description of this backend. Optional | | `--metadata` | arbitrary user-defined machine-readable data of this backend. Optional | | `--tls.enabled` | if TLS is checked | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends static-address delete Delete a static backend by ID. ### Usage ```sh theme={null} ngrok api backends static-address delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends static-address get Get detailed information about a static backend by ID ### Usage ```sh theme={null} ngrok api backends static-address get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends static-address list List all static backends on this account ### Usage ```sh theme={null} ngrok api backends static-address list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends static-address update Update static backend by ID ### Usage ```sh theme={null} ngrok api backends static-address update [flags] ``` ### Flags | Flag | Description | | - | - | | `--address` | the address to forward to | | `--description` | human-readable description of this backend. Optional | | `--metadata` | arbitrary user-defined machine-readable data of this backend. Optional | | `--tls.enabled` | if TLS is checked | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends tunnel-group A Tunnel Group Backend balances traffic among all online tunnels that match a label selector. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-backends-tunnel-group-create) | Create a new TunnelGroup backend | | [delete](#ngrok-api-backends-tunnel-group-delete) | Delete a TunnelGroup backend by ID. | | [get](#ngrok-api-backends-tunnel-group-get) | Get detailed information about a TunnelGroup... | | [list](#ngrok-api-backends-tunnel-group-list) | List all TunnelGroup backends on this account | | [update](#ngrok-api-backends-tunnel-group-update) | Update TunnelGroup backend by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends tunnel-group create Create a new TunnelGroup backend ### Usage ```sh theme={null} ngrok api backends tunnel-group create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this backend. Optional | | `--labels` | labels to watch for tunnels on, e.g. app->foo, dc->bar | | `--metadata` | arbitrary user-defined machine-readable data of this backend. Optional | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends tunnel-group delete Delete a TunnelGroup backend by ID. ### Usage ```sh theme={null} ngrok api backends tunnel-group delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends tunnel-group get Get detailed information about a TunnelGroup backend by ID ### Usage ```sh theme={null} ngrok api backends tunnel-group get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends tunnel-group list List all TunnelGroup backends on this account ### Usage ```sh theme={null} ngrok api backends tunnel-group list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends tunnel-group update Update TunnelGroup backend by ID ### Usage ```sh theme={null} ngrok api backends tunnel-group update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this backend. Optional | | `--labels` | labels to watch for tunnels on, e.g. app->foo, dc->bar | | `--metadata` | arbitrary user-defined machine-readable data of this backend. Optional | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends weighted A Weighted Backend balances traffic among the referenced backends. Traffic is assigned proportionally to each based on its weight. The percentage of traffic is calculated by dividing a backend's weight by the sum of all weights. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-backends-weighted-create) | Create a new Weighted backend | | [delete](#ngrok-api-backends-weighted-delete) | Delete a Weighted backend by ID. | | [get](#ngrok-api-backends-weighted-get) | Get detailed information about a Weighted... | | [list](#ngrok-api-backends-weighted-list) | List all Weighted backends on this account | | [update](#ngrok-api-backends-weighted-update) | Update Weighted backend by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends weighted create Create a new Weighted backend ### Usage ```sh theme={null} ngrok api backends weighted create [flags] ``` ### Flags | Flag | Description | | - | - | | `--backends` | the ids of the child backends to their weights \[0-10000] | | `--description` | human-readable description of this backend. Optional | | `--metadata` | arbitrary user-defined machine-readable data of this backend. Optional | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends weighted delete Delete a Weighted backend by ID. ### Usage ```sh theme={null} ngrok api backends weighted delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends weighted get Get detailed information about a Weighted backend by ID ### Usage ```sh theme={null} ngrok api backends weighted get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends weighted list List all Weighted backends on this account ### Usage ```sh theme={null} ngrok api backends weighted list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api backends weighted update Update Weighted backend by ID ### Usage ```sh theme={null} ngrok api backends weighted update [flags] ``` ### Flags | Flag | Description | | - | - | | `--backends` | the ids of the child backends to their weights \[0-10000] | | `--description` | human-readable description of this backend. Optional | | `--metadata` | arbitrary user-defined machine-readable data of this backend. Optional | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api bot-users ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-bot-users-create) | Create a new bot user | | [delete](#ngrok-api-bot-users-delete) | Delete a bot user by ID | | [get](#ngrok-api-bot-users-get) | Get the details of a Bot User by ID. | | [list](#ngrok-api-bot-users-list) | List all bot users in this account. | | [update](#ngrok-api-bot-users-update) | Update attributes of a bot user by ID. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api bot-users create Create a new bot user ### Usage ```sh theme={null} ngrok api bot-users create [flags] ``` ### Flags | Flag | Description | | - | - | | `--active` | whether or not the service user is active | | `--name` | human-readable name used to identify the service user | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api bot-users delete Delete a bot user by ID ### Usage ```sh theme={null} ngrok api bot-users delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api bot-users get Get the details of a Bot User by ID. ### Usage ```sh theme={null} ngrok api bot-users get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api bot-users list List all bot users in this account. ### Usage ```sh theme={null} ngrok api bot-users list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api bot-users update Update attributes of a bot user by ID. ### Usage ```sh theme={null} ngrok api bot-users update [flags] ``` ### Flags | Flag | Description | | - | - | | `--active` | whether or not the service user is active | | `--name` | human-readable name used to identify the service user | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api certificate-authorities Certificate Authorities are x509 certificates that are used to sign other x509 certificates. Attach a Certificate Authority to the Mutual TLS module to verify that the TLS certificate presented by a client has been signed by this CA. Certificate Authorities are used only for mTLS validation only and thus a private key is not included in the resource. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-certificate-authorities-create) | Upload a new Certificate Authority | | [delete](#ngrok-api-certificate-authorities-delete) | Delete a Certificate Authority | | [get](#ngrok-api-certificate-authorities-get) | Get detailed information about a certificate... | | [list](#ngrok-api-certificate-authorities-list) | List all Certificate Authority on this account | | [update](#ngrok-api-certificate-authorities-update) | Update attributes of a Certificate Authority... | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api certificate-authorities create Upload a new Certificate Authority ### Usage ```sh theme={null} ngrok api certificate-authorities create [flags] ``` ### Flags | Flag | Description | | - | - | | `--ca-pem` | raw PEM of the Certificate Authority | | `--description` | human-readable description of this Certificate Authority. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this Certificate Authority. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api certificate-authorities delete Delete a Certificate Authority ### Usage ```sh theme={null} ngrok api certificate-authorities delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api certificate-authorities get Get detailed information about a certificate authority ### Usage ```sh theme={null} ngrok api certificate-authorities get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api certificate-authorities list List all Certificate Authority on this account ### Usage ```sh theme={null} ngrok api certificate-authorities list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api certificate-authorities update Update attributes of a Certificate Authority by ID ### Usage ```sh theme={null} ngrok api certificate-authorities update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this Certificate Authority. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this Certificate Authority. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api credentials Tunnel Credentials are ngrok agent authtokens. They authorize the ngrok agent to connect the ngrok service as your account. They are installed with the ngrok config add-authtoken command or by specifying it in the ngrok.yml configuration file with the authtoken property. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-credentials-create) | Create a new tunnel authtoken credential | | [delete](#ngrok-api-credentials-delete) | Delete a tunnel authtoken credential by ID | | [get](#ngrok-api-credentials-get) | Get detailed information about a tunnel... | | [list](#ngrok-api-credentials-list) | List all tunnel authtoken credentials on this... | | [update](#ngrok-api-credentials-update) | Update attributes of an tunnel authtoken... | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api credentials create Create a new tunnel authtoken credential. This authtoken credential can be used to start a new tunnel session. The response to this API call is the only time the generated token is available. If you need it for future use, you must save it securely yourself. ### Usage ```sh theme={null} ngrok api credentials create [flags] ``` ### Flags | Flag | Description | | - | - | | `--acl` | optional list of ACL rules. If unspecified, the credential will have no restrictions. The only allowed ACL rule at this time is the bind rule. The bind rule allows the caller to restrict what domains, addresses, and labels the token is allowed to bind. For example, to allow the token to open a tunnel on example.ngrok.io your ACL would include the rule bind:example.ngrok.io. Bind rules for domains may specify a leading wildcard to match multiple domains with a common suffix. For example, you may specify a rule of bind:*.example.com which will allow x.example.com, y.example.com, *.example.com, etc. Bind rules for labels may specify a wildcard key and/or value to match multiple labels. For example, you may specify a rule of bind:*=example which will allow x=example, y=example, etc. A rule of '*' is equivalent to no acl at all and will explicitly permit all actions. | | `--description` | human-readable description of who or what will use the credential to authenticate. Optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this credential. Optional, max 4096 bytes. | | `--owner-email` | If supplied at credential creation, ownership will be assigned to the specified User. Only admins may specify an owner other than themselves. Only one of owner\_id, owner\_email, or scim\_user\_id may be specified. specified. | | `--owner-id` | If supplied at credential creation, ownership will be assigned to the specified User or Service User. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Service User. Accepts one of: User ID, User email, or SCIM User ID. | | `--precomputed-token` | Only authorized accounts may supply a pre-computed token that will be associated with the created credentials. | | `--scim-user-id` | If supplied at credential creation, ownership will be assigned to the specified SCIM User. Only admins may specify an owner other than themselves. Only one of owner\_id, owner\_email, or scim\_user\_id may be specified. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api credentials delete Delete a tunnel authtoken credential by ID ### Usage ```sh theme={null} ngrok api credentials delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api credentials get Get detailed information about a tunnel authtoken credential ### Usage ```sh theme={null} ngrok api credentials get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api credentials list List all tunnel authtoken credentials on this account ### Usage ```sh theme={null} ngrok api credentials list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api credentials update Update attributes of an tunnel authtoken credential by ID ### Usage ```sh theme={null} ngrok api credentials update [flags] ``` ### Flags | Flag | Description | | - | - | | `--acl` | optional list of ACL rules. If unspecified, the credential will have no restrictions. The only allowed ACL rule at this time is the bind rule. The bind rule allows the caller to restrict what domains, addresses, and labels the token is allowed to bind. For example, to allow the token to open a tunnel on example.ngrok.io your ACL would include the rule bind:example.ngrok.io. Bind rules for domains may specify a leading wildcard to match multiple domains with a common suffix. For example, you may specify a rule of bind:*.example.com which will allow x.example.com, y.example.com, *.example.com, etc. Bind rules for labels may specify a wildcard key and/or value to match multiple labels. For example, you may specify a rule of bind:*=example which will allow x=example, y=example, etc. A rule of '*' is equivalent to no acl at all and will explicitly permit all actions. | | `--description` | human-readable description of who or what will use the credential to authenticate. Optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this credential. Optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules ### SubCommands | Command | Description | | - | - | | [https-edge-mutual-tls](#ngrok-api-edge-modules-https-edge-mutual-tls) | | | [https-edge-route-backend](#ngrok-api-edge-modules-https-edge-route-backend) | | | [https-edge-route-circuit-breaker](#ngrok-api-edge-modules-https-edge-route-circuit-breaker) | | | [https-edge-route-compression](#ngrok-api-edge-modules-https-edge-route-compression) | | | [https-edge-route-ip-restriction](#ngrok-api-edge-modules-https-edge-route-ip-restriction) | | | [https-edge-route-oauth](#ngrok-api-edge-modules-https-edge-route-oauth) | | | [https-edge-route-oidc](#ngrok-api-edge-modules-https-edge-route-oidc) | | | [https-edge-route-request-headers](#ngrok-api-edge-modules-https-edge-route-request-headers) | | | [https-edge-route-response-headers](#ngrok-api-edge-modules-https-edge-route-response-headers) | | | [https-edge-route-saml](#ngrok-api-edge-modules-https-edge-route-saml) | | | [https-edge-route-traffic-policy](#ngrok-api-edge-modules-https-edge-route-traffic-policy) | | | [https-edge-route-user-agent-filter](#ngrok-api-edge-modules-https-edge-route-user-agent-filter) | | | [https-edge-route-webhook-verification](#ngrok-api-edge-modules-https-edge-route-webhook-verification) | | | [https-edge-route-websocket-tcp-converter](#ngrok-api-edge-modules-https-edge-route-websocket-tcp-converter) | | | [https-edge-tls-termination](#ngrok-api-edge-modules-https-edge-tls-termination) | | | [tcp-edge-backend](#ngrok-api-edge-modules-tcp-edge-backend) | | | [tcp-edge-ip-restriction](#ngrok-api-edge-modules-tcp-edge-ip-restriction) | | | [tcp-edge-traffic-policy](#ngrok-api-edge-modules-tcp-edge-traffic-policy) | | | [tls-edge-backend](#ngrok-api-edge-modules-tls-edge-backend) | | | [tls-edge-ip-restriction](#ngrok-api-edge-modules-tls-edge-ip-restriction) | | | [tls-edge-mutual-tls](#ngrok-api-edge-modules-tls-edge-mutual-tls) | | | [tls-edge-tls-termination](#ngrok-api-edge-modules-tls-edge-tls-termination) | | | [tls-edge-traffic-policy](#ngrok-api-edge-modules-tls-edge-traffic-policy) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-mutual-tls ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-mutual-tls-delete) | | | [get](#ngrok-api-edge-modules-https-edge-mutual-tls-get) | | | [replace](#ngrok-api-edge-modules-https-edge-mutual-tls-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-mutual-tls delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-mutual-tls delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-mutual-tls get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-mutual-tls get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-mutual-tls replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-mutual-tls replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.certificate-authority-ids` | list of certificate authorities that will be used to validate the TLS client certificate presented by the initiator of the TLS connection | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-backend ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-backend-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-backend-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-backend-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-backend delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-backend delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-backend get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-backend get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-backend replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-backend replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.backend-id` | backend to be used to back this endpoint | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-circuit-breaker ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-circuit-breaker-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-circuit-breaker-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-circuit-breaker-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-circuit-breaker delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-circuit-breaker delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-circuit-breaker get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-circuit-breaker get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-circuit-breaker replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-circuit-breaker replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.error-threshold-percentage` | Error threshold percentage should be between 0 - 1.0, not 0-100.0 | | `--module.num-buckets` | Integer number of buckets into which metrics are retained. Max 128. | | `--module.rolling-window` | Integer number of seconds in the statistical rolling window that metrics are retained for. | | `--module.tripped-duration` | Integer number of seconds after which the circuit is tripped to wait before re-evaluating upstream health | | `--module.volume-threshold` | Integer number of requests in a rolling window that will trip the circuit. Helpful if traffic volume is low. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-compression ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-compression-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-compression-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-compression-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-compression delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-compression delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-compression get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-compression get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-compression replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-compression replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-ip-restriction ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-ip-restriction-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-ip-restriction-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-ip-restriction-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-ip-restriction delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-ip-restriction delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-ip-restriction get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-ip-restriction get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-ip-restriction replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-ip-restriction replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-oauth ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-oauth-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-oauth-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-oauth-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-oauth delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-oauth delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-oauth get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-oauth get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-oauth replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-oauth replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.auth-check-interval` | Integer number of seconds after which ngrok guarantees it will refresh user state from the identity provider and recheck whether the user is still authorized to access the endpoint. This is the preferred tunable to use to enforce a minimum amount of time after which a revoked user will no longer be able to access the resource. | | `--module.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--module.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--module.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--module.provider.amazon.client-id` | | | `--module.provider.amazon.client-secret` | | | `--module.provider.amazon.email-addresses` | | | `--module.provider.amazon.email-domains` | | | `--module.provider.amazon.scopes` | | | `--module.provider.facebook.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--module.provider.facebook.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--module.provider.facebook.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.facebook.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.facebook.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--module.provider.github.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--module.provider.github.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--module.provider.github.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.github.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.github.organizations` | a list of github org identifiers. users who are members of any of the listed organizations will be allowed access. identifiers should be the organization's 'slug' | | `--module.provider.github.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--module.provider.github.teams` | a list of github teams identifiers. users will be allowed access to the endpoint if they are a member of any of these teams. identifiers should be in the 'slug' format qualified with the org name, e.g. org-name/team-name | | `--module.provider.gitlab.client-id` | | | `--module.provider.gitlab.client-secret` | | | `--module.provider.gitlab.email-addresses` | | | `--module.provider.gitlab.email-domains` | | | `--module.provider.gitlab.scopes` | | | `--module.provider.google.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--module.provider.google.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--module.provider.google.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.google.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.google.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--module.provider.linkedin.client-id` | | | `--module.provider.linkedin.client-secret` | | | `--module.provider.linkedin.email-addresses` | | | `--module.provider.linkedin.email-domains` | | | `--module.provider.linkedin.scopes` | | | `--module.provider.microsoft.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--module.provider.microsoft.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--module.provider.microsoft.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.microsoft.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.microsoft.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--module.provider.twitch.client-id` | | | `--module.provider.twitch.client-secret` | | | `--module.provider.twitch.email-addresses` | | | `--module.provider.twitch.email-domains` | | | `--module.provider.twitch.scopes` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-oidc ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-oidc-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-oidc-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-oidc-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-oidc delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-oidc delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-oidc get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-oidc get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-oidc replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-oidc replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.client-id` | The OIDC app's client ID and OIDC audience. | | `--module.client-secret` | The OIDC app's client secret. | | `--module.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--module.issuer` | URL of the OIDC "OpenID provider". This is the base URL used for discovery. | | `--module.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--module.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--module.scopes` | The set of scopes to request from the OIDC identity provider. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-request-headers ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-request-headers-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-request-headers-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-request-headers-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-request-headers delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-request-headers delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-request-headers get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-request-headers get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-request-headers replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-request-headers replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.add` | a map of header key to header value that will be injected into the HTTP Request before being sent to the upstream application server | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.remove` | a list of header names that will be removed from the HTTP Request before being sent to the upstream application server | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-response-headers ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-response-headers-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-response-headers-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-response-headers-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-response-headers delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-response-headers delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-response-headers get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-response-headers get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-response-headers replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-response-headers replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.add` | a map of header key to header value that will be injected into the HTTP Response returned to the HTTP client | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.remove` | a list of header names that will be removed from the HTTP Response returned to the HTTP client | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-saml ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-saml-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-saml-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-saml-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-saml delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-saml delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-saml get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-saml get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-saml replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-saml replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.allow-idp-initiated` | If true, the IdP may initiate a login directly (e.g. the user does not need to visit the endpoint first and then be redirected). The IdP should set the RelayState parameter to the target URL of the resource they want the user to be redirected to after the SAML login assertion has been processed. | | `--module.authorized-groups` | If present, only users who are a member of one of the listed groups may access the target endpoint. | | `--module.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.force-authn` | If true, indicates that whenever we redirect a user to the IdP for authentication that the IdP must prompt the user for authentication credentials even if the user already has a valid session with the IdP. | | `--module.idp-metadata` | The full XML IdP EntityDescriptor. Your IdP may provide this to you as a a file to download or as a URL. | | `--module.idp-metadata-url` | The IdP's metadata URL which returns the XML IdP EntityDescriptor. The IdP's metadata URL specifies how to connect to the IdP as well as its public key which is then used to validate the signature on incoming SAML assertions to the ACS endpoint. | | `--module.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--module.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--module.nameid-format` | Defines the name identifier format the SP expects the IdP to use in its assertions to identify subjects. If unspecified, a default value of urn:oasis:names:tc:SAML:2.0:nameid-format:persistent will be used. A subset of the allowed values enumerated by the SAML specification are supported. | | `--module.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-traffic-policy ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-traffic-policy-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-traffic-policy-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-traffic-policy-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-traffic-policy delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-traffic-policy delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-traffic-policy get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-traffic-policy get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-traffic-policy replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-traffic-policy replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.value` | the traffic policy that should be applied to the traffic on your endpoint. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-user-agent-filter ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-user-agent-filter-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-user-agent-filter-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-user-agent-filter-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-user-agent-filter delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-user-agent-filter delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-user-agent-filter get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-user-agent-filter get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-user-agent-filter replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-user-agent-filter replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.allow` | | | `--module.deny` | | | `--module.enabled` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-webhook-verification ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-webhook-verification-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-webhook-verification-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-webhook-verification-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-webhook-verification delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-webhook-verification delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-webhook-verification get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-webhook-verification get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-webhook-verification replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-webhook-verification replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.provider` | a string indicating which webhook provider will be sending webhooks to this endpoint. Value must be one of the supported providers defined at [https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification) ([https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification)) | | `--module.secret` | a string secret used to validate requests from the given provider. All providers except AWS SNS require a secret | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-websocket-tcp-converter ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-route-websocket-tcp-converter-delete) | | | [get](#ngrok-api-edge-modules-https-edge-route-websocket-tcp-converter-get) | | | [replace](#ngrok-api-edge-modules-https-edge-route-websocket-tcp-converter-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-websocket-tcp-converter delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-websocket-tcp-converter delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-websocket-tcp-converter get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-websocket-tcp-converter get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-route-websocket-tcp-converter replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-route-websocket-tcp-converter replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-tls-termination ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-https-edge-tls-termination-delete) | | | [get](#ngrok-api-edge-modules-https-edge-tls-termination-get) | | | [replace](#ngrok-api-edge-modules-https-edge-tls-termination-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-tls-termination delete ### Usage ```sh theme={null} ngrok api edge-modules https-edge-tls-termination delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-tls-termination get ### Usage ```sh theme={null} ngrok api edge-modules https-edge-tls-termination get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules https-edge-tls-termination replace ### Usage ```sh theme={null} ngrok api edge-modules https-edge-tls-termination replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.min-version` | The minimum TLS version used for termination and advertised to the client during the TLS handshake. if unspecified, ngrok will choose an industry-safe default. This value must be null if terminate\_at is set to upstream. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-backend ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-tcp-edge-backend-delete) | | | [get](#ngrok-api-edge-modules-tcp-edge-backend-get) | | | [replace](#ngrok-api-edge-modules-tcp-edge-backend-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-backend delete ### Usage ```sh theme={null} ngrok api edge-modules tcp-edge-backend delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-backend get ### Usage ```sh theme={null} ngrok api edge-modules tcp-edge-backend get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-backend replace ### Usage ```sh theme={null} ngrok api edge-modules tcp-edge-backend replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.backend-id` | backend to be used to back this endpoint | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-ip-restriction ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-tcp-edge-ip-restriction-delete) | | | [get](#ngrok-api-edge-modules-tcp-edge-ip-restriction-get) | | | [replace](#ngrok-api-edge-modules-tcp-edge-ip-restriction-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-ip-restriction delete ### Usage ```sh theme={null} ngrok api edge-modules tcp-edge-ip-restriction delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-ip-restriction get ### Usage ```sh theme={null} ngrok api edge-modules tcp-edge-ip-restriction get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-ip-restriction replace ### Usage ```sh theme={null} ngrok api edge-modules tcp-edge-ip-restriction replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-traffic-policy ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-tcp-edge-traffic-policy-delete) | | | [get](#ngrok-api-edge-modules-tcp-edge-traffic-policy-get) | | | [replace](#ngrok-api-edge-modules-tcp-edge-traffic-policy-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-traffic-policy delete ### Usage ```sh theme={null} ngrok api edge-modules tcp-edge-traffic-policy delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-traffic-policy get ### Usage ```sh theme={null} ngrok api edge-modules tcp-edge-traffic-policy get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tcp-edge-traffic-policy replace ### Usage ```sh theme={null} ngrok api edge-modules tcp-edge-traffic-policy replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.value` | the traffic policy that should be applied to the traffic on your endpoint. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-backend ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-tls-edge-backend-delete) | | | [get](#ngrok-api-edge-modules-tls-edge-backend-get) | | | [replace](#ngrok-api-edge-modules-tls-edge-backend-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-backend delete ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-backend delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-backend get ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-backend get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-backend replace ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-backend replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.backend-id` | backend to be used to back this endpoint | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-ip-restriction ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-tls-edge-ip-restriction-delete) | | | [get](#ngrok-api-edge-modules-tls-edge-ip-restriction-get) | | | [replace](#ngrok-api-edge-modules-tls-edge-ip-restriction-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-ip-restriction delete ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-ip-restriction delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-ip-restriction get ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-ip-restriction get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-ip-restriction replace ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-ip-restriction replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-mutual-tls ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-tls-edge-mutual-tls-delete) | | | [get](#ngrok-api-edge-modules-tls-edge-mutual-tls-get) | | | [replace](#ngrok-api-edge-modules-tls-edge-mutual-tls-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-mutual-tls delete ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-mutual-tls delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-mutual-tls get ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-mutual-tls get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-mutual-tls replace ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-mutual-tls replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.certificate-authority-ids` | list of certificate authorities that will be used to validate the TLS client certificate presented by the initiator of the TLS connection | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-tls-termination ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-tls-edge-tls-termination-delete) | | | [get](#ngrok-api-edge-modules-tls-edge-tls-termination-get) | | | [replace](#ngrok-api-edge-modules-tls-edge-tls-termination-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-tls-termination delete ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-tls-termination delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-tls-termination get ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-tls-termination get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-tls-termination replace ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-tls-termination replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.min-version` | The minimum TLS version used for termination and advertised to the client during the TLS handshake. if unspecified, ngrok will choose an industry-safe default. This value must be null if terminate\_at is set to upstream. | | `--module.terminate-at` | edge if the ngrok edge should terminate TLS traffic, upstream if TLS traffic should be passed through to the upstream ngrok agent / application server for termination. if upstream is chosen, most other modules will be disallowed because they rely on the ngrok edge being able to access the underlying traffic. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-traffic-policy ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-edge-modules-tls-edge-traffic-policy-delete) | | | [get](#ngrok-api-edge-modules-tls-edge-traffic-policy-get) | | | [replace](#ngrok-api-edge-modules-tls-edge-traffic-policy-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-traffic-policy delete ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-traffic-policy delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-traffic-policy get ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-traffic-policy get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edge-modules tls-edge-traffic-policy replace ### Usage ```sh theme={null} ngrok api edge-modules tls-edge-traffic-policy replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.value` | the traffic policy that should be applied to the traffic on your endpoint. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges ### SubCommands | Command | Description | | - | - | | [https](#ngrok-api-edges-https) | | | [https-routes](#ngrok-api-edges-https-routes) | | | [tcp](#ngrok-api-edges-tcp) | | | [tls](#ngrok-api-edges-tls) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-edges-https-create) | Create an HTTPS Edge | | [delete](#ngrok-api-edges-https-delete) | Delete an HTTPS Edge by ID | | [get](#ngrok-api-edges-https-get) | Get an HTTPS Edge by ID | | [list](#ngrok-api-edges-https-list) | Returns a list of all HTTPS Edges on this account | | [update](#ngrok-api-edges-https-update) | Updates an HTTPS Edge by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https create Create an HTTPS Edge ### Usage ```sh theme={null} ngrok api edges https create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of what this edge will be used for; optional, max 255 bytes. | | `--hostports` | hostports served by this edge | | `--metadata` | arbitrary user-defined machine-readable data of this edge; optional, max 4096 bytes. | | `--mutual-tls.certificate-authority-ids` | list of certificate authorities that will be used to validate the TLS client certificate presented by the initiator of the TLS connection | | `--mutual-tls.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--tls-termination.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--tls-termination.min-version` | The minimum TLS version used for termination and advertised to the client during the TLS handshake. if unspecified, ngrok will choose an industry-safe default. This value must be null if terminate\_at is set to upstream. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https delete Delete an HTTPS Edge by ID ### Usage ```sh theme={null} ngrok api edges https delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https get Get an HTTPS Edge by ID ### Usage ```sh theme={null} ngrok api edges https get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https list Returns a list of all HTTPS Edges on this account ### Usage ```sh theme={null} ngrok api edges https list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https update Updates an HTTPS Edge by ID. If a module is not specified in the update, it will not be modified. However, each module configuration that is specified will completely replace the existing value. There is no way to delete an existing module via this API, instead use the delete module API. ### Usage ```sh theme={null} ngrok api edges https update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of what this edge will be used for; optional, max 255 bytes. | | `--hostports` | hostports served by this edge | | `--metadata` | arbitrary user-defined machine-readable data of this edge; optional, max 4096 bytes. | | `--mutual-tls.certificate-authority-ids` | list of certificate authorities that will be used to validate the TLS client certificate presented by the initiator of the TLS connection | | `--mutual-tls.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--tls-termination.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--tls-termination.min-version` | The minimum TLS version used for termination and advertised to the client during the TLS handshake. if unspecified, ngrok will choose an industry-safe default. This value must be null if terminate\_at is set to upstream. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https-routes ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-edges-https-routes-create) | Create an HTTPS Edge Route | | [delete](#ngrok-api-edges-https-routes-delete) | Delete an HTTPS Edge Route by ID | | [get](#ngrok-api-edges-https-routes-get) | Get an HTTPS Edge Route by ID | | [update](#ngrok-api-edges-https-routes-update) | Updates an HTTPS Edge Route by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https-routes create Create an HTTPS Edge Route ### Usage ```sh theme={null} ngrok api edges https-routes create [flags] ``` ### Flags | Flag | Description | | - | - | | `--backend.backend-id` | backend to be used to back this endpoint | | `--backend.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--circuit-breaker.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--circuit-breaker.error-threshold-percentage` | Error threshold percentage should be between 0 - 1.0, not 0-100.0 | | `--circuit-breaker.num-buckets` | Integer number of buckets into which metrics are retained. Max 128. | | `--circuit-breaker.rolling-window` | Integer number of seconds in the statistical rolling window that metrics are retained for. | | `--circuit-breaker.tripped-duration` | Integer number of seconds after which the circuit is tripped to wait before re-evaluating upstream health | | `--circuit-breaker.volume-threshold` | Integer number of requests in a rolling window that will trip the circuit. Helpful if traffic volume is low. | | `--compression.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--description` | human-readable description of what this edge will be used for; optional, max 255 bytes. | | `--ip-restriction.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--ip-restriction.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--match` | Route selector: "/blog" or "example.com" or "example.com/blog" | | `--match-type` | Type of match to use for this route. Valid values are "exact\_path" and "path\_prefix". | | `--metadata` | arbitrary user-defined machine-readable data of this edge. Optional, max 4096 bytes. | | `--oauth.auth-check-interval` | Integer number of seconds after which ngrok guarantees it will refresh user state from the identity provider and recheck whether the user is still authorized to access the endpoint. This is the preferred tunable to use to enforce a minimum amount of time after which a revoked user will no longer be able to access the resource. | | `--oauth.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--oauth.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--oauth.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--oauth.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--oauth.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--oauth.provider.amazon.client-id` | | | `--oauth.provider.amazon.client-secret` | | | `--oauth.provider.amazon.email-addresses` | | | `--oauth.provider.amazon.email-domains` | | | `--oauth.provider.amazon.scopes` | | | `--oauth.provider.facebook.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.facebook.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.facebook.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.facebook.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.facebook.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.github.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.github.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.github.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.github.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.github.organizations` | a list of github org identifiers. users who are members of any of the listed organizations will be allowed access. identifiers should be the organization's 'slug' | | `--oauth.provider.github.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.github.teams` | a list of github teams identifiers. users will be allowed access to the endpoint if they are a member of any of these teams. identifiers should be in the 'slug' format qualified with the org name, e.g. org-name/team-name | | `--oauth.provider.gitlab.client-id` | | | `--oauth.provider.gitlab.client-secret` | | | `--oauth.provider.gitlab.email-addresses` | | | `--oauth.provider.gitlab.email-domains` | | | `--oauth.provider.gitlab.scopes` | | | `--oauth.provider.google.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.google.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.google.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.google.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.google.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.linkedin.client-id` | | | `--oauth.provider.linkedin.client-secret` | | | `--oauth.provider.linkedin.email-addresses` | | | `--oauth.provider.linkedin.email-domains` | | | `--oauth.provider.linkedin.scopes` | | | `--oauth.provider.microsoft.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.microsoft.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.microsoft.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.microsoft.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.microsoft.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.twitch.client-id` | | | `--oauth.provider.twitch.client-secret` | | | `--oauth.provider.twitch.email-addresses` | | | `--oauth.provider.twitch.email-domains` | | | `--oauth.provider.twitch.scopes` | | | `--oidc.client-id` | The OIDC app's client ID and OIDC audience. | | `--oidc.client-secret` | The OIDC app's client secret. | | `--oidc.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--oidc.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--oidc.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--oidc.issuer` | URL of the OIDC "OpenID provider". This is the base URL used for discovery. | | `--oidc.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--oidc.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--oidc.scopes` | The set of scopes to request from the OIDC identity provider. | | `--request-headers.add` | a map of header key to header value that will be injected into the HTTP Request before being sent to the upstream application server | | `--request-headers.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--request-headers.remove` | a list of header names that will be removed from the HTTP Request before being sent to the upstream application server | | `--response-headers.add` | a map of header key to header value that will be injected into the HTTP Response returned to the HTTP client | | `--response-headers.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--response-headers.remove` | a list of header names that will be removed from the HTTP Response returned to the HTTP client | | `--saml.allow-idp-initiated` | If true, the IdP may initiate a login directly (e.g. the user does not need to visit the endpoint first and then be redirected). The IdP should set the RelayState parameter to the target URL of the resource they want the user to be redirected to after the SAML login assertion has been processed. | | `--saml.authorized-groups` | If present, only users who are a member of one of the listed groups may access the target endpoint. | | `--saml.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--saml.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--saml.force-authn` | If true, indicates that whenever we redirect a user to the IdP for authentication that the IdP must prompt the user for authentication credentials even if the user already has a valid session with the IdP. | | `--saml.idp-metadata` | The full XML IdP EntityDescriptor. Your IdP may provide this to you as a a file to download or as a URL. | | `--saml.idp-metadata-url` | The IdP's metadata URL which returns the XML IdP EntityDescriptor. The IdP's metadata URL specifies how to connect to the IdP as well as its public key which is then used to validate the signature on incoming SAML assertions to the ACS endpoint. | | `--saml.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--saml.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--saml.nameid-format` | Defines the name identifier format the SP expects the IdP to use in its assertions to identify subjects. If unspecified, a default value of urn:oasis:names:tc:SAML:2.0:nameid-format:persistent will be used. A subset of the allowed values enumerated by the SAML specification are supported. | | `--saml.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--traffic-policy.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--traffic-policy.value` | the traffic policy that should be applied to the traffic on your endpoint. | | `--user-agent-filter.allow` | | | `--user-agent-filter.deny` | | | `--user-agent-filter.enabled` | | | `--webhook-verification.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--webhook-verification.provider` | a string indicating which webhook provider will be sending webhooks to this endpoint. Value must be one of the supported providers defined at [https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification) ([https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification)) | | `--webhook-verification.secret` | a string secret used to validate requests from the given provider. All providers except AWS SNS require a secret | | `--websocket-tcp-converter.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https-routes delete Delete an HTTPS Edge Route by ID ### Usage ```sh theme={null} ngrok api edges https-routes delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https-routes get Get an HTTPS Edge Route by ID ### Usage ```sh theme={null} ngrok api edges https-routes get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges https-routes update Updates an HTTPS Edge Route by ID. If a module is not specified in the update, it will not be modified. However, each module configuration that is specified will completely replace the existing value. There is no way to delete an existing module via this API, instead use the delete module API. ### Usage ```sh theme={null} ngrok api edges https-routes update [flags] ``` ### Flags | Flag | Description | | - | - | | `--backend.backend-id` | backend to be used to back this endpoint | | `--backend.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--circuit-breaker.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--circuit-breaker.error-threshold-percentage` | Error threshold percentage should be between 0 - 1.0, not 0-100.0 | | `--circuit-breaker.num-buckets` | Integer number of buckets into which metrics are retained. Max 128. | | `--circuit-breaker.rolling-window` | Integer number of seconds in the statistical rolling window that metrics are retained for. | | `--circuit-breaker.tripped-duration` | Integer number of seconds after which the circuit is tripped to wait before re-evaluating upstream health | | `--circuit-breaker.volume-threshold` | Integer number of requests in a rolling window that will trip the circuit. Helpful if traffic volume is low. | | `--compression.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--description` | human-readable description of what this edge will be used for; optional, max 255 bytes. | | `--ip-restriction.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--ip-restriction.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--match` | Route selector: "/blog" or "example.com" or "example.com/blog" | | `--match-type` | Type of match to use for this route. Valid values are "exact\_path" and "path\_prefix". | | `--metadata` | arbitrary user-defined machine-readable data of this edge. Optional, max 4096 bytes. | | `--oauth.auth-check-interval` | Integer number of seconds after which ngrok guarantees it will refresh user state from the identity provider and recheck whether the user is still authorized to access the endpoint. This is the preferred tunable to use to enforce a minimum amount of time after which a revoked user will no longer be able to access the resource. | | `--oauth.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--oauth.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--oauth.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--oauth.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--oauth.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--oauth.provider.amazon.client-id` | | | `--oauth.provider.amazon.client-secret` | | | `--oauth.provider.amazon.email-addresses` | | | `--oauth.provider.amazon.email-domains` | | | `--oauth.provider.amazon.scopes` | | | `--oauth.provider.facebook.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.facebook.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.facebook.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.facebook.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.facebook.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.github.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.github.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.github.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.github.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.github.organizations` | a list of github org identifiers. users who are members of any of the listed organizations will be allowed access. identifiers should be the organization's 'slug' | | `--oauth.provider.github.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.github.teams` | a list of github teams identifiers. users will be allowed access to the endpoint if they are a member of any of these teams. identifiers should be in the 'slug' format qualified with the org name, e.g. org-name/team-name | | `--oauth.provider.gitlab.client-id` | | | `--oauth.provider.gitlab.client-secret` | | | `--oauth.provider.gitlab.email-addresses` | | | `--oauth.provider.gitlab.email-domains` | | | `--oauth.provider.gitlab.scopes` | | | `--oauth.provider.google.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.google.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.google.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.google.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.google.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.linkedin.client-id` | | | `--oauth.provider.linkedin.client-secret` | | | `--oauth.provider.linkedin.email-addresses` | | | `--oauth.provider.linkedin.email-domains` | | | `--oauth.provider.linkedin.scopes` | | | `--oauth.provider.microsoft.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.microsoft.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.microsoft.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.microsoft.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.microsoft.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.twitch.client-id` | | | `--oauth.provider.twitch.client-secret` | | | `--oauth.provider.twitch.email-addresses` | | | `--oauth.provider.twitch.email-domains` | | | `--oauth.provider.twitch.scopes` | | | `--oidc.client-id` | The OIDC app's client ID and OIDC audience. | | `--oidc.client-secret` | The OIDC app's client secret. | | `--oidc.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--oidc.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--oidc.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--oidc.issuer` | URL of the OIDC "OpenID provider". This is the base URL used for discovery. | | `--oidc.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--oidc.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--oidc.scopes` | The set of scopes to request from the OIDC identity provider. | | `--request-headers.add` | a map of header key to header value that will be injected into the HTTP Request before being sent to the upstream application server | | `--request-headers.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--request-headers.remove` | a list of header names that will be removed from the HTTP Request before being sent to the upstream application server | | `--response-headers.add` | a map of header key to header value that will be injected into the HTTP Response returned to the HTTP client | | `--response-headers.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--response-headers.remove` | a list of header names that will be removed from the HTTP Response returned to the HTTP client | | `--saml.allow-idp-initiated` | If true, the IdP may initiate a login directly (e.g. the user does not need to visit the endpoint first and then be redirected). The IdP should set the RelayState parameter to the target URL of the resource they want the user to be redirected to after the SAML login assertion has been processed. | | `--saml.authorized-groups` | If present, only users who are a member of one of the listed groups may access the target endpoint. | | `--saml.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--saml.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--saml.force-authn` | If true, indicates that whenever we redirect a user to the IdP for authentication that the IdP must prompt the user for authentication credentials even if the user already has a valid session with the IdP. | | `--saml.idp-metadata` | The full XML IdP EntityDescriptor. Your IdP may provide this to you as a a file to download or as a URL. | | `--saml.idp-metadata-url` | The IdP's metadata URL which returns the XML IdP EntityDescriptor. The IdP's metadata URL specifies how to connect to the IdP as well as its public key which is then used to validate the signature on incoming SAML assertions to the ACS endpoint. | | `--saml.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--saml.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--saml.nameid-format` | Defines the name identifier format the SP expects the IdP to use in its assertions to identify subjects. If unspecified, a default value of urn:oasis:names:tc:SAML:2.0:nameid-format:persistent will be used. A subset of the allowed values enumerated by the SAML specification are supported. | | `--saml.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--traffic-policy.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--traffic-policy.value` | the traffic policy that should be applied to the traffic on your endpoint. | | `--user-agent-filter.allow` | | | `--user-agent-filter.deny` | | | `--user-agent-filter.enabled` | | | `--webhook-verification.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--webhook-verification.provider` | a string indicating which webhook provider will be sending webhooks to this endpoint. Value must be one of the supported providers defined at [https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification) ([https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification)) | | `--webhook-verification.secret` | a string secret used to validate requests from the given provider. All providers except AWS SNS require a secret | | `--websocket-tcp-converter.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tcp ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-edges-tcp-create) | Create a TCP Edge | | [delete](#ngrok-api-edges-tcp-delete) | Delete a TCP Edge by ID | | [get](#ngrok-api-edges-tcp-get) | Get a TCP Edge by ID | | [list](#ngrok-api-edges-tcp-list) | Returns a list of all TCP Edges on this account | | [update](#ngrok-api-edges-tcp-update) | Updates a TCP Edge by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tcp create Create a TCP Edge ### Usage ```sh theme={null} ngrok api edges tcp create [flags] ``` ### Flags | Flag | Description | | - | - | | `--backend.backend-id` | backend to be used to back this endpoint | | `--backend.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--description` | human-readable description of what this edge will be used for; optional, max 255 bytes. | | `--hostports` | hostports served by this edge | | `--ip-restriction.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--ip-restriction.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--metadata` | arbitrary user-defined machine-readable data of this edge. Optional, max 4096 bytes. | | `--traffic-policy.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--traffic-policy.value` | the traffic policy that should be applied to the traffic on your endpoint. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tcp delete Delete a TCP Edge by ID ### Usage ```sh theme={null} ngrok api edges tcp delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tcp get Get a TCP Edge by ID ### Usage ```sh theme={null} ngrok api edges tcp get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tcp list Returns a list of all TCP Edges on this account ### Usage ```sh theme={null} ngrok api edges tcp list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tcp update Updates a TCP Edge by ID. If a module is not specified in the update, it will not be modified. However, each module configuration that is specified will completely replace the existing value. There is no way to delete an existing module via this API, instead use the delete module API. ### Usage ```sh theme={null} ngrok api edges tcp update [flags] ``` ### Flags | Flag | Description | | - | - | | `--backend.backend-id` | backend to be used to back this endpoint | | `--backend.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--description` | human-readable description of what this edge will be used for; optional, max 255 bytes. | | `--hostports` | hostports served by this edge | | `--ip-restriction.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--ip-restriction.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--metadata` | arbitrary user-defined machine-readable data of this edge. Optional, max 4096 bytes. | | `--traffic-policy.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--traffic-policy.value` | the traffic policy that should be applied to the traffic on your endpoint. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tls ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-edges-tls-create) | Create a TLS Edge | | [delete](#ngrok-api-edges-tls-delete) | Delete a TLS Edge by ID | | [get](#ngrok-api-edges-tls-get) | Get a TLS Edge by ID | | [list](#ngrok-api-edges-tls-list) | Returns a list of all TLS Edges on this account | | [update](#ngrok-api-edges-tls-update) | Updates a TLS Edge by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tls create Create a TLS Edge ### Usage ```sh theme={null} ngrok api edges tls create [flags] ``` ### Flags | Flag | Description | | - | - | | `--backend.backend-id` | backend to be used to back this endpoint | | `--backend.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--description` | human-readable description of what this edge will be used for; optional, max 255 bytes. | | `--hostports` | hostports served by this edge | | `--ip-restriction.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--ip-restriction.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--metadata` | arbitrary user-defined machine-readable data of this edge. Optional, max 4096 bytes. | | `--mutual-tls.certificate-authority-ids` | list of certificate authorities that will be used to validate the TLS client certificate presented by the initiator of the TLS connection | | `--mutual-tls.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--tls-termination.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--tls-termination.min-version` | The minimum TLS version used for termination and advertised to the client during the TLS handshake. if unspecified, ngrok will choose an industry-safe default. This value must be null if terminate\_at is set to upstream. | | `--tls-termination.terminate-at` | edge if the ngrok edge should terminate TLS traffic, upstream if TLS traffic should be passed through to the upstream ngrok agent / application server for termination. if upstream is chosen, most other modules will be disallowed because they rely on the ngrok edge being able to access the underlying traffic. | | `--traffic-policy.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--traffic-policy.value` | the traffic policy that should be applied to the traffic on your endpoint. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tls delete Delete a TLS Edge by ID ### Usage ```sh theme={null} ngrok api edges tls delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tls get Get a TLS Edge by ID ### Usage ```sh theme={null} ngrok api edges tls get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tls list Returns a list of all TLS Edges on this account ### Usage ```sh theme={null} ngrok api edges tls list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api edges tls update Updates a TLS Edge by ID. If a module is not specified in the update, it will not be modified. However, each module configuration that is specified will completely replace the existing value. There is no way to delete an existing module via this API, instead use the delete module API. ### Usage ```sh theme={null} ngrok api edges tls update [flags] ``` ### Flags | Flag | Description | | - | - | | `--backend.backend-id` | backend to be used to back this endpoint | | `--backend.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--description` | human-readable description of what this edge will be used for; optional, max 255 bytes. | | `--hostports` | hostports served by this edge | | `--ip-restriction.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--ip-restriction.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--metadata` | arbitrary user-defined machine-readable data of this edge. Optional, max 4096 bytes. | | `--mutual-tls.certificate-authority-ids` | list of certificate authorities that will be used to validate the TLS client certificate presented by the initiator of the TLS connection | | `--mutual-tls.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--tls-termination.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--tls-termination.min-version` | The minimum TLS version used for termination and advertised to the client during the TLS handshake. if unspecified, ngrok will choose an industry-safe default. This value must be null if terminate\_at is set to upstream. | | `--tls-termination.terminate-at` | edge if the ngrok edge should terminate TLS traffic, upstream if TLS traffic should be passed through to the upstream ngrok agent / application server for termination. if upstream is chosen, most other modules will be disallowed because they rely on the ngrok edge being able to access the underlying traffic. | | `--traffic-policy.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--traffic-policy.value` | the traffic policy that should be applied to the traffic on your endpoint. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoint-configurations Endpoint Configurations are a reusable group of modules that encapsulate how traffic to a domain or address is handled. Endpoint configurations are only applied to Domains and TCP Addresses they have been attached to. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-endpoint-configurations-create) | Create a new endpoint configuration | | [delete](#ngrok-api-endpoint-configurations-delete) | Delete an endpoint configuration | | [get](#ngrok-api-endpoint-configurations-get) | Returns detailed information about an endpoint... | | [list](#ngrok-api-endpoint-configurations-list) | Returns a list of all endpoint configurations... | | [update](#ngrok-api-endpoint-configurations-update) | Updates an endpoint configuration | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoint-configurations create Create a new endpoint configuration ### Usage ```sh theme={null} ngrok api endpoint-configurations create [flags] ``` ### Flags | Flag | Description | | - | - | | `--backend.backend-id` | backend to be used to back this endpoint | | `--backend.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--basic-auth.allow-options` | true or false indicating whether to allow OPTIONS requests through without authentication which is necessary for CORS. default is false | | `--basic-auth.auth-provider-id` | determines how the basic auth credentials are validated. Currently only the value agent is supported which means that credentials will be validated against the username and password specified by the ngrok agent's --basic-auth flag, if any. | | `--basic-auth.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--basic-auth.realm` | an arbitrary string to be specified in as the 'realm' value in the WWW-Authenticate header. default is ngrok | | `--circuit-breaker.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--circuit-breaker.error-threshold-percentage` | Error threshold percentage should be between 0 - 1.0, not 0-100.0 | | `--circuit-breaker.num-buckets` | Integer number of buckets into which metrics are retained. Max 128. | | `--circuit-breaker.rolling-window` | Integer number of seconds in the statistical rolling window that metrics are retained for. | | `--circuit-breaker.tripped-duration` | Integer number of seconds after which the circuit is tripped to wait before re-evaluating upstream health | | `--circuit-breaker.volume-threshold` | Integer number of requests in a rolling window that will trip the circuit. Helpful if traffic volume is low. | | `--compression.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--description` | human-readable description of what this endpoint configuration will be do when applied or what traffic it will be applied to. Optional, max 255 bytes | | `--ip-policy.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--ip-policy.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--metadata` | arbitrary user-defined machine-readable data of this endpoint configuration. Optional, max 4096 bytes. | | `--mutual-tls.certificate-authority-ids` | list of certificate authorities that will be used to validate the TLS client certificate presented by the initiator of the TLS connection | | `--mutual-tls.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--oauth.auth-check-interval` | Integer number of seconds after which ngrok guarantees it will refresh user state from the identity provider and recheck whether the user is still authorized to access the endpoint. This is the preferred tunable to use to enforce a minimum amount of time after which a revoked user will no longer be able to access the resource. | | `--oauth.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--oauth.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--oauth.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--oauth.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--oauth.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--oauth.provider.amazon.client-id` | | | `--oauth.provider.amazon.client-secret` | | | `--oauth.provider.amazon.email-addresses` | | | `--oauth.provider.amazon.email-domains` | | | `--oauth.provider.amazon.scopes` | | | `--oauth.provider.facebook.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.facebook.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.facebook.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.facebook.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.facebook.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.github.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.github.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.github.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.github.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.github.organizations` | a list of github org identifiers. users who are members of any of the listed organizations will be allowed access. identifiers should be the organization's 'slug' | | `--oauth.provider.github.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.github.teams` | a list of github teams identifiers. users will be allowed access to the endpoint if they are a member of any of these teams. identifiers should be in the 'slug' format qualified with the org name, e.g. org-name/team-name | | `--oauth.provider.gitlab.client-id` | | | `--oauth.provider.gitlab.client-secret` | | | `--oauth.provider.gitlab.email-addresses` | | | `--oauth.provider.gitlab.email-domains` | | | `--oauth.provider.gitlab.scopes` | | | `--oauth.provider.google.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.google.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.google.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.google.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.google.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.linkedin.client-id` | | | `--oauth.provider.linkedin.client-secret` | | | `--oauth.provider.linkedin.email-addresses` | | | `--oauth.provider.linkedin.email-domains` | | | `--oauth.provider.linkedin.scopes` | | | `--oauth.provider.microsoft.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.microsoft.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.microsoft.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.microsoft.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.microsoft.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.twitch.client-id` | | | `--oauth.provider.twitch.client-secret` | | | `--oauth.provider.twitch.email-addresses` | | | `--oauth.provider.twitch.email-domains` | | | `--oauth.provider.twitch.scopes` | | | `--oidc.client-id` | The OIDC app's client ID and OIDC audience. | | `--oidc.client-secret` | The OIDC app's client secret. | | `--oidc.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--oidc.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--oidc.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--oidc.issuer` | URL of the OIDC "OpenID provider". This is the base URL used for discovery. | | `--oidc.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--oidc.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--oidc.scopes` | The set of scopes to request from the OIDC identity provider. | | `--request-headers.add` | a map of header key to header value that will be injected into the HTTP Request before being sent to the upstream application server | | `--request-headers.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--request-headers.remove` | a list of header names that will be removed from the HTTP Request before being sent to the upstream application server | | `--response-headers.add` | a map of header key to header value that will be injected into the HTTP Response returned to the HTTP client | | `--response-headers.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--response-headers.remove` | a list of header names that will be removed from the HTTP Response returned to the HTTP client | | `--saml.allow-idp-initiated` | If true, the IdP may initiate a login directly (e.g. the user does not need to visit the endpoint first and then be redirected). The IdP should set the RelayState parameter to the target URL of the resource they want the user to be redirected to after the SAML login assertion has been processed. | | `--saml.authorized-groups` | If present, only users who are a member of one of the listed groups may access the target endpoint. | | `--saml.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--saml.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--saml.force-authn` | If true, indicates that whenever we redirect a user to the IdP for authentication that the IdP must prompt the user for authentication credentials even if the user already has a valid session with the IdP. | | `--saml.idp-metadata` | The full XML IdP EntityDescriptor. Your IdP may provide this to you as a a file to download or as a URL. | | `--saml.idp-metadata-url` | The IdP's metadata URL which returns the XML IdP EntityDescriptor. The IdP's metadata URL specifies how to connect to the IdP as well as its public key which is then used to validate the signature on incoming SAML assertions to the ACS endpoint. | | `--saml.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--saml.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--saml.nameid-format` | Defines the name identifier format the SP expects the IdP to use in its assertions to identify subjects. If unspecified, a default value of urn:oasis:names:tc:SAML:2.0:nameid-format:persistent will be used. A subset of the allowed values enumerated by the SAML specification are supported. | | `--saml.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--tls-termination.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--tls-termination.min-version` | The minimum TLS version used for termination and advertised to the client during the TLS handshake. if unspecified, ngrok will choose an industry-safe default. This value must be null if terminate\_at is set to upstream. | | `--tls-termination.terminate-at` | edge if the ngrok edge should terminate TLS traffic, upstream if TLS traffic should be passed through to the upstream ngrok agent / application server for termination. if upstream is chosen, most other modules will be disallowed because they rely on the ngrok edge being able to access the underlying traffic. | | `--type` | they type of traffic this endpoint configuration can be applied to. one of: http, https, tcp | | `--webhook-validation.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--webhook-validation.provider` | a string indicating which webhook provider will be sending webhooks to this endpoint. Value must be one of the supported providers defined at [https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification) ([https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification)) | | `--webhook-validation.secret` | a string secret used to validate requests from the given provider. All providers except AWS SNS require a secret | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoint-configurations delete Delete an endpoint configuration. This operation will fail if the endpoint configuration is still referenced by any reserved domain or reserved address. ### Usage ```sh theme={null} ngrok api endpoint-configurations delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoint-configurations get Returns detailed information about an endpoint configuration ### Usage ```sh theme={null} ngrok api endpoint-configurations get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoint-configurations list Returns a list of all endpoint configurations on this account ### Usage ```sh theme={null} ngrok api endpoint-configurations list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoint-configurations update Updates an endpoint configuration. If a module is not specified in the update, it will not be modified. However, each module configuration that is specified will completely replace the existing value. There is no way to delete an existing module via this API, instead use the delete module API. ### Usage ```sh theme={null} ngrok api endpoint-configurations update [flags] ``` ### Flags | Flag | Description | | - | - | | `--backend.backend-id` | backend to be used to back this endpoint | | `--backend.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--basic-auth.allow-options` | true or false indicating whether to allow OPTIONS requests through without authentication which is necessary for CORS. default is false | | `--basic-auth.auth-provider-id` | determines how the basic auth credentials are validated. Currently only the value agent is supported which means that credentials will be validated against the username and password specified by the ngrok agent's --basic-auth flag, if any. | | `--basic-auth.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--basic-auth.realm` | an arbitrary string to be specified in as the 'realm' value in the WWW-Authenticate header. default is ngrok | | `--circuit-breaker.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--circuit-breaker.error-threshold-percentage` | Error threshold percentage should be between 0 - 1.0, not 0-100.0 | | `--circuit-breaker.num-buckets` | Integer number of buckets into which metrics are retained. Max 128. | | `--circuit-breaker.rolling-window` | Integer number of seconds in the statistical rolling window that metrics are retained for. | | `--circuit-breaker.tripped-duration` | Integer number of seconds after which the circuit is tripped to wait before re-evaluating upstream health | | `--circuit-breaker.volume-threshold` | Integer number of requests in a rolling window that will trip the circuit. Helpful if traffic volume is low. | | `--compression.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--description` | human-readable description of what this endpoint configuration will be do when applied or what traffic it will be applied to. Optional, max 255 bytes | | `--ip-policy.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--ip-policy.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--metadata` | arbitrary user-defined machine-readable data of this endpoint configuration. Optional, max 4096 bytes. | | `--mutual-tls.certificate-authority-ids` | list of certificate authorities that will be used to validate the TLS client certificate presented by the initiator of the TLS connection | | `--mutual-tls.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--oauth.auth-check-interval` | Integer number of seconds after which ngrok guarantees it will refresh user state from the identity provider and recheck whether the user is still authorized to access the endpoint. This is the preferred tunable to use to enforce a minimum amount of time after which a revoked user will no longer be able to access the resource. | | `--oauth.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--oauth.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--oauth.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--oauth.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--oauth.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--oauth.provider.amazon.client-id` | | | `--oauth.provider.amazon.client-secret` | | | `--oauth.provider.amazon.email-addresses` | | | `--oauth.provider.amazon.email-domains` | | | `--oauth.provider.amazon.scopes` | | | `--oauth.provider.facebook.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.facebook.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.facebook.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.facebook.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.facebook.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.github.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.github.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.github.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.github.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.github.organizations` | a list of github org identifiers. users who are members of any of the listed organizations will be allowed access. identifiers should be the organization's 'slug' | | `--oauth.provider.github.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.github.teams` | a list of github teams identifiers. users will be allowed access to the endpoint if they are a member of any of these teams. identifiers should be in the 'slug' format qualified with the org name, e.g. org-name/team-name | | `--oauth.provider.gitlab.client-id` | | | `--oauth.provider.gitlab.client-secret` | | | `--oauth.provider.gitlab.email-addresses` | | | `--oauth.provider.gitlab.email-domains` | | | `--oauth.provider.gitlab.scopes` | | | `--oauth.provider.google.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.google.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.google.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.google.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.google.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.linkedin.client-id` | | | `--oauth.provider.linkedin.client-secret` | | | `--oauth.provider.linkedin.email-addresses` | | | `--oauth.provider.linkedin.email-domains` | | | `--oauth.provider.linkedin.scopes` | | | `--oauth.provider.microsoft.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--oauth.provider.microsoft.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--oauth.provider.microsoft.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.microsoft.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--oauth.provider.microsoft.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--oauth.provider.twitch.client-id` | | | `--oauth.provider.twitch.client-secret` | | | `--oauth.provider.twitch.email-addresses` | | | `--oauth.provider.twitch.email-domains` | | | `--oauth.provider.twitch.scopes` | | | `--oidc.client-id` | The OIDC app's client ID and OIDC audience. | | `--oidc.client-secret` | The OIDC app's client secret. | | `--oidc.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--oidc.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--oidc.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--oidc.issuer` | URL of the OIDC "OpenID provider". This is the base URL used for discovery. | | `--oidc.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--oidc.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--oidc.scopes` | The set of scopes to request from the OIDC identity provider. | | `--request-headers.add` | a map of header key to header value that will be injected into the HTTP Request before being sent to the upstream application server | | `--request-headers.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--request-headers.remove` | a list of header names that will be removed from the HTTP Request before being sent to the upstream application server | | `--response-headers.add` | a map of header key to header value that will be injected into the HTTP Response returned to the HTTP client | | `--response-headers.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--response-headers.remove` | a list of header names that will be removed from the HTTP Response returned to the HTTP client | | `--saml.allow-idp-initiated` | If true, the IdP may initiate a login directly (e.g. the user does not need to visit the endpoint first and then be redirected). The IdP should set the RelayState parameter to the target URL of the resource they want the user to be redirected to after the SAML login assertion has been processed. | | `--saml.authorized-groups` | If present, only users who are a member of one of the listed groups may access the target endpoint. | | `--saml.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--saml.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--saml.force-authn` | If true, indicates that whenever we redirect a user to the IdP for authentication that the IdP must prompt the user for authentication credentials even if the user already has a valid session with the IdP. | | `--saml.idp-metadata` | The full XML IdP EntityDescriptor. Your IdP may provide this to you as a a file to download or as a URL. | | `--saml.idp-metadata-url` | The IdP's metadata URL which returns the XML IdP EntityDescriptor. The IdP's metadata URL specifies how to connect to the IdP as well as its public key which is then used to validate the signature on incoming SAML assertions to the ACS endpoint. | | `--saml.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--saml.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--saml.nameid-format` | Defines the name identifier format the SP expects the IdP to use in its assertions to identify subjects. If unspecified, a default value of urn:oasis:names:tc:SAML:2.0:nameid-format:persistent will be used. A subset of the allowed values enumerated by the SAML specification are supported. | | `--saml.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--tls-termination.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--tls-termination.min-version` | The minimum TLS version used for termination and advertised to the client during the TLS handshake. if unspecified, ngrok will choose an industry-safe default. This value must be null if terminate\_at is set to upstream. | | `--tls-termination.terminate-at` | edge if the ngrok edge should terminate TLS traffic, upstream if TLS traffic should be passed through to the upstream ngrok agent / application server for termination. if upstream is chosen, most other modules will be disallowed because they rely on the ngrok edge being able to access the underlying traffic. | | `--webhook-validation.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--webhook-validation.provider` | a string indicating which webhook provider will be sending webhooks to this endpoint. Value must be one of the supported providers defined at [https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification) ([https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification)) | | `--webhook-validation.secret` | a string secret used to validate requests from the given provider. All providers except AWS SNS require a secret | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoints Endpoints provides an API for querying the endpoint objects which define what tunnel or edge is used to serve a hostport. Only active endpoints associated with a tunnel or backend are returned. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-endpoints-create) | Create an endpoint, ... | | [delete](#ngrok-api-endpoints-delete) | Delete an Endpoint by ID, ... | | [get](#ngrok-api-endpoints-get) | Get the status of an endpoint by ID | | [list](#ngrok-api-endpoints-list) | List all active endpoints on the account | | [update](#ngrok-api-endpoints-update) | Update an Endpoint by ID, ... | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoints create Create an endpoint, currently available only for cloud endpoints ### Usage ```sh theme={null} ngrok api endpoints create [flags] ``` ### Flags | Flag | Description | | - | - | | `--bindings` | the bindings associated with this endpoint | | `--description` | user-supplied description of the associated tunnel | | `--metadata` | user-supplied metadata of the associated tunnel or edge object | | `--pooling-enabled` | | | `--traffic-policy` | The traffic policy attached to this endpoint | | `--traffic-policy-file` | The traffic policy attached to this endpoint (path to a file) | | `--traffic-policy-url` | The traffic policy attached to this endpoint (URL to fetch from) | | `--type` | Type of endpoint. Only 'cloud' is currently supported (represents a cloud endpoint). Defaults to 'cloud' if not specified. | | `--url` | the url of the endpoint | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoints delete Delete an Endpoint by ID, currently available only for cloud endpoints ### Usage ```sh theme={null} ngrok api endpoints delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoints get Get the status of an endpoint by ID ### Usage ```sh theme={null} ngrok api endpoints get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoints list List all active endpoints on the account ### Usage ```sh theme={null} ngrok api endpoints list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api endpoints update Update an Endpoint by ID, currently available only for cloud endpoints ### Usage ```sh theme={null} ngrok api endpoints update [flags] ``` ### Flags | Flag | Description | | - | - | | `--bindings` | the bindings associated with this endpoint | | `--description` | user-supplied description of the associated tunnel | | `--metadata` | user-supplied metadata of the associated tunnel or edge object | | `--pooling-enabled` | | | `--traffic-policy` | The traffic policy attached to this endpoint | | `--traffic-policy-file` | The traffic policy attached to this endpoint (path to a file) | | `--traffic-policy-url` | The traffic policy attached to this endpoint (URL to fetch from) | | `--url` | the url of the endpoint | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-destinations ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-event-destinations-create) | Create a new Event Destination | | [delete](#ngrok-api-event-destinations-delete) | Delete an Event Destination | | [get](#ngrok-api-event-destinations-get) | Get detailed information about an Event... | | [list](#ngrok-api-event-destinations-list) | List all Event Destinations on this account. | | [send-test-event](#ngrok-api-event-destinations-send-test-event) | Send a test event to an Event Destination | | [update](#ngrok-api-event-destinations-update) | Update attributes of an Event Destination. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-destinations create Create a new Event Destination. It will not apply to anything until it is associated with an Event Subscription. ### Usage ```sh theme={null} ngrok api event-destinations create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | Human-readable description of the Event Destination. Optional, max 255 bytes. | | `--format` | The output format you would like to serialize events into when sending to their target. Currently the only accepted value is JSON. | | `--metadata` | Arbitrary user-defined machine-readable data of this Event Destination. Optional, max 4096 bytes. | | `--target.azure-logs-ingestion.client-id` | Client ID for the application client | | `--target.azure-logs-ingestion.client-secret` | Client Secret for the application client | | `--target.azure-logs-ingestion.data-collection-rule-id` | Data collection rule immutable ID | | `--target.azure-logs-ingestion.data-collection-stream-name` | Data collection stream name to use as destination, located inside the DCR | | `--target.azure-logs-ingestion.logs-ingestion-uri` | Data collection endpoint logs ingestion URI | | `--target.azure-logs-ingestion.tenant-id` | Tenant ID for the Azure account | | `--target.cloudwatch-logs.auth.creds.aws-access-key-id` | The ID portion of an AWS access key. | | `--target.cloudwatch-logs.auth.creds.aws-secret-access-key` | The secret portion of an AWS access key. | | `--target.cloudwatch-logs.auth.role.role-arn` | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | `--target.cloudwatch-logs.log-group-arn` | An Amazon Resource Name specifying the CloudWatch Logs group to deposit events into. | | `--target.datadog.api-key` | Datadog API key to use. | | `--target.datadog.ddsite` | Datadog site to send event to. | | `--target.datadog.ddtags` | Tags to send with the event. | | `--target.datadog.service` | Service name to send with the event. | | `--target.debug.callback-url` | URL to send events to. | | `--target.debug.log` | Whether or not to output to publisher service logs. | | `--target.firehose.auth.creds.aws-access-key-id` | The ID portion of an AWS access key. | | `--target.firehose.auth.creds.aws-secret-access-key` | The secret portion of an AWS access key. | | `--target.firehose.auth.role.role-arn` | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | `--target.firehose.delivery-stream-arn` | An Amazon Resource Name specifying the Firehose delivery stream to deposit events into. | | `--target.kinesis.auth.creds.aws-access-key-id` | The ID portion of an AWS access key. | | `--target.kinesis.auth.creds.aws-secret-access-key` | The secret portion of an AWS access key. | | `--target.kinesis.auth.role.role-arn` | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | `--target.kinesis.stream-arn` | An Amazon Resource Name specifying the Kinesis stream to deposit events into. | | `--verify-with-test-event` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-destinations delete Delete an Event Destination. If the Event Destination is still referenced by an Event Subscription. ### Usage ```sh theme={null} ngrok api event-destinations delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-destinations get Get detailed information about an Event Destination by ID. ### Usage ```sh theme={null} ngrok api event-destinations get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-destinations list List all Event Destinations on this account. ### Usage ```sh theme={null} ngrok api event-destinations list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-destinations send-test-event Send a test event to an Event Destination ### Usage ```sh theme={null} ngrok api event-destinations send-test-event [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-destinations update Update attributes of an Event Destination. ### Usage ```sh theme={null} ngrok api event-destinations update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | Human-readable description of the Event Destination. Optional, max 255 bytes. | | `--format` | The output format you would like to serialize events into when sending to their target. Currently the only accepted value is JSON. | | `--metadata` | Arbitrary user-defined machine-readable data of this Event Destination. Optional, max 4096 bytes. | | `--target.azure-logs-ingestion.client-id` | Client ID for the application client | | `--target.azure-logs-ingestion.client-secret` | Client Secret for the application client | | `--target.azure-logs-ingestion.data-collection-rule-id` | Data collection rule immutable ID | | `--target.azure-logs-ingestion.data-collection-stream-name` | Data collection stream name to use as destination, located inside the DCR | | `--target.azure-logs-ingestion.logs-ingestion-uri` | Data collection endpoint logs ingestion URI | | `--target.azure-logs-ingestion.tenant-id` | Tenant ID for the Azure account | | `--target.cloudwatch-logs.auth.creds.aws-access-key-id` | The ID portion of an AWS access key. | | `--target.cloudwatch-logs.auth.creds.aws-secret-access-key` | The secret portion of an AWS access key. | | `--target.cloudwatch-logs.auth.role.role-arn` | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | `--target.cloudwatch-logs.log-group-arn` | An Amazon Resource Name specifying the CloudWatch Logs group to deposit events into. | | `--target.datadog.api-key` | Datadog API key to use. | | `--target.datadog.ddsite` | Datadog site to send event to. | | `--target.datadog.ddtags` | Tags to send with the event. | | `--target.datadog.service` | Service name to send with the event. | | `--target.debug.callback-url` | URL to send events to. | | `--target.debug.log` | Whether or not to output to publisher service logs. | | `--target.firehose.auth.creds.aws-access-key-id` | The ID portion of an AWS access key. | | `--target.firehose.auth.creds.aws-secret-access-key` | The secret portion of an AWS access key. | | `--target.firehose.auth.role.role-arn` | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | `--target.firehose.delivery-stream-arn` | An Amazon Resource Name specifying the Firehose delivery stream to deposit events into. | | `--target.kinesis.auth.creds.aws-access-key-id` | The ID portion of an AWS access key. | | `--target.kinesis.auth.creds.aws-secret-access-key` | The secret portion of an AWS access key. | | `--target.kinesis.auth.role.role-arn` | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | `--target.kinesis.stream-arn` | An Amazon Resource Name specifying the Kinesis stream to deposit events into. | | `--verify-with-test-event` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-sources ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-event-sources-create) | Add an additional type for which this event... | | [delete](#ngrok-api-event-sources-delete) | Remove a type for which this event... | | [get](#ngrok-api-event-sources-get) | Get the details for a given type that triggers... | | [list](#ngrok-api-event-sources-list) | List the types for which this event... | | [update](#ngrok-api-event-sources-update) | Update the type for which this event... | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-sources create Add an additional type for which this event subscription will trigger ### Usage ```sh theme={null} ngrok api event-sources create [flags] ``` ### Flags | Flag | Description | | - | - | | `--fields` | | | `--filter` | | | `--subscription-id` | The unique identifier for the Event Subscription that this Event Source is attached to. | | `--type` | Type of event for which an event subscription will trigger | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-sources delete Remove a type for which this event subscription will trigger ### Usage ```sh theme={null} ngrok api event-sources delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--subscription-id` | The unique identifier for the Event Subscription that this Event Source is attached to. | | `--type` | Type of event for which an event subscription will trigger | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-sources get Get the details for a given type that triggers for the given event subscription ### Usage ```sh theme={null} ngrok api event-sources get [flags] ``` ### Flags | Flag | Description | | - | - | | `--subscription-id` | The unique identifier for the Event Subscription that this Event Source is attached to. | | `--type` | Type of event for which an event subscription will trigger | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-sources list List the types for which this event subscription will trigger ### Usage ```sh theme={null} ngrok api event-sources list [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-sources update Update the type for which this event subscription will trigger ### Usage ```sh theme={null} ngrok api event-sources update [flags] ``` ### Flags | Flag | Description | | - | - | | `--fields` | | | `--filter` | | | `--subscription-id` | The unique identifier for the Event Subscription that this Event Source is attached to. | | `--type` | Type of event for which an event subscription will trigger | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-subscriptions ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-event-subscriptions-create) | Create an Event Subscription. | | [delete](#ngrok-api-event-subscriptions-delete) | Delete an Event Subscription. | | [get](#ngrok-api-event-subscriptions-get) | Get an Event Subscription by ID. | | [list](#ngrok-api-event-subscriptions-list) | List this Account's Event Subscriptions. | | [update](#ngrok-api-event-subscriptions-update) | Update an Event Subscription. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-subscriptions create Create an Event Subscription. ### Usage ```sh theme={null} ngrok api event-subscriptions create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | Arbitrary customer supplied information intended to be human readable. Optional, max 255 chars. | | `--destination-ids` | A list of Event Destination IDs which should be used for this Event Subscription. | | `--metadata` | Arbitrary customer supplied information intended to be machine readable. Optional, max 4096 chars. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-subscriptions delete Delete an Event Subscription. ### Usage ```sh theme={null} ngrok api event-subscriptions delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-subscriptions get Get an Event Subscription by ID. ### Usage ```sh theme={null} ngrok api event-subscriptions get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-subscriptions list List this Account's Event Subscriptions. ### Usage ```sh theme={null} ngrok api event-subscriptions list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api event-subscriptions update Update an Event Subscription. ### Usage ```sh theme={null} ngrok api event-subscriptions update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | Arbitrary customer supplied information intended to be human readable. Optional, max 255 chars. | | `--destination-ids` | A list of Event Destination IDs which should be used for this Event Subscription. | | `--metadata` | Arbitrary customer supplied information intended to be machine readable. Optional, max 4096 chars. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policies IP Policies are reusable groups of CIDR ranges with an allow or deny action. They can be attached to endpoints via the Endpoint Configuration IP Policy module. They can also be used with IP Restrictions to control source IP ranges that can start tunnel sessions and connect to the API and dashboard. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-ip-policies-create) | Create a new IP policy | | [delete](#ngrok-api-ip-policies-delete) | Delete an IP policy | | [get](#ngrok-api-ip-policies-get) | Get detailed information about an IP policy by ID | | [list](#ngrok-api-ip-policies-list) | List all IP policies on this account | | [update](#ngrok-api-ip-policies-update) | Update attributes of an IP policy by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policies create Create a new IP policy. It will not apply to any traffic until you associate to a traffic source via an endpoint configuration or IP restriction. ### Usage ```sh theme={null} ngrok api ip-policies create [flags] ``` ### Flags | Flag | Description | | - | - | | `--action` | this field is deprecated. Please leave it empty and use the ip policy rule object's "action" field instead. It is temporarily retained for backwards compatibility reasons. | | `--description` | human-readable description of the source IPs of this IP policy. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this IP policy. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policies delete Delete an IP policy. If the IP policy is referenced by another object for the purposes of traffic restriction it will be treated as if the IP policy remains but has zero rules. ### Usage ```sh theme={null} ngrok api ip-policies delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policies get Get detailed information about an IP policy by ID. ### Usage ```sh theme={null} ngrok api ip-policies get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policies list List all IP policies on this account ### Usage ```sh theme={null} ngrok api ip-policies list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policies update Update attributes of an IP policy by ID ### Usage ```sh theme={null} ngrok api ip-policies update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of the source IPs of this IP policy. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this IP policy. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policy-rules IP Policy Rules are the IPv4 or IPv6 CIDRs entries that make up an IP Policy. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-ip-policy-rules-create) | Create a new IP policy rule attached to an IP... | | [delete](#ngrok-api-ip-policy-rules-delete) | Delete an IP policy rule. | | [get](#ngrok-api-ip-policy-rules-get) | Get detailed information about an IP policy... | | [list](#ngrok-api-ip-policy-rules-list) | List all IP policy rules on this account | | [update](#ngrok-api-ip-policy-rules-update) | Update attributes of an IP policy rule by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policy-rules create Create a new IP policy rule attached to an IP Policy. ### Usage ```sh theme={null} ngrok api ip-policy-rules create [flags] ``` ### Flags | Flag | Description | | - | - | | `--action` | the action to apply to the policy rule, either allow or deny | | `--cidr` | an IP or IP range specified in CIDR notation. IPv4 and IPv6 are both supported. | | `--description` | human-readable description of the source IPs of this IP rule. optional, max 255 bytes. | | `--ip-policy-id` | ID of the IP policy this IP policy rule will be attached to | | `--metadata` | arbitrary user-defined machine-readable data of this IP policy rule. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policy-rules delete Delete an IP policy rule. ### Usage ```sh theme={null} ngrok api ip-policy-rules delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policy-rules get Get detailed information about an IP policy rule by ID. ### Usage ```sh theme={null} ngrok api ip-policy-rules get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policy-rules list List all IP policy rules on this account ### Usage ```sh theme={null} ngrok api ip-policy-rules list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-policy-rules update Update attributes of an IP policy rule by ID ### Usage ```sh theme={null} ngrok api ip-policy-rules update [flags] ``` ### Flags | Flag | Description | | - | - | | `--cidr` | an IP or IP range specified in CIDR notation. IPv4 and IPv6 are both supported. | | `--description` | human-readable description of the source IPs of this IP rule. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this IP policy rule. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-restrictions An IP restriction is a restriction placed on the CIDRs that are allowed to initiate traffic to a specific aspect of your ngrok account. An IP restriction has a type which defines the ingress it applies to. IP restrictions can be used to enforce the source IPs that can make API requests, log in to the dashboard, start ngrok agents, and connect to your public-facing endpoints. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-ip-restrictions-create) | Create a new IP restriction | | [delete](#ngrok-api-ip-restrictions-delete) | Delete an IP restriction | | [get](#ngrok-api-ip-restrictions-get) | Get detailed information about an IP restriction | | [list](#ngrok-api-ip-restrictions-list) | List all IP restrictions on this account | | [update](#ngrok-api-ip-restrictions-update) | Update attributes of an IP restriction by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-restrictions create Create a new IP restriction ### Usage ```sh theme={null} ngrok api ip-restrictions create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this IP restriction. optional, max 255 bytes. | | `--enforced` | true if the IP restriction will be enforced. if false, only warnings will be issued | | `--ip-policy-ids` | the set of IP policy identifiers that are used to enforce the restriction | | `--metadata` | arbitrary user-defined machine-readable data of this IP restriction. optional, max 4096 bytes. | | `--type` | the type of IP restriction. this defines what traffic will be restricted with the attached policies. four values are currently supported: dashboard, api, agent, and endpoints | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-restrictions delete Delete an IP restriction ### Usage ```sh theme={null} ngrok api ip-restrictions delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-restrictions get Get detailed information about an IP restriction ### Usage ```sh theme={null} ngrok api ip-restrictions get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-restrictions list List all IP restrictions on this account ### Usage ```sh theme={null} ngrok api ip-restrictions list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ip-restrictions update Update attributes of an IP restriction by ID ### Usage ```sh theme={null} ngrok api ip-restrictions update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this IP restriction. optional, max 255 bytes. | | `--enforced` | true if the IP restriction will be enforced. if false, only warnings will be issued | | `--ip-policy-ids` | the set of IP policy identifiers that are used to enforce the restriction | | `--metadata` | arbitrary user-defined machine-readable data of this IP restriction. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api kubernetes-operators KubernetesOperators is used by the Kubernetes Operator to register and manage its own resource, as well as for users to see active kubernetes clusters. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-kubernetes-operators-create) | Create a new Kubernetes Operator | | [delete](#ngrok-api-kubernetes-operators-delete) | Delete a Kubernetes Operator | | [get](#ngrok-api-kubernetes-operators-get) | Get of a Kubernetes Operator | | [get-bound-endpoints](#ngrok-api-kubernetes-operators-get-bound-endpoints) | List Endpoints bound to a Kubernetes Operator | | [list](#ngrok-api-kubernetes-operators-list) | List all Kubernetes Operators owned by this... | | [update](#ngrok-api-kubernetes-operators-update) | Update an existing Kubernetes operator by ID. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api kubernetes-operators create Create a new Kubernetes Operator ### Usage ```sh theme={null} ngrok api kubernetes-operators create [flags] ``` ### Flags | Flag | Description | | - | - | | `--binding.csr` | CSR is supplied during initial creation to enable creating a mutual TLS secured connection between ngrok and the operator. This is an internal implementation detail and subject to change. | | `--binding.endpoint-selectors` | the list of cel expressions that filter the k8s bound endpoints for this operator | | `--binding.ingress-endpoint` | the public ingress endpoint for this Kubernetes Operator | | `--deployment.cluster-name` | user-given name for the cluster the Kubernetes Operator is deployed to | | `--deployment.name` | the deployment name | | `--deployment.namespace` | the namespace this Kubernetes Operator is deployed to | | `--deployment.version` | the version of this Kubernetes Operator | | `--description` | human-readable description of this Kubernetes Operator. optional, max 255 bytes. | | `--enabled-features` | features enabled for this Kubernetes Operator. a subset of "bindings", "ingress", and "gateway" | | `--metadata` | arbitrary user-defined machine-readable data of this Kubernetes Operator. optional, max 4096 bytes. | | `--region` | the ngrok region in which the ingress for this operator is served. defaults to "global" | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api kubernetes-operators delete Delete a Kubernetes Operator ### Usage ```sh theme={null} ngrok api kubernetes-operators delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api kubernetes-operators get Get of a Kubernetes Operator ### Usage ```sh theme={null} ngrok api kubernetes-operators get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api kubernetes-operators get-bound-endpoints List Endpoints bound to a Kubernetes Operator ### Usage ```sh theme={null} ngrok api kubernetes-operators get-bound-endpoints [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api kubernetes-operators list List all Kubernetes Operators owned by this account ### Usage ```sh theme={null} ngrok api kubernetes-operators list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api kubernetes-operators update Update an existing Kubernetes operator by ID. ### Usage ```sh theme={null} ngrok api kubernetes-operators update [flags] ``` ### Flags | Flag | Description | | - | - | | `--binding.csr` | CSR is supplied during initial creation to enable creating a mutual TLS secured connection between ngrok and the operator. This is an internal implementation detail and subject to change. | | `--binding.endpoint-selectors` | the list of cel expressions that filter the k8s bound endpoints for this operator | | `--binding.ingress-endpoint` | the public ingress endpoint for this Kubernetes Operator | | `--deployment.name` | the deployment name | | `--deployment.version` | the version of this Kubernetes Operator | | `--description` | human-readable description of this Kubernetes Operator. optional, max 255 bytes. | | `--enabled-features` | features enabled for this Kubernetes Operator. a subset of "bindings", "ingress", and "gateway" | | `--metadata` | arbitrary user-defined machine-readable data of this Kubernetes Operator. optional, max 4096 bytes. | | `--region` | the ngrok region in which the ingress for this operator is served. defaults to "global" | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks Networks provide private address space for an account. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-networks-create) | Create a network | | [delete](#ngrok-api-networks-delete) | Delete a network and its IP assignments. | | [get](#ngrok-api-networks-get) | Get a network by ID. | | [ips](#ngrok-api-networks-ips) | IP assignments reserve addresses in a network. | | [list](#ngrok-api-networks-list) | List networks owned by the authenticated account. | | [peers](#ngrok-api-networks-peers) | Peers are devices or processes with a current session in a network. They are read-only. | | [update](#ngrok-api-networks-update) | Update the description or metadata | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks create Create a network. Omitted CIDRs and the search domain receive defaults. ### Usage ```sh theme={null} ngrok api networks create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | Optional human-readable description. | | `--ipv-4-cidr` | Private IPv4 CIDR. If omitted, the API allocates an RFC 1918 /16. | | `--ipv-6-cidr` | Private IPv6 CIDR. If omitted, the API allocates a /56 within the account's unique-local /48. | | `--metadata` | User-defined metadata as string key-value pairs. | | `--search-domain` | DNS suffix without a leading dot. Defaults to the network ID, with its underscore replaced by a hyphen, under ngrok.network. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks delete Delete a network and its IP assignments. ### Usage ```sh theme={null} ngrok api networks delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks get Get a network by ID. ### Usage ```sh theme={null} ngrok api networks get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks ips IP assignments reserve addresses in a network. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-networks-ips-create) | Reserve a static IP address in a network. | | [delete](#ngrok-api-networks-ips-delete) | Release an IP assignment. | | [get](#ngrok-api-networks-ips-get) | Get an IP assignment by ID within its network. | | [list](#ngrok-api-networks-ips-list) | List IP assignments in a network. | | [update](#ngrok-api-networks-ips-update) | Update an assignment's name | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks ips create Reserve a static IP address in a network. ### Usage ```sh theme={null} ngrok api networks ips create [flags] ``` ### Flags | Flag | Description | | - | - | | `--address` | Requested address within the network's CIDR. If omitted, the API allocates an address. | | `--ip-version` | Address family: 4 or 6. Required without an address. If both are present, they must match. | | `--name` | Optional DNS label, unique per address family within the network. Names are case-insensitive and returned in lowercase. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks ips delete Release an IP assignment. ### Usage ```sh theme={null} ngrok api networks ips delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks ips get Get an IP assignment by ID within its network. ### Usage ```sh theme={null} ngrok api networks ips get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks ips list List IP assignments in a network. ### Usage ```sh theme={null} ngrok api networks ips list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks ips update Update an assignment's name. An empty string clears the name. ### Usage ```sh theme={null} ngrok api networks ips update [flags] ``` ### Flags | Flag | Description | | - | - | | `--name` | Optional DNS label, unique per address family within the network. Names are case-insensitive and returned in lowercase. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks list List networks owned by the authenticated account. ### Usage ```sh theme={null} ngrok api networks list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks peers Peers are devices or processes with a current session in a network. They are read-only. ### SubCommands | Command | Description | | - | - | | [get](#ngrok-api-networks-peers-get) | Get a peer by ID within its network. | | [list](#ngrok-api-networks-peers-list) | List peers in a network. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks peers get Get a peer by ID within its network. ### Usage ```sh theme={null} ngrok api networks peers get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks peers list List peers in a network. ### Usage ```sh theme={null} ngrok api networks peers list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api networks update Update the description or metadata. An empty string clears the description. An empty map clears metadata. ### Usage ```sh theme={null} ngrok api networks update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | Optional human-readable description. | | `--metadata` | User-defined metadata as string key-value pairs. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api personal-access-tokens Personal Access Tokens (PATs) are bearer credentials that authenticate the ngrok agent as the issuing principal. The token secret is only returned at creation time; save it securely. A PAT can use only its scopes, and only while the principal's current role in the account still grants them. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-personal-access-tokens-create) | Create a new Personal Access Token | | [delete](#ngrok-api-personal-access-tokens-delete) | Delete a Personal Access Token by ID | | [get](#ngrok-api-personal-access-tokens-get) | Get a Personal Access Token by ID. | | [list](#ngrok-api-personal-access-tokens-list) | List all Personal Access Tokens for this account | | [revoke](#ngrok-api-personal-access-tokens-revoke) | Revoke a Personal Access Token by ID | | [update](#ngrok-api-personal-access-tokens-update) | Update attributes of a Personal Access Token... | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api personal-access-tokens create Create a new Personal Access Token. The token secret is only returned in this response and cannot be retrieved later. If lost, you must create a new token. ### Usage ```sh theme={null} ngrok api personal-access-tokens create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this token. Optional, max 255 bytes. | | `--expires-at` | optional expiration time for this token. If unset the token does not expire. | | `--scopes` | scopes this token can use, e.g. \["endpoints:read"]. Each must be a scope you can grant. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api personal-access-tokens delete Delete a Personal Access Token by ID. The token immediately stops working and disappears from Get/List, but is not permanently removed -- it may be restored by an ngrok operator as part of a support request. ### Usage ```sh theme={null} ngrok api personal-access-tokens delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api personal-access-tokens get Get a Personal Access Token by ID. ### Usage ```sh theme={null} ngrok api personal-access-tokens get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api personal-access-tokens list List all Personal Access Tokens for this account ### Usage ```sh theme={null} ngrok api personal-access-tokens list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api personal-access-tokens revoke Revoke a Personal Access Token by ID. Unlike Delete, a revoked token remains visible in Get/List responses. ### Usage ```sh theme={null} ngrok api personal-access-tokens revoke [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api personal-access-tokens update Update attributes of a Personal Access Token by ID. ### Usage ```sh theme={null} ngrok api personal-access-tokens update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this token. Optional, max 255 bytes. | | `--scopes` | replaces the token's scopes. Omit to keep them. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module ### SubCommands | Command | Description | | - | - | | [backend](#ngrok-api-pointcfg-module-backend) | | | [basic-auth](#ngrok-api-pointcfg-module-basic-auth) | | | [circuit-breaker](#ngrok-api-pointcfg-module-circuit-breaker) | | | [compression](#ngrok-api-pointcfg-module-compression) | | | [ip-policy](#ngrok-api-pointcfg-module-ip-policy) | | | [mutual-tls](#ngrok-api-pointcfg-module-mutual-tls) | | | [oauth](#ngrok-api-pointcfg-module-oauth) | | | [oidc](#ngrok-api-pointcfg-module-oidc) | | | [request-headers](#ngrok-api-pointcfg-module-request-headers) | | | [response-headers](#ngrok-api-pointcfg-module-response-headers) | | | [saml](#ngrok-api-pointcfg-module-saml) | | | [tls-termination](#ngrok-api-pointcfg-module-tls-termination) | | | [webhook-validation](#ngrok-api-pointcfg-module-webhook-validation) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module backend ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-backend-delete) | | | [get](#ngrok-api-pointcfg-module-backend-get) | | | [replace](#ngrok-api-pointcfg-module-backend-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module backend delete ### Usage ```sh theme={null} ngrok api pointcfg-module backend delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module backend get ### Usage ```sh theme={null} ngrok api pointcfg-module backend get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module backend replace ### Usage ```sh theme={null} ngrok api pointcfg-module backend replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.backend-id` | backend to be used to back this endpoint | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module basic-auth ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-basic-auth-delete) | | | [get](#ngrok-api-pointcfg-module-basic-auth-get) | | | [replace](#ngrok-api-pointcfg-module-basic-auth-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module basic-auth delete ### Usage ```sh theme={null} ngrok api pointcfg-module basic-auth delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module basic-auth get ### Usage ```sh theme={null} ngrok api pointcfg-module basic-auth get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module basic-auth replace ### Usage ```sh theme={null} ngrok api pointcfg-module basic-auth replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.allow-options` | true or false indicating whether to allow OPTIONS requests through without authentication which is necessary for CORS. default is false | | `--module.auth-provider-id` | determines how the basic auth credentials are validated. Currently only the value agent is supported which means that credentials will be validated against the username and password specified by the ngrok agent's --basic-auth flag, if any. | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.realm` | an arbitrary string to be specified in as the 'realm' value in the WWW-Authenticate header. default is ngrok | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module circuit-breaker ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-circuit-breaker-delete) | | | [get](#ngrok-api-pointcfg-module-circuit-breaker-get) | | | [replace](#ngrok-api-pointcfg-module-circuit-breaker-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module circuit-breaker delete ### Usage ```sh theme={null} ngrok api pointcfg-module circuit-breaker delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module circuit-breaker get ### Usage ```sh theme={null} ngrok api pointcfg-module circuit-breaker get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module circuit-breaker replace ### Usage ```sh theme={null} ngrok api pointcfg-module circuit-breaker replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.error-threshold-percentage` | Error threshold percentage should be between 0 - 1.0, not 0-100.0 | | `--module.num-buckets` | Integer number of buckets into which metrics are retained. Max 128. | | `--module.rolling-window` | Integer number of seconds in the statistical rolling window that metrics are retained for. | | `--module.tripped-duration` | Integer number of seconds after which the circuit is tripped to wait before re-evaluating upstream health | | `--module.volume-threshold` | Integer number of requests in a rolling window that will trip the circuit. Helpful if traffic volume is low. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module compression ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-compression-delete) | | | [get](#ngrok-api-pointcfg-module-compression-get) | | | [replace](#ngrok-api-pointcfg-module-compression-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module compression delete ### Usage ```sh theme={null} ngrok api pointcfg-module compression delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module compression get ### Usage ```sh theme={null} ngrok api pointcfg-module compression get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module compression replace ### Usage ```sh theme={null} ngrok api pointcfg-module compression replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module ip-policy ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-ip-policy-delete) | | | [get](#ngrok-api-pointcfg-module-ip-policy-get) | | | [replace](#ngrok-api-pointcfg-module-ip-policy-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module ip-policy delete ### Usage ```sh theme={null} ngrok api pointcfg-module ip-policy delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module ip-policy get ### Usage ```sh theme={null} ngrok api pointcfg-module ip-policy get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module ip-policy replace ### Usage ```sh theme={null} ngrok api pointcfg-module ip-policy replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.ip-policy-ids` | list of all IP policies that will be used to check if a source IP is allowed access to the endpoint | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module mutual-tls ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-mutual-tls-delete) | | | [get](#ngrok-api-pointcfg-module-mutual-tls-get) | | | [replace](#ngrok-api-pointcfg-module-mutual-tls-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module mutual-tls delete ### Usage ```sh theme={null} ngrok api pointcfg-module mutual-tls delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module mutual-tls get ### Usage ```sh theme={null} ngrok api pointcfg-module mutual-tls get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module mutual-tls replace ### Usage ```sh theme={null} ngrok api pointcfg-module mutual-tls replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.certificate-authority-ids` | list of certificate authorities that will be used to validate the TLS client certificate presented by the initiator of the TLS connection | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module oauth ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-oauth-delete) | | | [get](#ngrok-api-pointcfg-module-oauth-get) | | | [replace](#ngrok-api-pointcfg-module-oauth-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module oauth delete ### Usage ```sh theme={null} ngrok api pointcfg-module oauth delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module oauth get ### Usage ```sh theme={null} ngrok api pointcfg-module oauth get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module oauth replace ### Usage ```sh theme={null} ngrok api pointcfg-module oauth replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.auth-check-interval` | Integer number of seconds after which ngrok guarantees it will refresh user state from the identity provider and recheck whether the user is still authorized to access the endpoint. This is the preferred tunable to use to enforce a minimum amount of time after which a revoked user will no longer be able to access the resource. | | `--module.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--module.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--module.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--module.provider.amazon.client-id` | | | `--module.provider.amazon.client-secret` | | | `--module.provider.amazon.email-addresses` | | | `--module.provider.amazon.email-domains` | | | `--module.provider.amazon.scopes` | | | `--module.provider.facebook.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--module.provider.facebook.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--module.provider.facebook.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.facebook.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.facebook.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--module.provider.github.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--module.provider.github.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--module.provider.github.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.github.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.github.organizations` | a list of github org identifiers. users who are members of any of the listed organizations will be allowed access. identifiers should be the organization's 'slug' | | `--module.provider.github.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--module.provider.github.teams` | a list of github teams identifiers. users will be allowed access to the endpoint if they are a member of any of these teams. identifiers should be in the 'slug' format qualified with the org name, e.g. org-name/team-name | | `--module.provider.gitlab.client-id` | | | `--module.provider.gitlab.client-secret` | | | `--module.provider.gitlab.email-addresses` | | | `--module.provider.gitlab.email-domains` | | | `--module.provider.gitlab.scopes` | | | `--module.provider.google.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--module.provider.google.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--module.provider.google.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.google.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.google.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--module.provider.linkedin.client-id` | | | `--module.provider.linkedin.client-secret` | | | `--module.provider.linkedin.email-addresses` | | | `--module.provider.linkedin.email-domains` | | | `--module.provider.linkedin.scopes` | | | `--module.provider.microsoft.client-id` | the OAuth app client ID. retrieve it from the identity provider's dashboard where you created your own OAuth app. optional. if unspecified, ngrok will use its own managed oauth application which has additional restrictions. see the OAuth module docs for more details. if present, client\_secret must be present as well. | | `--module.provider.microsoft.client-secret` | the OAuth app client secret. retrieve if from the identity provider's dashboard where you created your own OAuth app. optional, see all of the caveats in the docs for client\_id. | | `--module.provider.microsoft.email-addresses` | a list of email addresses of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.microsoft.email-domains` | a list of email domains of users authenticated by identity provider who are allowed access to the endpoint | | `--module.provider.microsoft.scopes` | a list of provider-specific OAuth scopes with the permissions your OAuth app would like to ask for. these may not be set if you are using the ngrok-managed oauth app (i.e. you must pass both client\_id and client\_secret to set scopes) | | `--module.provider.twitch.client-id` | | | `--module.provider.twitch.client-secret` | | | `--module.provider.twitch.email-addresses` | | | `--module.provider.twitch.email-domains` | | | `--module.provider.twitch.scopes` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module oidc ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-oidc-delete) | | | [get](#ngrok-api-pointcfg-module-oidc-get) | | | [replace](#ngrok-api-pointcfg-module-oidc-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module oidc delete ### Usage ```sh theme={null} ngrok api pointcfg-module oidc delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module oidc get ### Usage ```sh theme={null} ngrok api pointcfg-module oidc get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module oidc replace ### Usage ```sh theme={null} ngrok api pointcfg-module oidc replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.client-id` | The OIDC app's client ID and OIDC audience. | | `--module.client-secret` | The OIDC app's client secret. | | `--module.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--module.issuer` | URL of the OIDC "OpenID provider". This is the base URL used for discovery. | | `--module.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--module.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--module.scopes` | The set of scopes to request from the OIDC identity provider. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module request-headers ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-request-headers-delete) | | | [get](#ngrok-api-pointcfg-module-request-headers-get) | | | [replace](#ngrok-api-pointcfg-module-request-headers-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module request-headers delete ### Usage ```sh theme={null} ngrok api pointcfg-module request-headers delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module request-headers get ### Usage ```sh theme={null} ngrok api pointcfg-module request-headers get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module request-headers replace ### Usage ```sh theme={null} ngrok api pointcfg-module request-headers replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.add` | a map of header key to header value that will be injected into the HTTP Request before being sent to the upstream application server | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.remove` | a list of header names that will be removed from the HTTP Request before being sent to the upstream application server | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module response-headers ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-response-headers-delete) | | | [get](#ngrok-api-pointcfg-module-response-headers-get) | | | [replace](#ngrok-api-pointcfg-module-response-headers-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module response-headers delete ### Usage ```sh theme={null} ngrok api pointcfg-module response-headers delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module response-headers get ### Usage ```sh theme={null} ngrok api pointcfg-module response-headers get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module response-headers replace ### Usage ```sh theme={null} ngrok api pointcfg-module response-headers replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.add` | a map of header key to header value that will be injected into the HTTP Response returned to the HTTP client | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.remove` | a list of header names that will be removed from the HTTP Response returned to the HTTP client | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module saml ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-saml-delete) | | | [get](#ngrok-api-pointcfg-module-saml-get) | | | [replace](#ngrok-api-pointcfg-module-saml-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module saml delete ### Usage ```sh theme={null} ngrok api pointcfg-module saml delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module saml get ### Usage ```sh theme={null} ngrok api pointcfg-module saml get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module saml replace ### Usage ```sh theme={null} ngrok api pointcfg-module saml replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.allow-idp-initiated` | If true, the IdP may initiate a login directly (e.g. the user does not need to visit the endpoint first and then be redirected). The IdP should set the RelayState parameter to the target URL of the resource they want the user to be redirected to after the SAML login assertion has been processed. | | `--module.authorized-groups` | If present, only users who are a member of one of the listed groups may access the target endpoint. | | `--module.cookie-prefix` | the prefix of the session cookie that ngrok sets on the http client to cache authentication. default is 'ngrok.' | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.force-authn` | If true, indicates that whenever we redirect a user to the IdP for authentication that the IdP must prompt the user for authentication credentials even if the user already has a valid session with the IdP. | | `--module.idp-metadata` | The full XML IdP EntityDescriptor. Your IdP may provide this to you as a a file to download or as a URL. | | `--module.idp-metadata-url` | The IdP's metadata URL which returns the XML IdP EntityDescriptor. The IdP's metadata URL specifies how to connect to the IdP as well as its public key which is then used to validate the signature on incoming SAML assertions to the ACS endpoint. | | `--module.inactivity-timeout` | Integer number of seconds of inactivity after which if the user has not accessed the endpoint, their session will time out and they will be forced to reauthenticate. | | `--module.maximum-duration` | Integer number of seconds of the maximum duration of an authenticated session. After this period is exceeded, a user must reauthenticate. | | `--module.nameid-format` | Defines the name identifier format the SP expects the IdP to use in its assertions to identify subjects. If unspecified, a default value of urn:oasis:names:tc:SAML:2.0:nameid-format:persistent will be used. A subset of the allowed values enumerated by the SAML specification are supported. | | `--module.options-passthrough` | Do not enforce authentication on HTTP OPTIONS requests. necessary if you are supporting CORS. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module tls-termination ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-tls-termination-delete) | | | [get](#ngrok-api-pointcfg-module-tls-termination-get) | | | [replace](#ngrok-api-pointcfg-module-tls-termination-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module tls-termination delete ### Usage ```sh theme={null} ngrok api pointcfg-module tls-termination delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module tls-termination get ### Usage ```sh theme={null} ngrok api pointcfg-module tls-termination get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module tls-termination replace ### Usage ```sh theme={null} ngrok api pointcfg-module tls-termination replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.min-version` | The minimum TLS version used for termination and advertised to the client during the TLS handshake. if unspecified, ngrok will choose an industry-safe default. This value must be null if terminate\_at is set to upstream. | | `--module.terminate-at` | edge if the ngrok edge should terminate TLS traffic, upstream if TLS traffic should be passed through to the upstream ngrok agent / application server for termination. if upstream is chosen, most other modules will be disallowed because they rely on the ngrok edge being able to access the underlying traffic. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module webhook-validation ### SubCommands | Command | Description | | - | - | | [delete](#ngrok-api-pointcfg-module-webhook-validation-delete) | | | [get](#ngrok-api-pointcfg-module-webhook-validation-get) | | | [replace](#ngrok-api-pointcfg-module-webhook-validation-replace) | | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module webhook-validation delete ### Usage ```sh theme={null} ngrok api pointcfg-module webhook-validation delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module webhook-validation get ### Usage ```sh theme={null} ngrok api pointcfg-module webhook-validation get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api pointcfg-module webhook-validation replace ### Usage ```sh theme={null} ngrok api pointcfg-module webhook-validation replace [flags] ``` ### Flags | Flag | Description | | - | - | | `--module.enabled` | true if the module will be applied to traffic, false to disable. default true if unspecified | | `--module.provider` | a string indicating which webhook provider will be sending webhooks to this endpoint. Value must be one of the supported providers defined at [https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification) ([https://ngrok.com/docs/cloud-edge/modules/webhook-verification](https://ngrok.com/docs/cloud-edge/modules/webhook-verification)) | | `--module.secret` | a string secret used to validate requests from the given provider. All providers except AWS SNS require a secret | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-addrs Reserved Addresses are TCP addresses that can be used to listen for traffic. TCP address hostnames and ports are assigned by ngrok, they cannot be chosen. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-reserved-addrs-create) | Create a new reserved address. | | [delete](#ngrok-api-reserved-addrs-delete) | Delete a reserved address. | | [delete-endpoint-config](#ngrok-api-reserved-addrs-delete-endpoint-config) | Detach the endpoint configuration attached to... | | [get](#ngrok-api-reserved-addrs-get) | Get the details of a reserved address. | | [list](#ngrok-api-reserved-addrs-list) | List all reserved addresses on this account. | | [update](#ngrok-api-reserved-addrs-update) | Update the attributes of a reserved address. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-addrs create Create a new reserved address. ### Usage ```sh theme={null} ngrok api reserved-addrs create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of what this reserved address will be used for | | `--endpoint-configuration-id` | ID of an endpoint configuration of type tcp that will be used to handle inbound traffic to this address | | `--metadata` | arbitrary user-defined machine-readable data of this reserved address. Optional, max 4096 bytes. | | `--region` | reserve the address in this geographic ngrok datacenter. Optional, default is us. (au, eu, ap, us, jp, in, sa) | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-addrs delete Delete a reserved address. ### Usage ```sh theme={null} ngrok api reserved-addrs delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-addrs delete-endpoint-config Detach the endpoint configuration attached to a reserved address. ### Usage ```sh theme={null} ngrok api reserved-addrs delete-endpoint-config [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-addrs get Get the details of a reserved address. ### Usage ```sh theme={null} ngrok api reserved-addrs get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-addrs list List all reserved addresses on this account. ### Usage ```sh theme={null} ngrok api reserved-addrs list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-addrs update Update the attributes of a reserved address. ### Usage ```sh theme={null} ngrok api reserved-addrs update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of what this reserved address will be used for | | `--endpoint-configuration-id` | ID of an endpoint configuration of type tcp that will be used to handle inbound traffic to this address | | `--metadata` | arbitrary user-defined machine-readable data of this reserved address. Optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-domains Reserved Domains are hostnames that you can listen for traffic on. Domains can be used to listen for http, https or tls traffic. You may use a domain that you own by creating a CNAME record specified in the returned resource. This CNAME record points traffic for that domain to ngrok's edge servers. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-reserved-domains-create) | Create a new reserved domain. | | [delete](#ngrok-api-reserved-domains-delete) | Delete a reserved domain. | | [delete-certificate](#ngrok-api-reserved-domains-delete-certificate) | Detach the certificate attached to a reserved... | | [delete-certificate-management-policy](#ngrok-api-reserved-domains-delete-certificate-management-policy) | Detach the certificate management policy... | | [delete-http-endpoint-config](#ngrok-api-reserved-domains-delete-http-endpoint-config) | Detach the http endpoint configuration... | | [delete-https-endpoint-config](#ngrok-api-reserved-domains-delete-https-endpoint-config) | Detach the https endpoint configuration... | | [get](#ngrok-api-reserved-domains-get) | Get the details of a reserved domain. | | [list](#ngrok-api-reserved-domains-list) | List all reserved domains on this account. | | [update](#ngrok-api-reserved-domains-update) | Update the attributes of a reserved domain. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-domains create Create a new reserved domain. ### Usage ```sh theme={null} ngrok api reserved-domains create [flags] ``` ### Flags | Flag | Description | | - | - | | `--certificate-id` | ID of a user-uploaded TLS certificate to use for connections to targeting this domain. Optional, mutually exclusive with certificate\_management\_policy. | | `--certificate-management-policy.authority` | certificate authority to request certificates from. The only supported value is letsencrypt. | | `--certificate-management-policy.private-key-type` | type of private key to use when requesting certificates. Defaults to ecdsa, can be either rsa or ecdsa. | | `--description` | human-readable description of what this reserved domain will be used for | | `--domain` | hostname of the reserved domain | | `--http-endpoint-configuration-id` | ID of an endpoint configuration of type http that will be used to handle inbound http traffic to this domain | | `--https-endpoint-configuration-id` | ID of an endpoint configuration of type https that will be used to handle inbound https traffic to this domain | | `--metadata` | arbitrary user-defined machine-readable data of this reserved domain. Optional, max 4096 bytes. | | `--name` | the domain name to reserve. It may be a full domain name like app.example.com. If the name does not contain a '.' it will reserve that subdomain on ngrok.io. | | `--region` | deprecated: With the launch of the ngrok Global Network domains traffic is now handled globally. This field applied only to endpoints. Note that agents may still connect to specific regions. Optional, null by default. (au, eu, ap, us, jp, in, sa) | | `--resolves-to` | Region and IP resolution targets configured for the Domain. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-domains delete Delete a reserved domain. ### Usage ```sh theme={null} ngrok api reserved-domains delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-domains delete-certificate Detach the certificate attached to a reserved domain. ### Usage ```sh theme={null} ngrok api reserved-domains delete-certificate [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-domains delete-certificate-management-policy Detach the certificate management policy attached to a reserved domain. ### Usage ```sh theme={null} ngrok api reserved-domains delete-certificate-management-policy [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-domains delete-http-endpoint-config Detach the http endpoint configuration attached to a reserved domain. ### Usage ```sh theme={null} ngrok api reserved-domains delete-http-endpoint-config [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-domains delete-https-endpoint-config Detach the https endpoint configuration attached to a reserved domain. ### Usage ```sh theme={null} ngrok api reserved-domains delete-https-endpoint-config [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-domains get Get the details of a reserved domain. ### Usage ```sh theme={null} ngrok api reserved-domains get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-domains list List all reserved domains on this account. ### Usage ```sh theme={null} ngrok api reserved-domains list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api reserved-domains update Update the attributes of a reserved domain. ### Usage ```sh theme={null} ngrok api reserved-domains update [flags] ``` ### Flags | Flag | Description | | - | - | | `--certificate-id` | ID of a user-uploaded TLS certificate to use for connections to targeting this domain. Optional, mutually exclusive with certificate\_management\_policy. | | `--certificate-management-policy.authority` | certificate authority to request certificates from. The only supported value is letsencrypt. | | `--certificate-management-policy.private-key-type` | type of private key to use when requesting certificates. Defaults to ecdsa, can be either rsa or ecdsa. | | `--description` | human-readable description of what this reserved domain will be used for | | `--http-endpoint-configuration-id` | ID of an endpoint configuration of type http that will be used to handle inbound http traffic to this domain | | `--https-endpoint-configuration-id` | ID of an endpoint configuration of type https that will be used to handle inbound https traffic to this domain | | `--metadata` | arbitrary user-defined machine-readable data of this reserved domain. Optional, max 4096 bytes. | | `--region` | deprecated: With the launch of the ngrok Global Network domains traffic is now handled globally. This field applied only to endpoints. Note that agents may still connect to specific regions. Optional, null by default. (au, eu, ap, us, jp, in, sa) | | `--resolves-to` | Region and IP resolution targets configured for the Domain. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api root ### SubCommands | Command | Description | | - | - | | [get](#ngrok-api-root-get) | | | [self](#ngrok-api-root-self) | Get the details of the data used in this... | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api root get ### Usage ```sh theme={null} ngrok api root get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api root self Get the details of the data used in this current request ### Usage ```sh theme={null} ngrok api root self [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api secrets Secrets is an api service for securely storing and managing sensitive data such as secrets, credentials, and tokens. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-secrets-create) | Create a new Secret | | [delete](#ngrok-api-secrets-delete) | Delete a Secret | | [get](#ngrok-api-secrets-get) | Get a Secret by ID | | [list](#ngrok-api-secrets-list) | List all Secrets owned by account | | [update](#ngrok-api-secrets-update) | Update an existing Secret by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api secrets create Create a new Secret ### Usage ```sh theme={null} ngrok api secrets create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | description of Secret | | `--metadata` | Arbitrary user-defined metadata for this Secret | | `--name` | Name of secret | | `--value` | Value of secret | | `--value-file` | Value of secret (path to a file) | | `--vault-id` | unique identifier of the referenced vault | | `--vault-name` | name of the referenced vault | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api secrets delete Delete a Secret ### Usage ```sh theme={null} ngrok api secrets delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api secrets get Get a Secret by ID ### Usage ```sh theme={null} ngrok api secrets get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api secrets list List all Secrets owned by account ### Usage ```sh theme={null} ngrok api secrets list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api secrets update Update an existing Secret by ID ### Usage ```sh theme={null} ngrok api secrets update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | description of Secret | | `--metadata` | Arbitrary user-defined metadata for this Secret | | `--name` | Name of secret | | `--value` | Value of secret | | `--value-file` | Value of secret (path to a file) | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api service-users ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-service-users-create) | Create a new service user | | [delete](#ngrok-api-service-users-delete) | Delete a service user by ID | | [get](#ngrok-api-service-users-get) | Get the details of a Bot User by ID. | | [list](#ngrok-api-service-users-list) | List all service users in this account. | | [update](#ngrok-api-service-users-update) | Update attributes of a service user by ID. | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api service-users create Create a new service user ### Usage ```sh theme={null} ngrok api service-users create [flags] ``` ### Flags | Flag | Description | | - | - | | `--active` | whether or not the service is active | | `--name` | human-readable name used to identify the service | | `--roles` | roles the service user holds. Defaults to developer. Any other choice needs a plan with role-based access control. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api service-users delete Delete a service user by ID ### Usage ```sh theme={null} ngrok api service-users delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api service-users get Get the details of a Bot User by ID. ### Usage ```sh theme={null} ngrok api service-users get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api service-users list List all service users in this account. ### Usage ```sh theme={null} ngrok api service-users list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api service-users update Update attributes of a service user by ID. ### Usage ```sh theme={null} ngrok api service-users update [flags] ``` ### Flags | Flag | Description | | - | - | | `--active` | whether or not the service is active | | `--name` | human-readable name used to identify the service | | `--roles` | roles that replace the service user's current roles. Any choice other than developer needs a plan with role-based access control. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-certificate-authorities An SSH Certificate Authority is a pair of an SSH Certificate and its private key that can be used to sign other SSH host and user certificates. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-ssh-certificate-authorities-create) | Create a new SSH Certificate Authority | | [delete](#ngrok-api-ssh-certificate-authorities-delete) | Delete an SSH Certificate Authority | | [get](#ngrok-api-ssh-certificate-authorities-get) | Get detailed information about an SSH... | | [list](#ngrok-api-ssh-certificate-authorities-list) | List all SSH Certificate Authorities on this... | | [update](#ngrok-api-ssh-certificate-authorities-update) | Update an SSH Certificate Authority | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-certificate-authorities create Create a new SSH Certificate Authority ### Usage ```sh theme={null} ngrok api ssh-certificate-authorities create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this SSH Certificate Authority. optional, max 255 bytes. | | `--elliptic-curve` | the type of elliptic curve to use when creating an ECDSA key | | `--key-size` | the key size to use when creating an RSA key. one of 2048 or 4096 | | `--metadata` | arbitrary user-defined machine-readable data of this SSH Certificate Authority. optional, max 4096 bytes. | | `--private-key-type` | the type of private key to generate. one of rsa, ecdsa, ed25519 | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-certificate-authorities delete Delete an SSH Certificate Authority ### Usage ```sh theme={null} ngrok api ssh-certificate-authorities delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-certificate-authorities get Get detailed information about an SSH Certificate Authority ### Usage ```sh theme={null} ngrok api ssh-certificate-authorities get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-certificate-authorities list List all SSH Certificate Authorities on this account ### Usage ```sh theme={null} ngrok api ssh-certificate-authorities list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-certificate-authorities update Update an SSH Certificate Authority ### Usage ```sh theme={null} ngrok api ssh-certificate-authorities update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this SSH Certificate Authority. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this SSH Certificate Authority. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-credentials SSH Credentials are SSH public keys that can be used to start SSH tunnels via the ngrok SSH tunnel gateway. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-ssh-credentials-create) | Create a new ssh\_credential from an uploaded... | | [delete](#ngrok-api-ssh-credentials-delete) | Delete an ssh\_credential by ID | | [get](#ngrok-api-ssh-credentials-get) | Get detailed information about an ssh\_credential | | [list](#ngrok-api-ssh-credentials-list) | List all ssh credentials on this account | | [update](#ngrok-api-ssh-credentials-update) | Update attributes of an ssh\_credential by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-credentials create Create a new ssh\_credential from an uploaded public SSH key. This ssh credential can be used to start new tunnels via ngrok's SSH gateway. ### Usage ```sh theme={null} ngrok api ssh-credentials create [flags] ``` ### Flags | Flag | Description | | - | - | | `--acl` | optional list of ACL rules. If unspecified, the credential will have no restrictions. The only allowed ACL rule at this time is the bind rule. The bind rule allows the caller to restrict what domains, addresses, and labels the token is allowed to bind. For example, to allow the token to open a tunnel on example.ngrok.io your ACL would include the rule bind:example.ngrok.io. Bind rules for domains may specify a leading wildcard to match multiple domains with a common suffix. For example, you may specify a rule of bind:*.example.com which will allow x.example.com, y.example.com, *.example.com, etc. Bind rules for labels may specify a wildcard key and/or value to match multiple labels. For example, you may specify a rule of bind:*=example which will allow x=example, y=example, etc. A rule of '*' is equivalent to no acl at all and will explicitly permit all actions. | | `--description` | human-readable description of who or what will use the ssh credential to authenticate. Optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this ssh credential. Optional, max 4096 bytes. | | `--owner-email` | If supplied at credential creation, ownership will be assigned to the specified User. Only admins may specify an owner other than themselves. Both owner\_id and owner\_email may not be specified. | | `--owner-id` | If supplied at credential creation, ownership will be assigned to the specified User or Bot. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Bot. | | `--public-key` | the PEM-encoded public key of the SSH keypair that will be used to authenticate | | `--scim-user-id` | If supplied at credential creation, ownership will be assigned to the specified SCIM User. Only admins may specify an owner other than themselves. Only one of owner\_id, owner\_email, or scim\_user\_id may be specified. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-credentials delete Delete an ssh\_credential by ID ### Usage ```sh theme={null} ngrok api ssh-credentials delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-credentials get Get detailed information about an ssh\_credential ### Usage ```sh theme={null} ngrok api ssh-credentials get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-credentials list List all ssh credentials on this account ### Usage ```sh theme={null} ngrok api ssh-credentials list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-credentials update Update attributes of an ssh\_credential by ID ### Usage ```sh theme={null} ngrok api ssh-credentials update [flags] ``` ### Flags | Flag | Description | | - | - | | `--acl` | optional list of ACL rules. If unspecified, the credential will have no restrictions. The only allowed ACL rule at this time is the bind rule. The bind rule allows the caller to restrict what domains, addresses, and labels the token is allowed to bind. For example, to allow the token to open a tunnel on example.ngrok.io your ACL would include the rule bind:example.ngrok.io. Bind rules for domains may specify a leading wildcard to match multiple domains with a common suffix. For example, you may specify a rule of bind:*.example.com which will allow x.example.com, y.example.com, *.example.com, etc. Bind rules for labels may specify a wildcard key and/or value to match multiple labels. For example, you may specify a rule of bind:*=example which will allow x=example, y=example, etc. A rule of '*' is equivalent to no acl at all and will explicitly permit all actions. | | `--description` | human-readable description of who or what will use the ssh credential to authenticate. Optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this ssh credential. Optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-host-certificates SSH Host Certificates along with the corresponding private key allows an SSH server to assert its authenticity to connecting SSH clients who trust the SSH Certificate Authority that was used to sign the certificate. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-ssh-host-certificates-create) | Create a new SSH Host Certificate | | [delete](#ngrok-api-ssh-host-certificates-delete) | Delete an SSH Host Certificate | | [get](#ngrok-api-ssh-host-certificates-get) | Get detailed information about an SSH Host... | | [list](#ngrok-api-ssh-host-certificates-list) | List all SSH Host Certificates issued on this... | | [update](#ngrok-api-ssh-host-certificates-update) | Update an SSH Host Certificate | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-host-certificates create Create a new SSH Host Certificate ### Usage ```sh theme={null} ngrok api ssh-host-certificates create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this SSH Host Certificate. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this SSH Host Certificate. optional, max 4096 bytes. | | `--principals` | the list of principals included in the ssh host certificate. This is the list of hostnames and/or IP addresses that are authorized to serve SSH traffic with this certificate. Dangerously, if no principals are specified, this certificate is considered valid for all hosts. | | `--public-key` | a public key in OpenSSH Authorized Keys format that this certificate signs | | `--ssh-certificate-authority-id` | the ssh certificate authority that is used to sign this ssh host certificate | | `--valid-after` | The time when the host certificate becomes valid, in RFC 3339 format. Defaults to the current time if unspecified. | | `--valid-until` | The time when this host certificate becomes invalid, in RFC 3339 format. If unspecified, a default value of one year in the future will be used. The OpenSSH certificates RFC calls this valid\_before. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-host-certificates delete Delete an SSH Host Certificate ### Usage ```sh theme={null} ngrok api ssh-host-certificates delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-host-certificates get Get detailed information about an SSH Host Certificate ### Usage ```sh theme={null} ngrok api ssh-host-certificates get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-host-certificates list List all SSH Host Certificates issued on this account ### Usage ```sh theme={null} ngrok api ssh-host-certificates list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-host-certificates update Update an SSH Host Certificate ### Usage ```sh theme={null} ngrok api ssh-host-certificates update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this SSH Host Certificate. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this SSH Host Certificate. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-user-certificates SSH User Certificates are presented by SSH clients when connecting to an SSH server to authenticate their connection. The SSH server must trust the SSH Certificate Authority used to sign the certificate. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-ssh-user-certificates-create) | Create a new SSH User Certificate | | [delete](#ngrok-api-ssh-user-certificates-delete) | Delete an SSH User Certificate | | [get](#ngrok-api-ssh-user-certificates-get) | Get detailed information about an SSH User... | | [list](#ngrok-api-ssh-user-certificates-list) | List all SSH User Certificates issued on this... | | [update](#ngrok-api-ssh-user-certificates-update) | Update an SSH User Certificate | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-user-certificates create Create a new SSH User Certificate ### Usage ```sh theme={null} ngrok api ssh-user-certificates create [flags] ``` ### Flags | Flag | Description | | - | - | | `--critical-options` | A map of critical options included in the certificate. Only two critical options are currently defined by OpenSSH: force-command and source-address. See the OpenSSH certificate protocol spec ([https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)) for additional details. | | `--description` | human-readable description of this SSH User Certificate. optional, max 255 bytes. | | `--extensions` | A map of extensions included in the certificate. Extensions are additional metadata that can be interpreted by the SSH server for any purpose. These can be used to permit or deny the ability to open a terminal, do port forwarding, x11 forwarding, and more. If unspecified, the certificate will include limited permissions with the following extension map: \{"permit-pty": "", "permit-user-rc": ""} OpenSSH understands a number of predefined extensions. See the OpenSSH certificate protocol spec ([https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)) for additional details. | | `--metadata` | arbitrary user-defined machine-readable data of this SSH User Certificate. optional, max 4096 bytes. | | `--principals` | the list of principals included in the ssh user certificate. This is the list of usernames that the certificate holder may sign in as on a machine authorizing the signing certificate authority. Dangerously, if no principals are specified, this certificate may be used to log in as any user. | | `--public-key` | a public key in OpenSSH Authorized Keys format that this certificate signs | | `--ssh-certificate-authority-id` | the ssh certificate authority that is used to sign this ssh user certificate | | `--valid-after` | The time when the user certificate becomes valid, in RFC 3339 format. Defaults to the current time if unspecified. | | `--valid-until` | The time when this host certificate becomes invalid, in RFC 3339 format. If unspecified, a default value of 24 hours will be used. The OpenSSH certificates RFC calls this valid\_before. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-user-certificates delete Delete an SSH User Certificate ### Usage ```sh theme={null} ngrok api ssh-user-certificates delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-user-certificates get Get detailed information about an SSH User Certificate ### Usage ```sh theme={null} ngrok api ssh-user-certificates get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-user-certificates list List all SSH User Certificates issued on this account ### Usage ```sh theme={null} ngrok api ssh-user-certificates list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api ssh-user-certificates update Update an SSH User Certificate ### Usage ```sh theme={null} ngrok api ssh-user-certificates update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this SSH User Certificate. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this SSH User Certificate. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tls-certificates TLS Certificates are pairs of x509 certificates and their matching private key that can be used to terminate TLS traffic. TLS certificates are unused until they are attached to a Domain. TLS Certificates may also be provisioned by ngrok automatically for domains on which you have enabled automated certificate provisioning. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-tls-certificates-create) | Upload a new TLS certificate | | [delete](#ngrok-api-tls-certificates-delete) | Delete a TLS certificate | | [get](#ngrok-api-tls-certificates-get) | Get detailed information about a TLS certificate | | [list](#ngrok-api-tls-certificates-list) | List all TLS certificates on this account | | [update](#ngrok-api-tls-certificates-update) | Update attributes of a TLS Certificate by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tls-certificates create Upload a new TLS certificate ### Usage ```sh theme={null} ngrok api tls-certificates create [flags] ``` ### Flags | Flag | Description | | - | - | | `--certificate-pem` | chain of PEM-encoded certificates, leaf first. See Certificate Bundles ([https://ngrok.com/docs/cloud-edge/endpoints#certificate-chains](https://ngrok.com/docs/cloud-edge/endpoints#certificate-chains)). | | `--description` | human-readable description of this TLS certificate. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this TLS certificate. optional, max 4096 bytes. | | `--private-key-pem` | private key for the TLS certificate, PEM-encoded. See Private Keys ([https://ngrok.com/docs/cloud-edge/endpoints#private-keys](https://ngrok.com/docs/cloud-edge/endpoints#private-keys)). | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tls-certificates delete Delete a TLS certificate ### Usage ```sh theme={null} ngrok api tls-certificates delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tls-certificates get Get detailed information about a TLS certificate ### Usage ```sh theme={null} ngrok api tls-certificates get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tls-certificates list List all TLS certificates on this account ### Usage ```sh theme={null} ngrok api tls-certificates list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tls-certificates update Update attributes of a TLS Certificate by ID ### Usage ```sh theme={null} ngrok api tls-certificates update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | human-readable description of this TLS certificate. optional, max 255 bytes. | | `--metadata` | arbitrary user-defined machine-readable data of this TLS certificate. optional, max 4096 bytes. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tunnel-sessions Tunnel Sessions represent instances of ngrok agents or SSH reverse tunnel sessions that are running and connected to the ngrok service. Each tunnel session can include one or more Tunnels. ### SubCommands | Command | Description | | - | - | | [get](#ngrok-api-tunnel-sessions-get) | Get the detailed status of a tunnel session by ID | | [list](#ngrok-api-tunnel-sessions-list) | List all online tunnel sessions running on... | | [restart](#ngrok-api-tunnel-sessions-restart) | Issues a command instructing the ngrok agent... | | [stop](#ngrok-api-tunnel-sessions-stop) | Issues a command instructing the ngrok agent... | | [update](#ngrok-api-tunnel-sessions-update) | Issues a command instructing the ngrok agent... | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tunnel-sessions get Get the detailed status of a tunnel session by ID ### Usage ```sh theme={null} ngrok api tunnel-sessions get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tunnel-sessions list List all online tunnel sessions running on this account. ### Usage ```sh theme={null} ngrok api tunnel-sessions list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tunnel-sessions restart Issues a command instructing the ngrok agent to restart. The agent restarts itself by calling exec() on platforms that support it. This operation is notably not supported on Windows. When an agent restarts, it reconnects with a new tunnel session ID. ### Usage ```sh theme={null} ngrok api tunnel-sessions restart [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tunnel-sessions stop Issues a command instructing the ngrok agent that started this tunnel session to exit. ### Usage ```sh theme={null} ngrok api tunnel-sessions stop [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tunnel-sessions update Issues a command instructing the ngrok agent to update itself to the latest version. After this call completes successfully, the ngrok agent will be in the update process. A caller should wait some amount of time to allow the update to complete (at least 10 seconds) before making a call to the Restart endpoint to request that the agent restart itself to start using the new code. This call will never update an ngrok agent to a new major version which could cause breaking compatibility issues. If you wish to update to a new major version, that must be done manually. Still, please be aware that updating your ngrok agent could break your integration. This call will fail in any of the following circumstances: there is no update available the ngrok agent's configuration disabled update checks the agent is currently in process of updating the agent has already successfully updated but has not yet been restarted ### Usage ```sh theme={null} ngrok api tunnel-sessions update [flags] ``` ### Flags | Flag | Description | | - | - | | `--version` | request that the ngrok agent update to this specific version instead of the latest available version | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tunnels Tunnels provide endpoints to access services exposed by a running ngrok agent tunnel session or an SSH reverse tunnel session. ### SubCommands | Command | Description | | - | - | | [get](#ngrok-api-tunnels-get) | Get the status of a tunnel by ID | | [list](#ngrok-api-tunnels-list) | List all online tunnels currently running on... | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tunnels get Get the status of a tunnel by ID ### Usage ```sh theme={null} ngrok api tunnels get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api tunnels list List all online tunnels currently running on the account. ### Usage ```sh theme={null} ngrok api tunnels list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api vaults Vaults is an api service for securely storing and managing sensitive data such as secrets, credentials, and tokens. ### SubCommands | Command | Description | | - | - | | [create](#ngrok-api-vaults-create) | Create a new Vault | | [delete](#ngrok-api-vaults-delete) | Delete a Vault | | [get](#ngrok-api-vaults-get) | Get a Vault by ID | | [get-secrets-by-vault](#ngrok-api-vaults-get-secrets-by-vault) | Get Secrets by Vault ID | | [list](#ngrok-api-vaults-list) | List all Vaults owned by account | | [update](#ngrok-api-vaults-update) | Update an existing Vault by ID | ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api vaults create Create a new Vault ### Usage ```sh theme={null} ngrok api vaults create [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | description of Vault | | `--metadata` | Arbitrary user-defined metadata for this Vault | | `--name` | Name of vault | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api vaults delete Delete a Vault ### Usage ```sh theme={null} ngrok api vaults delete [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api vaults get Get a Vault by ID ### Usage ```sh theme={null} ngrok api vaults get [flags] ``` ### Flags | Flag | Description | | - | - | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api vaults get-secrets-by-vault Get Secrets by Vault ID ### Usage ```sh theme={null} ngrok api vaults get-secrets-by-vault [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | | | `--limit` | | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api vaults list List all Vaults owned by account ### Usage ```sh theme={null} ngrok api vaults list [flags] ``` ### Flags | Flag | Description | | - | - | | `--before-id` | Expects a resource ID as its input. Returns earlier entries in the result set, sorted by ID. | | `--filter` | A CEL expression to filter the list results. Supports logical and comparison operators to match on fields such as id, metadata, created\_at, and more. See ngrok API Filtering for syntax and field details: [https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering) ([https://ngrok.com/docs/api/api-filtering](https://ngrok.com/docs/api/api-filtering)). | | `--limit` | Constrains the number of results in the dataset. See the API Overview ([https://ngrok.com/docs/api/index#pagination](https://ngrok.com/docs/api/index#pagination)) for details. | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | ## ngrok api vaults update Update an existing Vault by ID ### Usage ```sh theme={null} ngrok api vaults update [flags] ``` ### Flags | Flag | Description | | - | - | | `--description` | description of Vault | | `--metadata` | Arbitrary user-defined metadata for this Vault | | `--name` | Name of vault | | `--api-key` | API key to use | | `--config` | path to config files; they are merged if multiple | | `--log` | path to log file, 'stdout', 'stderr' or 'false' | | `--log-format` | log record format: 'term', 'logfmt', 'json' | | `--log-level` | logging level: 'debug', 'info', 'warn', 'error', 'crit' | # ngrok Agent CLI Configuration File Source: https://ngrok.com/docs/gateway/agent/config/index Learn about the ngrok Agent CLI configuration file for running multiple tunnels simultaneously and configuring advanced settings. ## Overview The ngrok agent supports an optional, YAML configuration file which provides you with the power to run multiple tunnels simultaneously as well as to tweak some of its more advanced settings. YAML is whitespace-sensitive. Use spaces only (tabs are not valid in YAML) and keep keys at the same level aligned to the same column. ✅ Valid: ```yaml theme={null} endpoints: - name: example upstream: url: 80 ``` ❌ Invalid—`upstream` is indented inconsistently and will fail to parse: ```yaml theme={null} endpoints: - name: example upstream: url: 80 ``` ## Default locations The default location of the ngrok agent's configuration file varies based on your operating system. The easiest way to find the configuration file location is to run: ```sh theme={null} ngrok config check ``` which will validate and print the location of the configuration file. For the main operating systems supported, their default file locations are: * Linux: `~/.config/ngrok/ngrok.yml` * MacOS (Darwin): `~/Library/Application Support/ngrok/ngrok.yml` * Windows: `"%LocalAppData%\ngrok\ngrok.yml"` (`%LocalAppData` is usually `%UserProfile%\AppData\Local`.) ## Config file merging The `--config` flag in the `ngrok` command allows you to specify one or more ngrok configuration files to merge when starting the ngrok agent. The rules for merging are as follows: * Values from later configs override those from earlier configs. For example, if config1.yml and config2.yml both set `api_key`, then calling `ngrok http --config config1.yml,config2.yml 80` will use the `api_key` from config2.yml. * Lists are overwritten using the last value. * Maps, including the `tunnels` map, are merged by key and in the case of a duplicate key, the latter value is taken. ## Environment variables The ngrok agent supports environment variables for some configuration. Environment variables take precedence over the corresponding values specified in the configuration file. | Name | Configuration Property | | - | - | | `NGROK_AUTHTOKEN` | [`authtoken`](/docs/gateway/agent/config/v3/#authtoken) | | `NGROK_API_KEY` | [`api_key`](/docs/gateway/agent/config/v3/#api-key) | ## Configuration file formats * [Version 3](/docs/gateway/agent/config/v3/) * [Version 2](/docs/gateway/agent/config/v2/) (*deprecated*) # Agent Config Version 2 Source: https://ngrok.com/docs/gateway/agent/config/v2 Comprehensive reference documentation for ngrok Agent Configuration File version 2, including all configuration options and examples. ## Full example Below is a comprehensive configuration file for ngrok with detailed documentation for each option. To activate a specific option or tunnel, simply uncomment the relevant lines by removing the `#` at the beginning of the line. ```yaml theme={null} # ngrok Configuration File v2 # https://ngrok.com/docs/agent/config/ # ###################################################################### # # Agent Configuration # # ###################################################################### # ---------------------------------------------------------------------- # | Authtoken (required) | # ---------------------------------------------------------------------- # Specifies the authentication token (authtoken) used to connect to the # ngrok service. # # (1) You can get your default authtoken through the dashboard: # https://dashboard.ngrok.com/get-started/your-authtoken # # (2) You can view and generate authtokens through the dashboard # https://dashboard.ngrok.com/authtokens #authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p # ---------------------------------------------------------------------- # | API Key | # ---------------------------------------------------------------------- # The ngrok API key used to connect to the ngrok API. # # (!) This is only needed when using the ngrok api command and should # not be confused with the authtoken. # # (1) You can obtain and manage your API Keys through the dashboard: # https://dashboard.ngrok.com/api-keys #api_key: 24yRd5U3DestCQapJrrVHLOqiAC_7RviwRqpd3wc9dKLujQZN # ---------------------------------------------------------------------- # | Connect Timeout | # ---------------------------------------------------------------------- # How long to wait when establishing an agent session connection to the # ngrok service. # # Accepts duration, the default is 10s #connect_timeout: 30s # ---------------------------------------------------------------------- # | Console UI | # ---------------------------------------------------------------------- # Enable or disable the console UI in the terminal. # # Options: # true - Enable the console UI. # false - Disable the console UI, use structured log format. # iftty - (Default) Enable UI only if standard out is a TTY. #console_ui: iftty # ---------------------------------------------------------------------- # | Console UI Color | # ---------------------------------------------------------------------- # Sets the console UI background color in the terminal. # # To use a color other than black, set to `transparent` and adjust your # terminal's background. #console_ui_color: transparent # ---------------------------------------------------------------------- # | CRL No Verify | # ---------------------------------------------------------------------- # Skip Certificate Revocation List (CRL) verification if set to true. # # Accepts a boolean. Default is `false`. #crl_noverify: false # ---------------------------------------------------------------------- # | DNS Resolver IPs | # ---------------------------------------------------------------------- # List of DNS servers for resolving tunnel session DNS. # # Defaults to using the local system DNS servers. #dns_resolver_ips: # - 1.1.1.1 # - 8.8.8.8 # ---------------------------------------------------------------------- # | Heartbeat Interval | # ---------------------------------------------------------------------- # How often the ngrok agent should heartbeat to the ngrok servers defined # as a duration. # # Accepts a duration (for example, 10s, 1m). The default value is `10s`. #heartbeat_interval: 1m # ---------------------------------------------------------------------- # | Heartbeat Tolerance | # ---------------------------------------------------------------------- # This setting defines the maximum duration to wait for a heartbeat # response from the server before reconnecting the agent tunnel session. # # Accepts a duration (for example, 10s, 1m). The default value is `15s`. #heartbeat_tolerance: 5s # ---------------------------------------------------------------------- # | Inspect DB Size | # ---------------------------------------------------------------------- # This is the upper limit in bytes on memory to allocate when saving # requests over HTTP tunnels for inspection and reply. # # Accepts a numeric value. The default is `0`, equivalent to 50MB. # # (!) To disable inspection for all tunnels, set the value to `-1`. #inspect_db_size: 104857600 # 100mb # ---------------------------------------------------------------------- # | Log Level | # ---------------------------------------------------------------------- # Sets the log detail level. Higher verbosity with each level. # # Allowed values: # crit - Critical issues. # warn - Warnings. # error - Errors. # info - Informational messages. # debug - Detailed debugging info. #log_level: info # ---------------------------------------------------------------------- # | Log Format | # ---------------------------------------------------------------------- # Specifies the format of log records. # # Allowed values: # logfmt - Human and machine-friendly key/value pairs. # json - Newline-separated JSON objects. # term - Colored format for TTY; otherwise, same as logfmt. #log_format: json # ---------------------------------------------------------------------- # | Log | # ---------------------------------------------------------------------- # This is the destination where ngrok should write the logs. # # Allowed values: # stdout - write to standard out # stderr - write to standard error # false - disable logging # - write log records to file path on disk #log: /var/log/ngrok.log # ---------------------------------------------------------------------- # | Metadata | # ---------------------------------------------------------------------- # Custom string included in the ngrok API response for identifying tunnels. # # Maximum 4096 characters. #metadata: '{"serial": "00012xa-33rUtz9", "comment": "For customer alan@example.com"}' # ---------------------------------------------------------------------- # | Proxy URL | # ---------------------------------------------------------------------- # URL of an HTTP or SOCKS5 proxy for tunnel connections. # # (!) ngrok will also respect the `http_proxy` and `http_proxy_env` # environment variables. #proxy_url: socks5://localhost:9150 # ---------------------------------------------------------------------- # | Remote Management | # ---------------------------------------------------------------------- # Allows remote management of the ngrok agent (stop, restart, update) via # the ngrok API or Dashboard. # # Defaults to `true`. #remote_management: false # ---------------------------------------------------------------------- # | Root CAs | # ---------------------------------------------------------------------- # Root certificate authorities used to validate TLS connections to the # ngrok server. # # Allowed values: # trusted - Use only the trusted certificate root for ngrok.com. # host - Use the root certificates trusted by the host's OS (useful for MITM proxies with DPI). # - Path to a PEM file with additional trusted certificate authorities. #root_cas: trusted # ---------------------------------------------------------------------- # | Server Address | # ---------------------------------------------------------------------- # This is the URL of the ngrok server to connect to. # # (!) You should only set this value if you are using a Custom Agent # Ingress URL. # # https://ngrok.com/docs/agent/connect-url/#custom-connect-url #server_addr: tunnel.us.ingress.example.com:443 # ---------------------------------------------------------------------- # | Update Channel | # ---------------------------------------------------------------------- # Determines the stability of builds for updates. # # (!) Use `stable` for any production deployments. # # Allowed values: # stable - (Default) Production-ready builds. # unstable - Nightly builds; may be unstable. Not for production. # beta - Beta builds; may be unstable. Not for production. #update_channel: stable # ---------------------------------------------------------------------- # | Update Check | # ---------------------------------------------------------------------- # Controls whether the ngrok agent checks for updates. # # Defaults to true. #update_check: false # ---------------------------------------------------------------------- # | Version (required) | # ---------------------------------------------------------------------- # Specifies the version of the config file to use. version: 2 # ---------------------------------------------------------------------- # | Web Address | # ---------------------------------------------------------------------- # Network address to bind for the local agent web interface and API. # # https://ngrok.com/docs/agent/web-inspection-interface/ # https://ngrok.com/docs/agent/api/ #web_addr: localhost:4040 # ---------------------------------------------------------------------- # | Web Allow Hosts | # ---------------------------------------------------------------------- # List of allowed Host headers for requests to the local agent web # interface and API. The port is stripped from the Host header before # matching. # # Allowed formats: # 8.8.8.8 - Matches exact IP in Host header (for example, 8.8.8.8). # 1:2:3:4:5:6:7:8 - Matches exact IPv6 in Host header. # 10.0.0.0/8 - Matches any IP within a CIDR range. # example.com - Matches exact hostname. # .example.com - Matches subdomains (for example, sub.example.com). # # The default only allows localhost-like Hosts (localhost, 127.0.0.1, # ::1, and similar). #web_allow_hosts: # - 8.8.8.8 # - example.com # ###################################################################### # # Tunnels Configuration # # ###################################################################### # Define individual tunnels and their configurations. # # Each entry under 'tunnels' represents a named tunnel that can be # started using the 'ngrok start' command: # https://ngrok.com/docs/agent/cli/#ngrok-start # # To start all tunnels at once, use: # ngrok start --all # # List of common configuration fields: # https://ngrok.com/docs/agent/config/#common-tunnel-configuration-properties # # List of HTTP configuration fields: # https://ngrok.com/docs/agent/config/#http-configuration # # List of TCP configuration fields: # https://ngrok.com/docs/agent/config/#tcp-configuration # # List of TLS tunnel configuration fields: # https://ngrok.com/docs/agent/config/#tls-configuration # # List of Labeled tunnel configuration fields: # https://ngrok.com/docs/agent/config/#labeled-tunnel-configuration tunnels: # -------------------------------------------------------------------- # | Basic Tunnel Example | # -------------------------------------------------------------------- # Tunnel configuration for a website protected by basic authentication # with a custom host header. # # Start with after uncommenting: # ngrok start example-website # example-website: # addr: 8888 # basic_auth: # - "bob:bobpassword" # schemes: # - https # host_header: "app.local" # inspect: false # proto: http # domain: myapp.ngrok.app # -------------------------------------------------------------------- # | End-to-End TLS Tunnel | # -------------------------------------------------------------------- # Tunnel configuration for end-to-end TLS connections. # # Start with after uncommenting: # ngrok start example-e2e-tls # example-e2e-tls: # addr: 9000 # proto: tls # domain: myapp.example.com # crt: example.crt # key: example.key # -------------------------------------------------------------------- # | Tunnel with Traffic Policy | # -------------------------------------------------------------------- # Tunnel with a Traffic Policy. # # Start with after uncommenting: # ngrok start example-traffic-policy # # More info: # https://ngrok.com/docs/traffic-policy/ # example-traffic-policy: # traffic_policy: # on_tcp_connect: # - name: LimitIPs # expressions: # - "conn.client_ip != '1.1.1.1'" # Example rule to deny specific IPs. # actions: # - type: deny # addr: 8000 # proto: tcp # -------------------------------------------------------------------- # | SSH Access Tunnel | # -------------------------------------------------------------------- # Tunnel for SSH access. # # Start with: # ngrok start example-ssh-access # example-ssh-access: # addr: 22 # proto: tcp # remote_addr: 1.tcp.ngrok.io:12345 # -------------------------------------------------------------------- # | Load-Balanced Website Tunnel (Edges) | # -------------------------------------------------------------------- # Tunnel with load balancing based on labels. # # If you uncomment this, you can start this tunnel by running: # # ngrok start example-load-balanced-edge # example-load-balanced-edge: # labels: # - env=prod # - team=infra # addr: 8000 ``` ## Tunnel configurations The most common use of the configuration file is to define tunnel configurations. Defining tunnel configurations is useful because you may then start pre-configured tunnels by name from your command line without remembering all of the right arguments every time. You may also use this method to start as many tunnels as you like from a single ngrok agent. Tunnels are defined as mapping of name -> configuration under the `tunnels` property in your configuration file. ##### Define two tunnels named 'httpbin' and 'demo' ```yaml theme={null} tunnels: httpbin: proto: http addr: 8000 domain: alan-httpbin.ngrok.app demo: proto: http addr: 9090 domain: demo.inconshreveable.com inspect: false ``` ##### Start the tunnel named 'httpbin' ```sh theme={null} ngrok start httpbin ``` Each tunnel you define is a map of configuration option names to values. The name of a configuration option is usually the same as its corresponding command line switch with hyphens (`--host-header` becomes `host_header:` in the configuration file and `--url` becomes `url`). Tunnels can define a specific `proto` or use labels to dynamically connect to one or more matching ngrok Edges. All tunnels must define a specific `addr` that tells the agent where to send the traffic. Other properties are available and many are protocol-specific. ##### Start all tunnels defined in the configuration file ```sh theme={null} ngrok start --all ``` You can configure a single ngrok agent to tunnel to multiple services within a single agent session. ##### Common tunnel configuration properties | | | | | - | - | - | | `addr` | required | forward traffic to this local port number or network address. This can be just a port | | `metadata` | Optional | arbitrary user-defined metadata that will appear in the ngrok service API when listing tunnel sessions | ### HTTP configuration | | | | | - | - | - | | `basic_auth` | Array of `username:password` Strings | This is a list of username:password combinations to use for basic authenticate. Passwords must be at least 8 characters long. | | `circuit_breaker` | Float | Reject requests when 5XX responses exceed this ratio | | `compression` | `true`, `false` | gzip compress HTTP responses from your web service | | `host_header` | `rewrite`, `preserve`, custom | Rewrite the HTTP Host header to this value, or `preserve` to leave it unchanged. The `rewrite` option will rewrite the host header to match the hostname of the upstream service you are sending traffic to. | | `domain` | Any valid domain or hostname that you have previously registered with ngrok. | The domain to request. If using a custom domain, this requires registering in the [ngrok dashboard](https://dashboard.ngrok.com/domains) and setting a DNS CNAME value. When creating wildcard endpoints, you will need to surround the value with single quotes (domain: '\*.example.com'). | | `inspect` | `true`, `false` | enable/disable the http request inspection in the web and agent API (default: true) | | `ip_restriction.allow_cidrs` | Array of CIDRs | Rejects connections that don't match the given CIDRs | | `ip_restriction.deny_cidrs` | Array of CIDRs | Rejects connections that match the given CIDRs and allows all other CIDRs. | | `mutual_tls_cas` | Valid system path | The path to the TLS certificate authority to verify client certs in mutual TLS | | `oauth.allow_domains` | Array of Strings | Allow only OAuth2 users with these email domains | | `oauth.allow_emails` | Array of Strings | Allow only OAuth users with these emails | | `oauth.scopes` | Array of Strings | Request these OAuth2 scopes when a user authenticates | | `oauth.provider` | String | enforce authentication OAuth2 provider on the endpoint, for example, 'google'. For a lit of available providers, see [OAuth2 providers](/docs/gateway/traffic-policy/actions/oauth/#supported-providers). | | [`policy.inbound.name`](/docs/gateway/traffic-policy/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.inbound.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.inbound.actions.type`](/docs/gateway/traffic-policy/actions/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.inbound.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.name`](/docs/gateway/traffic-policy/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.actions.type`](/docs/gateway/traffic-policy/actions/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | `proto` | `http` | The tunnel protocol name. This defines the type of tunnel you would like to start. | | `proxy_proto` | String | The version of [PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) to use with this tunnel, empty if not using. Example values are 1 or 2. | | `request_header.add` | Array of `key:value` Strings | The headers to add to the request in the key:value format. | | `request_header.remove` | Array of Strings | The header keys to remove from the request. | | `response_header.add` | Array of Strings | The headers to add to the response in the key:value format. | | `response_header.remove` | Array of Strings | The header keys to remove from the response. | | `schemes` | `http`, `https` | Create an HTTP or HTTPS endpoint (or both) | | `subdomain` | Any valid combination of letters, numbers, hyphens or periods. | subdomain name to request. If unspecified, ngrok provides a unique subdomain based on your account type. | | [`traffic_policy.on_http_request.name`](/docs/gateway/traffic-policy/) | String | The name of an `on_http_request` rule that is part of a Traffic Policy | | [`traffic_policy.on_http_request.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | Expressions written using [CEL](https://github.com/google/cel-spec) that filter traffic the `on_http_request` policy rule applies to | | [`traffic_policy.on_http_request.actions.type`](/docs/gateway/traffic-policy/actions/) | String | The type of action that should be executed when this `on_http_request` policy rule is activated | | [`traffic_policy.on_http_request.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | The configuration required for the specific type of action specified | | [`traffic_policy.on_http_response.name`](/docs/gateway/traffic-policy/) | String | The name of an `on_http_response` rule that is part of a Traffic Policy | | [`traffic_policy.on_http_response.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | Expressions written using [CEL](https://github.com/google/cel-spec) that filter traffic the `on_http_response` policy rule applies to | | [`traffic_policy.on_http_response.actions.type`](/docs/gateway/traffic-policy/actions/) | String | The type of action that should be executed when this `on_http_response` policy rule is activated | | [`traffic_policy.on_http_response.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | The configuration required for the specific type of action specified | | `user_agent_filter.allow` | Array of Strings | Allows User-Agents that match against these RE2 Regular Expressions | | `user_agent_filter.deny` | Array of Strings | Denies User-Agents that match against these RE2 Regular Expressions | | `verify_webhook.provider` | String | Verify webhooks are signed by this provider, for example, 'slack'. For a full list of providers, see [Webhook Verification Providers](/docs/gateway/traffic-policy/actions/verify-webhook/#supported-providers). | | `verify_webhook.secret` | String | The secret used by provider to sign webhooks, if there is one | | `websocket_tcp_converter` | `true`, `false` | Convert ingress websocket connections to TCP upstream | ### TCP configuration | | | | | - | - | - | | `ip_restriction.allow_cidrs` | Array of CIDRs | Rejects connections that don't match the given CIDRs | | `ip_restriction.deny_cidrs` | Array of CIDRs | Rejects connections that match the given CIDRs and allows all other CIDRs. | | [`policy.inbound.name`](/docs/gateway/traffic-policy/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.inbound.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.inbound.actions.type`](/docs/gateway/traffic-policy/actions/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.inbound.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.name`](/docs/gateway/traffic-policy/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.actions.type`](/docs/gateway/traffic-policy/actions/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | `proto` | `tcp` | The tunnel protocol name. This defines the type of tunnel you would like to start. | | `proxy_proto` | String | The version of [PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) to use with this tunnel, empty if not using. Example values are 1 or 2. | | `remote_addr` | A valid TCP address from ngrok | bind the remote TCP address and port. These addresses can be reserved in the [ngrok dashboard](https://dashboard.ngrok.com/tcp-addresses) to use across sessions. For example: `remote_addr: 2.tcp.ngrok.io:21746` | | [`traffic_policy.inbound.name`](/docs/gateway/traffic-policy/) | String | The name of an inbound rule that is part of a Traffic Policy | | [`traffic_policy.inbound.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | Expressions written using [CEL](https://github.com/google/cel-spec) that filter traffic the inbound policy rule applies to | | [`traffic_policy.inbound.actions.type`](/docs/gateway/traffic-policy/actions/) | String | The type of action that should be executed when this inbound policy rule is activated | | [`traffic_policy.inbound.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | The configuration required for the specific type of action specified | | [`traffic_policy.outbound.name`](/docs/gateway/traffic-policy/) | String | The name of an outbound rule that is part of a Traffic Policy | | [`traffic_policy.outbound.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | Expressions written using [CEL](https://github.com/google/cel-spec) that filter traffic the outbound policy rule applies to | | [`traffic_policy.outbound.actions.type`](/docs/gateway/traffic-policy/actions/) | String | The type of action that should be executed when this outbound policy rule is activated | | [`traffic_policy.outbound.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | The configuration required for the specific type of action specified | ### TLS configuration | | | | | - | - | - | | `mutual_tls_cas` | Valid system path | The path to the TLS certificate authority to verify client certs for mutual TLS. You will also need to specify `key` and `crt` to enable mutual TLS. | | `crt` | Valid system path | PEM TLS certificate at this path to terminate TLS traffic before forwarding locally. Requires `--key` to also be specified. | | `domain` | Any valid domain or hostname that you have previously registered with ngrok. | The domain to request. If using a custom domain, this requires registering in the ngrok Dashboard and setting a DNS CNAME value. When creating wildcard endpoints, you will need to surround the value with single quotes (domain: '\*.example.com'). | | `ip_restriction.allow_cidrs` | Array of CIDRs | Rejects connections that don't match the given CIDRs | | `ip_restriction.deny_cidrs` | Array of CIDRs | Rejects connections that match the given CIDRs and allows all other CIDRs. | | `key` | Valid system path | PEM TLS private key at this path to terminate TLS traffic before forwarding locally. Requires `--crt` to also be specified. | | [`policy.inbound.name`](/docs/gateway/traffic-policy/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.inbound.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.inbound.actions.type`](/docs/gateway/traffic-policy/actions/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.inbound.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.name`](/docs/gateway/traffic-policy/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.actions.type`](/docs/gateway/traffic-policy/actions/) | String | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | [`policy.outbound.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | `policy` has been renamed to `traffic_policy` in agent version 3.14.0. | | `proto` | `tls` | The tunnel protocol name. This defines the type of tunnel you would like to start. | | `proxy_proto` | String | The version of [PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) to use with this tunnel, empty if not using. Example values are 1 or 2. | | `subdomain` | Any valid combination of letters, numbers, hyphens, or periods. | subdomain name to request. If unspecified, ngrok provides a unique subdomain based on your account type. | | `terminate_at` | `edge`, `agent`, or `upstream` | Terminate at ngrok `edge`, `agent`, or `upstream`. Defaults to `upstream` unless `--crt` or `--key` are present, in which case `edge` is the default. | | [`traffic_policy.inbound.name`](/docs/gateway/traffic-policy/) | String | The name of an inbound rule that is part of a Traffic Policy | | [`traffic_policy.inbound.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | Expressions written using [CEL](https://github.com/google/cel-spec) that filter traffic the inbound policy rule applies to | | [`traffic_policy.inbound.actions.type`](/docs/gateway/traffic-policy/actions/) | String | The type of action that should be executed when this inbound policy rule is activated | | [`traffic_policy.inbound.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | The configuration required for the specific type of action specified | | [`traffic_policy.outbound.name`](/docs/gateway/traffic-policy/) | String | The name of an outbound rule that is part of a Traffic Policy | | [`traffic_policy.outbound.expressions`](/docs/gateway/traffic-policy/how-it-works#expressions) | Array of Strings | Expressions written using [CEL](https://github.com/google/cel-spec) that filter traffic the outbound policy rule applies to | | [`traffic_policy.outbound.actions.type`](/docs/gateway/traffic-policy/actions/) | String | The type of action that should be executed when this outbound policy rule is activated | | [`traffic_policy.outbound.actions.config`](/docs/gateway/traffic-policy/actions/) | custom | The configuration required for the specific type of action specified | ### Labeled tunnel configuration | | | | | - | - | - | | `crt` | Valid system path | The path to a TLS certificate when doing TLS termination at the agent. | | `inspect` | `true`, `false` | enable/disable the http request inspection in the web and agent API (default: true) | | `key` | Valid system path | The path to a TLS key when doing TLS termination at the agent. | | `labels` | A list of labels (name=value) that can be used to identify a tunnel to an ngrok Edge (specifically a tunnel group backend). | The labels for this tunnel in the format name=value. For example: `edge=edghts_2gYaK9XAVa3ANouaDEPrkeaxkYT` | ##### Define two labeled tunnels ```yaml theme={null} tunnels: my-cool-website: labels: - env=prod - team=infra addr: 8000 inspect: false ssh-tunnel: labels: - hostname=my-hostname - service=ssh - team=development addr: 22 ``` When you use labeled tunnels, the `protocol` and `domain` are configured by the matching edge. Edges automatically create an `edge` label, for example: `edge=edghts_2gYaK9XAVa3ANouaDEPrkeaxkYT` ## Agent configuration The following is a list of options that can be configured at the root of your configuration file and specify the behavior of the agent. | Name | Description | | - | - | | [api\_key](#api-key) | Specifies the ngrok API key used to connect to the ngrok API. This is only needed when using the `ngrok api` command and should not be confused with the authtoken. | | [authtoken](#authtoken) | Specifies the authentication token (authtoken) used to connect to the ngrok service. | | [connect\_interface](#connect-interface) | Set the specific network interface for ngrok to use. This is only supported on Linux platforms. | | [connect\_timeout](#connect-timeout) | How long to wait when establishing an agent session connection to the ngrok service. The default is 10s. | | [console\_ui](#console-ui) | Enable/disable the console UI | | [console\_ui\_color](#console-ui-color) | Set the background color of the console UI | | [crl\_noverify](#crl-noverify) | Disables verifying Certificate Revocation List | | [dns\_resolver\_ips](#dns-resolver-ips) | Consult these DNS servers for tunnel session DNS resolution. | | [heartbeat\_interval](#heartbeat-interval) | How often the ngrok agent should heartbeat to the ngrok servers defined as a duration. Default is 10s. | | [heartbeat\_tolerance](#heartbeat-tolerance) | Reconnect the agent tunnel session if the server doesn't respond to a heartbeat within this tolerance defined as a duration. Default is 15s. | | [inspect\_db\_size](#inspect-db-size) | The size in bytes of the upper limit on memory to allocate to save requests over HTTP tunnels for inspection and replay. | | [log\_level](#log-level) | Logging level of detail. In increasing order of verbosity, possible values are: `crit`, `warn`, `error`, `info`, and `debug`. | | [log\_format](#log-format) | Format of written log records. | | [log](#log) | Write logs to this target destination. | | [metadata](#metadata) | Opaque, user-supplied string that will be returned as part of the ngrok API response to the [list online sessions](/docs/api-reference/tunnelsessions/list) resource for all tunnels started by this agent. | | [proxy\_url](#proxy-url) | URL of an HTTP or SOCKS5 proxy to use for establishing the tunnel connection. | | [region](#region) *(Deprecated)* | Choose the region where the ngrok agent will connect to host its tunnels. | | [remote\_management](#remote-management) | Set this to `true` to allow the ngrok agent to be remotely managed (stop, restart, update). Defaults to `true`. | | [root\_cas](#root-cas) | The root certificate authorities used to validate the TLS connection to the ngrok server. | | [server\_addr](#server-addr) | This is the URL of the ngrok server to connect to. You should only set this if you are using a custom ingress URL. | | [tunnels](#tunnels) | A map of names to tunnel definitions. See [V2 agent config tunnel definitions](/docs/gateway/agent/config/v2/#tunnel-configurations) for more details. | | [update\_channel](#update-channel) | The update channel determines the stability of released builds to update to. Use `stable` for all production deployments. | | [update\_check](#update-check) | This tells the ngrok agent if it should check for updates. Defaults to `true`. | | [version](#version) | Specifies the version of the config file to use. | | [web\_addr](#web-addr) | Network address to bind on for serving the local web interface and api. | | [web\_allow\_hosts](#web-allow-hosts) | Host headers to allow access for on the local web interface and api, can be a combination of IP's, CIDR ranges, and/or hostname suffixes. | ### `api_key` This option specifies the API key used to access the ngrok API through the [`ngrok api`](/docs/gateway/agent/cli-api) command. This is only needed when using the [ngrok API](/docs/api) and not the local ngrok agent API (available at `localhost:4040/api`). You can generate an API Key in the [ngrok Dashboard](https://dashboard.ngrok.com/api-keys) and install it using the `ngrok config add-api-key` command. ##### ngrok.yml specifying an API key ```yaml theme={null} api_key: 24yRd5U3DestCQapJrrVHLOqiAC_7RviwRqpd3wc9dKLujQZN ``` ### `authtoken` This option specifies the authentication token (sometimes called tunnel credential) used to authenticate this agent when it connects to the ngrok service. After you've created an ngrok account, your dashboard will display the authtoken assigned to your account. Your authtoken will work on multiple machines if you are just developing. When you want to deploy many agents on many devices, you can generate a unique authtoken for each device in the ngrok Dashboard or via the `ngrok api credentials` command. ##### ngrok.yml specifying an authtoken ```yaml theme={null} authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p ``` ### `connect_interface` Sets the specific network interface that the ngrok agent should use. This is only supported on Linux platforms. ### `connect_timeout` How long to wait when establishing an agent session connection to the ngrok service. This is specified as a duration, with the default being 10s. ### `console_ui` This option allows you to enable or disable the console UI that is displayed in your terminal window after starting ngrok. | | | | | - | - | - | | `true` | | Enable the console UI | | `false` | | Disable the console UI | | `iftty` | default | Enable the UI only if standard out is a TTY (not a file or pipe) | ### `console_ui_color` The command sets the background color when displaying the console UI in the terminal. To choose a color other than black, set the value to transparent and change the background of your terminal window. | | | | | - | - | - | | `transparent` | | Don't set a background color when displaying the console UI | | `black` | default | Set the console UI's background to black | ### `crl_noverify` This option will skip verifying with the Certificate Revocation List if set to `true`. This defaults to `false`. ### `dns_resolver_ips` Consult these DNS servers for tunnel session DNS resolution. By default, the ngrok agent will use the local system DNS servers to resolve. ### `heartbeat_interval` How often the ngrok agent should heartbeat to the ngrok servers defined as a duration. The default is 10s. ### `heartbeat_tolerance` Reconnect the agent tunnel session if the server doesn't respond to a heartbeat within this tolerance defined as a duration. The default is 15s. ### `inspect_db_size` This is the upper limit in bytes on memory to allocate when saving requests over HTTP tunnels for inspection and reply. The default is 0, which means 50MB. | | | | | - | - | - | | positive integers | | size in bytes of the upper limit on memory to allocate to save requests over HTTP tunnels for inspection and replay. | | `0` | default | use the default allocation limit, 50MB | | `-1` | | disable the inspection database; this has the effective behavior of disabling inspection for all tunnels | ### `log_level` This is the logging level of detail. In increasing order of verbosity, possible values are: `crit`, `warn`, `error`, `info`, and `debug`. ### `log_format` This is the format of written log records. | | | | | - | - | - | | `logfmt` | | human and machine friendly key/value pairs | | `json` | | newline-separated JSON objects | | `term` | default | custom colored human format if standard out is a TTY, otherwise same as `logfmt` | ### `log` This is the destination where ngrok should write the logs. | | | | | - | - | - | | `stdout` | | write to standard out | | `stderr` | | write to standard error | | `false` | default | disable logging | | other values | | write log records to file path on disk | ```yaml theme={null} log: /var/log/ngrok.log ``` ### `metadata` This is a user-supplied custom string that will be returned as part of the ngrok API response to the [list online sessions resource](/docs/api-reference/tunnelsessions/list) for all tunnels started by this agent. This is a useful mechanism to identify tunnels by your own device or customer identifier. Maximum 4096 characters. ```yaml theme={null} metadata: bad8c1c0-8fce-11e4-b4a9-0800200c9a66 ``` ### `proxy_url` This is the URL of an HTTP or SOCKS5 proxy to use for establishing the tunnel connection. Many HTTP proxies have connection size and duration limits that will cause ngrok to fail. Like many other networking tools, ngrok will also respect the environment variable `http_proxy` and `http_proxy_env` if it is set. ### `region` *Deprecated* This is the region where the ngrok agent will connect to. You can only choose one region per agent session. Choosing the region closest to you usually improves latency and performance. By default, the ngrok agent attempts to choose the best region for you. | Region Code | Region Name | | - | - | | `ap` | Asia/Pacific (Singapore) | | `au` | Australia (Sydney) | | `eu` | Europe (Frankfurt) | | `in` | India (Mumbai) | | `jp` | Japan (Tokyo) | | `sa` | South America (São Paulo) | | `us` | United States (Ohio) | | `us-cal-1` | United States (California) | ### `remote_management` Set this to `true` to allow the ngrok agent to be remotely managed (stop, restart, update) via the [ngrok API](/docs/api-reference/tunnelsessions/restart) or the [ngrok Dashboard](https://dashboard.ngrok.com/agents). Defaults to `true`. ### `root_cas` This is the root certificate authorities used to validate the TLS connection to the ngrok server. | | | | | - | - | - | | `trusted` | default | use only the trusted certificate root for the ngrok.com tunnel service | | `host` | | use the root certificates trusted by the host's operating system. This is helpful for working with machine-in-the-middle (MITM) proxies doing deep packet inspection (DPI). | | other values | | path to a certificate PEM file on disk with certificate authorities to trust | ### `server_addr` This is the URL of the ngrok server to connect to. You should set this if you are using a custom ingress URL. ### `tunnels` This is a map of names to tunnel definitions. See [tunnel definitions](#tunnel-configurations) for more details. ### `update_channel` The update channel determines the stability of released builds to update to. Use 'stable' for all production deployments. | | | | | - | - | - | | `stable` | default | These are builds that are ready to be used in production. | | `unstable` | | update to new nightly builds when available which could be broken. This should not be used in production. | | `beta` | | update to new beta builds when available which could be broken. This should not be used in production. | ### `update_check` This tells the ngrok agent if it should check for updates. Defaults to `true`. ### `version` Specifies the version of the config file to use. ### `web_addr` This is the network address to bind on for serving the local agent web interface and API. | Network address | | Bind to this network address | | - | - | - | | `127.0.0.1:4040` | default | default network address | | `false` | | disable the web UI | ### `web_allow_hosts` These are a list of specifiers for what Host headers will be allowed to make requests agains the local agent web interface and API. Any port is stripped off the Host header before matching is performed. | Allow string | | Example Host headers that would match | | - | - | - | | | default | requests to localhost-bound web interface or API endpoints are checked to have a localhost-like Host such as localhost, 127.0.0.1, or ::1 | | 8.8.8.8 | | an IP matches Host header (8.8.8.8) | | 1:2:3:4:5:6:7:8 | | an IPv6 matches Host header (1:2:3:4:5:6:7:8) | | 10.0.0.0/8 | | a CIDR range matches a Host header that is an IP address in that range (10.0.0.1 or 10.1.2.3) | | 1:2:3:4:5:6:7:8/16 | | a CIDR range matches a Host header that is an IPv6 address in that range (1:2:3:4:5:6:7:0) | | example.com | | a hostname without preceding period will match Host header exactly (example.com) | | .example.com | | a hostname with a preceding period Host header suffix (sub.example.com or foo.example.com) | ##### Allow an IP address and a domain as Host headers ```yaml theme={null} web_allow_hosts: - 8.8.8.8 - example.com ``` # Agent Config Version 3 Source: https://ngrok.com/docs/gateway/agent/config/v3 Reference documentation for ngrok Agent Configuration File version 3, including breaking changes, migration guide, and endpoint definitions. The ngrok agent supports an optional, YAML configuration file which enables you to configure advanced settings and run multiple endpoints simultaneously. The agent configuration file location depends on your system. You can check the location of your configuration file by running `ngrok config check` or edit the file in your terminal by running `ngrok config edit`. YAML is whitespace-sensitive. Use spaces only (tabs are not valid in YAML) and keep keys at the same level aligned to the same column. ✅ Valid: ```yaml theme={null} endpoints: - name: example upstream: url: 80 ``` ❌ Invalid—`upstream` is indented inconsistently and will fail to parse: ```yaml theme={null} endpoints: - name: example upstream: url: 80 ``` ## Breaking changes There are important differences between the v3 ngrok Agent Configuration file and v2: * Agent configuration options are no longer top-level, they are now nested under the `agent` field. * Endpoints are now configured through the `endpoints` field. * Deprecated the `tunnels` field in favor of [`endpoints`](#endpoint-definitions). * Changed `server_addr` agent configuration field to [`connect_url`](#connect-url). * Changed `root_cas` agent configuration field to [`connect_cas`](#connect-cas). * Labeled tunnels are no longer supported in v3. Use [endpoints](/docs/api-reference/endpoints/list) instead. ## Migrating from v2 Migrating from v2 to v3 requires you to update to the latest agent, nest your agent configuration options under the `agent` field and change the `version` field to `3` as well as modify any [agent configuration fields that have changed](#breaking-changes). For example, here is an example v2 configuration file: ```yaml theme={null} version: 2 authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p api_key: 24yRd5U3DestCQapJrrVHLOqiAC_7RviwRqpd3wc9dKLujQZN tunnels: basic: proto: http addr: 80 ``` For v3, you need to update the `version`, introduce the `agent` field, and convert from `tunnels` to `endpoints`: ```yaml theme={null} version: 3 agent: authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p api_key: 24yRd5U3DestCQapJrrVHLOqiAC_7RviwRqpd3wc9dKLujQZN endpoints: - name: http url: http://demo.ngrok.app upstream: url: 80 ``` You will also need to adjust the following fields if you are using them: * Change `root_cas` to `connect_cas` * Change `server_addr` to [`connect_url`](#connect-url) ## Supported fields The ngrok Agent Configuration file supports the following top-level configuration fields: * [Configuration File Version Field](#version) - `version` (required) * [Agent Configuration Field](#agent-configuration) - `agent` (required) * [Endpoint Definitions](#endpoint-definitions) - `endpoints` * [Tunnel Definitions](#tunnels-deprecated) - `tunnels` (deprecated) ## Version The top-level `version` field is **required**. Value **must** be `3`. ### Example ```yaml theme={null} version: 3 ``` ## Agent configuration The top-level `agent` field is **required**. The agent field allows you to modify both general and advanced agent configuration options, everything from your authtoken to whether the agent should check for updates. ### Agent configuration options | Name | Description | | - | - | | [`api_key`](#api-key) | The API key to be used when making requests through the `ngrok api` command. | | [`authtoken`](#authtoken) | The authentication token used to authenticate this agent when it connects to the ngrok service. | | [`connect_cas`](#connect-cas) | The root certificate authorities used to validate the TLS connection to the ngrok server. | | [`connect_interface`](#connect-interface) | Set the specific network interface for ngrok to use. This is only supported on Linux platforms. | | [`connect_timeout`](#connect-timeout) | How long to wait when establishing an agent session connection to the ngrok service. The default is 10s. | | [`connect_url`](#connect-url) | This is the URL of the ngrok server to connect to. You should only set this if you are using a custom ingress URL. | | [`console_ui`](#console-ui) | Enable/disable the console UI | | [`console_ui_color`](#console-ui-color) | Set the background color of the console UI | | [`crl_noverify`](#crl-noverify) | Disables verifying Certificate Revocation List | | [`dns_resolver_ips`](#dns-resolver-ips) | Consult these DNS servers for tunnel session DNS resolution. | | [`heartbeat_interval`](#heartbeat-interval) | How often the ngrok agent should heartbeat to the ngrok servers defined as a duration. Default is 10s. | | [`heartbeat_tolerance`](#heartbeat-tolerance) | Reconnect the agent tunnel session if the server doesn't respond to a heartbeat within this tolerance defined as a duration. Default is 15s. | | [`inspect_db_size`](#inspect-db-size) | The size in bytes of the upper limit on memory to allocate to save requests over HTTP tunnels for inspection and replay. | | [`log_level`](#log-level) | Logging level of detail. In increasing order of verbosity, possible values are: `crit`, `warn`, `error`, `info`, and `debug`. | | [`log_format`](#log-format) | Format of written log records. | | [`log`](#log) | Write logs to this target destination. | | [`metadata`](#metadata) | Opaque, user-supplied string that will be returned as part of the ngrok API response to the [list online sessions](/docs/api-reference/tunnelsessions/list) resource for all endpoints started by this agent. | | [`proxy_url`](#proxy-url) | URL of an HTTP or SOCKS5 proxy to use for establishing the tunnel connection. | | [`remote_management`](#remote-management) | Set this to `true` to allow the ngrok agent to be remotely managed (stop, restart, update). Defaults to `true`. | | [`update_channel`](#update-channel) | The update channel determines the stability of released builds to update to. Use `stable` for all production deployments. | | [`update_check`](#update-check) | This tells the ngrok agent if it should check for updates. Defaults to `true`. | | [`web_addr`](#web-addr) | Network address to bind on for serving the local web interface and api. | | [`web_allow_hosts`](#web-allow-hosts) | `Host` headers to allow access for on the local web interface and api, can be a combination of IP's, CIDR ranges, and/or hostname suffixes. | #### `authtoken` The authentication token used to authenticate this agent when it connects to the ngrok service. You can obtain your default authtoken through the [ngrok Dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). ##### Deploying on multiple devices Your default authtoken will work on multiple machines or devices. However, if you want more control and security across many devices, you can generate a unique authtoken for each device in the [ngrok Dashboard](https://dashboard.ngrok.com/authtokens) or via the `ngrok api credentials` command. ##### Example ```yaml {2,2} theme={null} agent: authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p ``` #### `api_key` This option is only required when you want to make requests using the `ngrok api` command and should not be confused with the `authtoken` option. The API key to be used when making requests through the `ngrok api` command. You can obtain and manage your API Keys through the [ngrok Dashboard](https://dashboard.ngrok.com/api-keys). ##### Example ```yaml {2,2} theme={null} agent: api_key: 24yRd5U3DestCQapJrrVHLOqiAC_7RviwRqpd3wc9dKLujQZN ``` #### `connect_cas` This is the root certificate authorities used to validate the TLS connection to the ngrok server. | Parameter | Default | Description | | - | - | - | | `trusted` | default | use only the trusted certificate root for the ngrok.com tunnel service | | `host` | | use the root certificates trusted by the host's operating system. This is helpful for working with machine-in-the-middle (MITM) proxies doing deep packet inspection (DPI). | | other values | | path to a certificate PEM file on disk with certificate authorities to trust | | | | | #### `connect_interface` Sets the specific network interface that the ngrok agent should use. This is only supported on Linux platforms. #### `connect_timeout` How long to wait when establishing an agent session connection to the ngrok service. This is specified as a duration, with the default being 10s. #### `connect_url` This is the URL of the ngrok server to connect to. You should set this if you are using a custom Connect URL. See [the Connect URL docs](/docs/gateway/agent/connect-url/) for more information. #### `console_ui` This option allows you to enable or disable the console UI that is displayed in your terminal window after starting ngrok. | Parameter | Default | Description | | - | - | - | | `true` | | Enable the console UI | | `false` | | Disable the console UI | | `iftty` | default | Enable the UI only if standard out is a TTY (not a file or pipe) | #### `console_ui_color` The command sets the background color when displaying the console UI in the terminal. To choose a color other than black, set the value to transparent and change the background of your terminal window. | Parameter | Default | Description | | - | - | - | | `transparent` | | Don't set a background color when displaying the console UI | | `black` | default | Set the console UI's background to black | #### `crl_noverify` This option will skip verifying with the Certificate Revocation List if set to `true`. This defaults to `false`. #### `dns_resolver_ips` Consult these DNS servers for tunnel session DNS resolution. By default, the ngrok agent will use the local system DNS servers to resolve. #### `heartbeat_interval` How often the ngrok agent should heartbeat to the ngrok servers defined as a duration. The default is 10s. #### `heartbeat_tolerance` Reconnect the agent tunnel session if the server doesn't respond to a heartbeat within this tolerance defined as a duration. The default is 15s. #### `inspect_db_size` This is the upper limit in bytes on memory to allocate when saving requests over HTTP endpoints for inspection and reply. The default is 0, which means 50MB. | Parameter | Default | Description | | - | - | - | | positive integers | | size in bytes of the upper limit on memory to allocate to save requests over HTTP endpoints for inspection and replay. | | `0` | default | use the default allocation limit, 50MB | | `-1` | | disable the inspection database; this has the effective behavior of disabling inspection for all tunnels | #### `log_level` This is the logging level of detail. In increasing order of verbosity, possible values are: `crit`, `warn`, `error`, `info`, and `debug`. #### `log_format` This is the format of written log records. | Parameter | Default | Description | | - | - | - | | `logfmt` | | human and machine friendly key/value pairs | | `json` | | newline-separated JSON objects | | `term` | default | custom colored human format if standard out is a TTY, otherwise same as `logfmt` | #### `log` This is the destination where ngrok should write the logs. | Parameter | Default | Description | | - | - | - | | `stdout` | | write to standard out | | `stderr` | | write to standard error | | `false` | default | disable logging | | `` | | write log records to file path on disk | ```yaml {2,2} theme={null} agent: log: /var/log/ngrok.log ``` #### `metadata` This is a user-supplied custom string that will be returned as part of the ngrok API response to the [list online sessions resource](/docs/api-reference/tunnelsessions/list) for all endpoints started by this agent. This is a useful mechanism to identify endpoints by your own device or customer identifier. Maximum 4096 characters. ```yaml {2,2} theme={null} agent: metadata: bad8c1c0-8fce-11e4-b4a9-0800200c9a66 ``` #### `proxy_url` This is the URL of an HTTP or SOCKS5 proxy to use for establishing the tunnel connection. Many HTTP proxies have connection size and duration limits that will cause ngrok to fail. Like many other networking tools, ngrok will also respect the environment variable `http_proxy` and `http_proxy_env` if it is set. #### `remote_management` Set this to `true` to allow the ngrok agent to be remotely managed (stop, restart, update) via the [ngrok API](/docs/api-reference/tunnelsessions/restart) or the [ngrok Dashboard](https://dashboard.ngrok.com/agents). Defaults to `true`. #### `update_channel` The update channel determines the stability of released builds to update to. Use 'stable' for all production deployments. | Parameter | Default | Description | | - | - | - | | `stable` | default | These are builds that are ready to be used in production. | | `unstable` | | update to new nightly builds when available which could be broken. This should not be used in production. | | `beta` | | update to new beta builds when available which could be broken. This should not be used in production. | #### `update_check` This tells the ngrok agent if it should check for updates. Defaults to `true`. #### `web_addr` This is the network address to bind on for serving the local agent web interface and API. | Network address | | Bind to this network address | | - | - | - | | `127.0.0.1:4040` | default | default network address | | `false` | | disable the web UI | #### `web_allow_hosts` These are a list of specifiers for what `Host` headers will be allowed to make requests agains the local agent web interface and API. Any port is stripped off the `Host` header before matching is performed. | Allow string | | Example `Host` headers that would match | | - | - | - | | | default | requests to localhost-bound web interface or API endpoints are checked to have a localhost-like `Host` such as localhost, 127.0.0.1, or ::1 | | `8.8.8.8` | | an IP matches `Host` header (`8.8.8.8`) | | `1:2:3:4:5:6:7:8` | | an IPv6 matches `Host` header (`1:2:3:4:5:6:7:8`) | | `10.0.0.0/8` | | a CIDR range matches a `Host` header that is an IP address in that range (`10.0.0.1 or 10.1.2.3`) | | `1:2:3:4:5:6:7:8/16` | | a CIDR range matches a `Host` header that is an IPv6 address in that range (`1:2:3:4:5:6:7:0`) | | `example.com` | | a hostname without preceding period will match `Host` header exactly (`example.com`) | | `.example.com` | | a hostname with a preceding period `Host` header suffix (`sub.example.com` or `foo.example.com`) | ##### Example Allow an IP address and a domain as `Host` headers: ```yaml {2-4} theme={null} agent: web_allow_hosts: - 8.8.8.8 - example.com ``` ### Working with machine-in-the-middle (MITM) proxies If you're working with machine-in-the-middle (MITM) proxies, you may need to specify a custom CA certificate to trust the ngrok server. This may require you to use the ngrok root CA certificate that the agent uses to connect to the ngrok edge. Use the following command to extract the ngrok agent self-signed root CA certificate: ``strings `which ngrok` | grep 'BEGIN CERTIFICATE' -A 22`` This outputs the root CA certificate in PEM format. ## Endpoint definitions The `endpoints` field enables you to define and configure multiple endpoints. Defining multiple endpoints this way enables you to start pre-configured endpoints by name without having to memorize the right arguments every time through the `ngrok start` command. You can also use this field to start multiple endpoints at the same time from a single agent using the `ngrok start --all` flag. The endpoints field accepts a list of endpoint configurations, the list of available endpoint configuration options can be found here. ### Example The snippet below defines two HTTPS endpoints, one TCP endpoint, and one TLS endpoint, named 'httpbin', 'demo', 'ssh', and 'demo-2', respectively: ```yaml theme={null} endpoints: - name: httpbin url: https://alan-httpbin.ngrok.app upstream: url: 8080 - name: demo url: https://demo.inconshreveable.com upstream: url: 8181 - name: ssh url: tcp://1.tcp.ngrok.io:12345 upstream: url: 22 - name: demo-2 url: tls://myexample.ngrok.app upstream: url: 443 ``` #### Start the endpoint named 'httpbin' ```sh theme={null} ngrok start httpbin ``` #### Start all endpoints ```sh theme={null} ngrok start --all ``` ### Endpoint configuration options | Name | Required | Description | | - | - | - | | [`agent_tls_termination`](#agent-tls-termination) | | Configuration for TLS termination at the ngrok agent. This is applicable only when the endpoint URL scheme is `tls://`. | | [`name`](#name) | Required | A unique name for this endpoint's configuration. | | [`url`](#url) | | The address you would like to use to forward traffic to your upstream service. Leave empty to get a randomly assigned address. | | [`bindings`](#bindings) | | Allows you to configure the bindings for this endpoint. Currently only one binding may be provided. | | [`metadata`](#metadata-1) | | An arbitrary string of user-defined metadata for this endpoint. | | [`description`](#description) | | An arbitrary user-defined description about this endpoint. | | [`traffic_policy`](#traffic-policy) | | Accepts a Traffic Policy configuration in YAML format. | | [`upstream`](#upstream) | Required | Upstream service configuration options. | #### `agent_tls_termination` Object. Configuration for TLS termination at the ngrok agent. This is applicable only when the endpoint URL scheme is `tls://`. ##### `agent_tls_termination.mutual_tls_certificate_authorities` String\[]. Specifies one or more Root CA certificates (as paths or raw PEM data) to validate client certificates for mutual TLS authentication. Available only for `tls://` URLs. ##### `agent_tls_termination.server_certificate` String. The server certificate (provided as a file path or raw PEM data) used by the ngrok agent for TLS termination. Available only for `tls://` URLs. ##### `agent_tls_termination.server_private_key` String. The private key (provided as a file path or raw PEM data) corresponding to the server certificate, used for TLS termination by the ngrok agent. Available only for `tls://` URLs. #### `name` **Required.** String. A unique name for this endpoint's configuration. This is the value you use when running the `ngrok start ` command. ##### Example ```yaml {3} theme={null} # ... endpoints: - name: example # ... ``` #### `url` String\[]. The address you would like to use to forward traffic to your upstream service. ##### Accepted formats: * **Domain** - `example.org` * When using the *domain* format you are only defining the domain. The scheme and port will be inferred. * **Origin** - `https://example.ngrok.app` *or* `https://example.ngrok.app:443` *or* `tcp://1.tcp.ngrok.io:12345` *or* `tls://example.ngrok.app` * When using the *origin* format you are defining the protocol, domain and port. HTTP endpoints accept ports `80` or `443` with respective protocol. * **Scheme (shorthand)** - `https://` *or* `tcp://` *or* `tls://` *or* `http://` * When using *scheme* you are defining the protocol and will receive back a randomly assigned ngrok address. * **Empty** - \`\` * When *empty* your endpoint will default to be `https` and receive back a randomly assigned ngrok address. ##### Example ```yaml {4} theme={null} # ... endpoints: - name: example url: https://example.ngrok.app # ... ``` #### `bindings` String\[]. Allows you to configure the bindings for this endpoint. Currently only one binding may be provided. The allowed values are `internal`, `public`, and `kubernetes` which allow you to create [public](/docs/gateway/endpoints/public-endpoints/), [internal](/docs/gateway/endpoints/internal-endpoints/), and [kubernetes endpoints](/docs/gateway/endpoints/kubernetes-endpoints/). This field is mainly only truly necessary for creating kubernetes endpoints as the default for all endpoints is to have a `public` binding, and an `internal` binding is automatically inferred when the endpoint URL ends in `.internal`. To create a kubernetes endpoint (which projects the endpoint into one or more kubernetes clusters running the ngrok Operator as a `Service` resource), set the `bindings` field to `["kubernetes"]`. ##### Example ```yaml {4} theme={null} # ... endpoints: - name: example url: https://example-service.example-namespace bindings: - kubernetes # ... ``` #### `metadata` String. An arbitrary string of user-defined metadata for this endpoint. This value will appear on the endpoint object in the ngrok API. ##### Example ```yaml {5} theme={null} # ... endpoints: - name: example url: https://example.ngrok.app metadata: '{ "id": "example-app" }' # ... ``` #### `description` String. An arbitrary user-defined description about this endpoint. This value will appear on the endpoint object in the ngrok API. ##### Example ```yaml {6} theme={null} # ... endpoints: - name: example url: https://example.ngrok.app metadata: '{ "id": "example-app" }' description: "our example application" # ... ``` #### `traffic_policy` Object. Accepts a Traffic Policy configuration in YAML format. Traffic Policy enables you to manage, route, and secure traffic through configuration. You can learn more about the individual parts of the Traffic Policy and the available actions here: * [HTTP Traffic Policy Docs](/docs/gateway/traffic-policy/) * [TLS Traffic Policy Docs](/docs/traffic-policy) * [TCP Traffic Policy Docs](/docs/traffic-policy) ##### Example ```yaml {7-15} theme={null} # ... endpoints: - name: example url: https://example.ngrok.app metadata: '{ "id": "example-app" }' description: "our example application" traffic_policy: on_http_request: - actions: - type: custom-response config: status_code: 200 body: hello, Traffic Policy! headers: content-type: text/plain # ... ``` #### `upstream` Object. Upstream service configuration options. ##### `upstream.url` String. The local or remote address you would like to incoming traffic to be forwarded to. ###### Accepted formats: * **Origin** - `https://example.org` *or* `http://example.org:80` *or* `tcp://127.0.0.1:80` * When using the *origin* format you are defining the protocol, domain and port. * When no port is present and scheme is `https` or `http` the port will be inferred. * For `https` port will be`443`. * For `http` port will be `80`. * **Domain** - `example.org` * This is only allowed for `https` and `http` endpoints. * For `tcp` and `tls` endpoints host and port is required. * When using the *domain* format you are only defining the host. * Scheme will default to `http`. * Port will default to `80`. * **Scheme (shorthand)** - `https://` * This only works for `https` and `http`. * For `tcp` and `tls` host and port is required. * When using *scheme* you are defining the protocol and the port will be inferred on the local host. * For `https` port will be`443`. * For `http` port will be `80`. * Host will be `localhost`. * **Port (shorthand)** - `8080` * When using *port* you are defining the port on the local host that will receive traffic. * Scheme will default to `http`. * Host will default to `localhost`. ###### Example ```yaml {16-17} theme={null} # ... endpoints: - name: example url: https://example.ngrok.app metadata: '{ "id": "example-app" }' description: "our example application" traffic_policy: on_http_request: - actions: - type: custom-response config: status_code: 200 body: hello, Traffic Policy! headers: content-type: text/plain upstream: url: 8080 # ... ``` ##### `upstream.protocol` String. The application layer protocol the agent should use when talking to your upstream application for HTTP endpoints. ###### Accepted values: * `http1` (default) * `http2` ###### Example ```yaml {18-18} theme={null} # ... endpoints: - name: example url: https://example.ngrok.app metadata: '{ "id": "example-app" }' description: "our example application" traffic_policy: on_http_request: - actions: - type: custom-response config: status_code: 200 body: hello, Traffic Policy! headers: content-type: text/plain upstream: url: 8080 protocol: http1 # ... ``` ##### `upstream.proxy_protocol` String. The version of [PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) to use with this endpoint, empty if not using. Possible values are `1` or `2`. ###### Example ```yaml {19-19} theme={null} # ... endpoints: - name: example url: https://example.ngrok.app metadata: '{ "id": "example-app" }' description: "our example application" traffic_policy: on_http_request: - actions: - type: custom-response config: status_code: 200 body: hello, Traffic Policy! headers: content-type: text/plain upstream: url: 8080 protocol: http1 proxy_protocol: 2 # ... ``` ## Tunnels (deprecated) The `tunnels` configuration field is deprecated and will be removed by the end of 2025. Please use the `endpoints` configuration field instead. v3 still supports the `tunnels` configuration field. This is a key-value list of tunnel name to configurations. An example can be found below: ```yaml theme={null} # Version of the ngrok Agent Configuration file. Required. version: 3 # Agent Configuration agent: authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p # Tunnel Configurations tunnels: basic: proto: http addr: 80 ``` ### Tunnel configuration options You can find the configuration options for `tunnels` under the [Agent Configuration v2 docs](/docs/gateway/agent/config/v2/#tunnel-configurations). ## Example configuration files Below are a collection of different agent configurations to serve as examples for your `ngrok.yml` file. ### Basic Here is a basic example configuration file. In this example, authentication is done using an authtoken and a single HTTPS endpoint named `basic` is defined with an endpoint URL `basic.ngrok.app` and an upstream URL of `localhost:8080`. ```yaml theme={null} # Version of the ngrok Agent Configuration file. Required. version: 3 # Agent Configuration agent: authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p # Endpoint Definitions endpoints: - name: basic url: basic.ngrok.app upstream: url: 8080 ``` #### Starting the `basic` endpoint Run the following command to start the basic endpoint. Make sure you update the authtoken in the example to your own before running the following command: ``` ngrok start basic ``` ### Multiple endpoints ```yaml theme={null} # Version of the ngrok Agent Configuration file. Required. version: 3 # Agent Configuration. Required. agent: authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p # Endpoint Definitions endpoints: - name: foo description: foo123 metadata: foo123 url: foo.ngrok.app upstream: url: 8080 protocol: http1 - name: bar url: bar.ngrok.app upstream: url: 8080 ``` ### Endpoint with inline Traffic Policy ```yaml theme={null} # Version of the ngrok Agent Configuration file. Required. version: 3 # Agent Configuration. Required. agent: authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p # Endpoint Definitions endpoints: - name: foo description: foo123 metadata: foo123 url: foo.ngrok.app # Traffic Policy that will be applied to this endpoint traffic_policy: on_http_request: - actions: - type: custom-response config: status_code: 200 body: hello, Traffic Policy! headers: content-type: text/plain upstream: url: 8080 protocol: http1 ``` ### Endpoint with Traffic Policy file ```yaml theme={null} # Version of the ngrok Agent Configuration file. Required. version: 3 # Agent Configuration. Required. agent: authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p # Endpoint Definitions endpoints: - name: bar url: bar.ngrok.app # Path to the Traffic Policy to be read and applied to this endpoint traffic_policy_file: /path/to/your/traffic-policy.yml upstream: url: 8080 protocol: http2 ``` ### Endpoint with a random address Here is an example configuration where ngrok will randomly assign your endpoint an ngrok branded address: ```yaml theme={null} # Version of the ngrok Agent Configuration file. Required. version: 3 # Agent Configuration. Required. agent: authtoken: 4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p # Endpoint Definitions endpoints: # Endpoint with no endpoint URL defined to get a randomly assigned ngrok address. - name: my_endpoint_name #url: http:// # uncomment this line if you want your endpoint to be HTTP, by default it's HTTPS upstream: url: 80 ``` # Agent Connect URLs Source: https://ngrok.com/docs/gateway/agent/connect-url Point the ngrok agent at your own branded hostname instead of the default connect URL, and choose which certificate authority issues its certificate. The Agent Connect URL is the hostname and port the ngrok agent (and agent SDKs) uses to establish its control connection to the ngrok service. By default, agents use `connect.ngrok-agent.com:443`. You can change this per agent by setting the [`connect_url`](/docs/gateway/agent/config/v3/#connect-url) field in your configuration file. ## Custom agent connect URL Create a custom agent connect URL to customize how your agents connect (for example `connect.example.com:443`). This involves delegating a DNS subdomain and configuring certificates for the new address. Key points: * Agents connecting via your custom agent connect URL must authenticate to your ngrok account; connections using credentials from other accounts are rejected. * Custom agent connect URLs support both ngrok‑issued and Let's Encrypt certificates. ### Create a custom agent connect URL 1. Choose a domain you own * You will delegate a subdomain via `NS` records to ngrok so it can manage the required DNS (A/AAAA) and certificates. 2. Create the agent connect URL entry * In the Dashboard: [https://dashboard.ngrok.com/connect-urls](https://dashboard.ngrok.com/connect-urls) * Or via API: [/api-reference/agentingresses/list/](/docs/api-reference/agentingresses/list/) 3. Pick a certificate issuer * ngrok Internal CA (fast, private CA, may require trust in some networks) * Let's Encrypt (public CA, may require additional provisioning time) ### Delegate DNS records After creation, the Dashboard prompts you to add `NS` records for the selected subdomain at your DNS provider. ### Update ngrok agent configuration Point your agents at the new address by setting [`connect_url`](/docs/gateway/agent/config/v3/#connect-url). The helper command [`ngrok config add-connect-url`](/docs/gateway/agent/cli/#ngrok-config-add-connect-url) can update your config for you. For example, if the domain of your custom agent connect URL is `connect.example.com` then you might add the following line to the agent's configuration file: ```bash theme={null} ngrok config add-connect-url connect.example.com:443 ``` ```bash theme={null} ssh -R 443:localhost:80 v2@connect.example.com http ``` ```go theme={null} import ( "context" "net" "os" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { agent, _ := ngrok.NewAgent( ngrok.WithAuthtoken(os.Getenv("NGROK_AUTHTOKEN")), ngrok.WithAgentConnectURL("connect.example.com:443"), ) return agent.Listen(ctx) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const session = await new ngrok.SessionBuilder() .authtokenFromEnv() .serverAddr("connect.example.com:443") .connect(); const listener = await session .httpEndpoint() .listenAndForward("http://localhost:8080"); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/classes/SessionBuilder.html#serveraddr](https://ngrok.github.io/ngrok-javascript/classes/SessionBuilder.html#serveraddr) ```python theme={null} import ngrok, asyncio async def create_listener() -> ngrok.Listener: session: ngrok.Session = ( await ngrok.SessionBuilder() .authtoken_from_env() .server_addr("connect.example.com:443") .connect() ) return (await session.http_endpoint() .listen_and_forward("http://localhost:8080")) listener = asyncio.run(create_listener()) print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/session\_builder.html#ngrok.SessionBuilder.server\_addr](https://ngrok.github.io/ngrok-python/session_builder.html#ngrok.SessionBuilder.server_addr) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .server_addr("connect.example.com:443") .connect() .await?; let tun = sess .http_endpoint() .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/session/struct.SessionBuilder.html#method.server\_addr](https://docs.rs/ngrok/latest/ngrok/session/struct.SessionBuilder.html#method.server_addr) In your Helm values.yaml file, set ``` serverAddr: "connect.example.com:443" ``` ## Regional scope and GSLB Custom agent connect URLs are pinned to a specific region for the agent's control connection and do not use [Global Server Load Balancing](/docs/gateway/domains/global-load-balancer/) for that connection. End-user traffic to your endpoints is still delivered using GSLB. ## Dedicated IPs When you create a custom agent connect URL, the DNS and certificates are branded to your domain, but the address still resolves to ngrok's shared IPs. If you need static, account-unique addresses, dedicated IPs are available. [Contact Sales](mailto:sales@ngrok.com?subject=Dedicated+Ingress+IPs) for details. ## Certificate issuer When you create a custom agent connect URL, choose one of two certificate issuers: * ngrok's internal CA * [Let's Encrypt](https://letsencrypt.org/) ngrok dashboard new agent connect URL dialog with Domain, Description, and Certificate Issuer fields ### ngrok internal CA * Default option; provisioned immediately. * Trusted by the ngrok agent out of the box. * Some enterprise inspection tools may not trust this private CA without additional configuration. ### Let's Encrypt * Public CA; useful when corporate proxies or network devices can't trust a private CA. * May require setting `connect_cas: host` (or providing the Let's Encrypt root) for some SDKs and for the SSH Reverse Tunnel agent. * Provisioning can take time (DNS propagation). ngrok issues a temporary certificate until Let's Encrypt is ready. Even if DNS checks pass in the Dashboard, Let's Encrypt may still be propagating. If you select Let's Encrypt, the agent connect URL drawer shows a status card while the certificate is provisioning: ngrok dashboard certificate provisioning delayed warning banner with a link to contact support Certificates issued by Let's Encrypt through ngrok are securely managed. Private keys aren't exposed to requesters. ## Why customize the agent connect URL? ### Branded connectivity If you use ngrok for production connectivity (devices in the field or customer environments), a custom agent connect URL lets you brand connectivity with your domain. ### Policy enforcement For development and testing, a custom agent connect URL helps enforce policy: * Ensure developers use a shared ngrok account (not personal accounts). * Block default agent connect URLs on corp networks while allowing your custom URL. ## Agent connect URL pricing Custom agent connect URLs are available on the Pay-as-you-go plan, billed by active endpoint hour. See [pricing](https://ngrok.com/pricing) for complete pricing details. # ngrok Agent CLI and SDK Connectivity Source: https://ngrok.com/docs/gateway/agent/connectivity How the ngrok Agent CLI and Agent SDKs reach the ngrok service, covering addresses, DNS resolution, TLS verification, heartbeats, and how to diagnose a session that won't start. When the ngrok Agent CLI and Agent SDKs start, they establish long-lived TLS connections to the ngrok service through which they create new endpoints and receive connections from ngrok's cloud service that are intended for your upstream services. ## Address By default, the latest ngrok Agent CLI dials the following Connect URL when it connects to the ngrok service. This address resolves to a [dynamic set of IP Addresses](/docs/gateway/domains/ip-addresses/). ``` connect.ngrok-agent.com:443 ``` All connections to ngrok servers are made on port 443. You can [customize the Connect URL](/docs/gateway/agent/connect-url/) to brand it with your own domain. By default, the agent connects to one region. It can also connect to several at once and load balance traffic across them. [Contact Sales](https://ngrok.com/enterprise/contact) to turn this on. The complete list of addresses and IP addresses available for the agent to connect to ngrok can be found in the [ngrok DNS tunnel.json file](https://s3.amazonaws.com/dns.ngrok.com/tunnel.json). Prior to ngrok agent version 3.3.0, the ngrok agent connected to `tunnel.*.ngrok.com` domains. The latest agent uses `connect.*.ngrok-agent.com` domains. ## Additional URLs In addition to the addresses used for connecting to the ngrok server, the ngrok agent may reach out to the following URLs. ### Certificate revocation list (CRL) check The ngrok agent reaches out to the following domains to check for revoked certificates. This check can be disabled by adding `crl_noverify: true` to your ngrok agent config. * Prior to ngrok agent version 3.10.0: `http://crl.ngrok.com/ngrok.crl` * Latest versions: `http://crl.ngrok-agent.com/ngrok.crl` These connections use HTTP and port 80. You can download and decode the CRL using OpenSSL: `openssl crl -inform PEM -text -noout -in ngrok.crl` ### ngrok Agent update check The ngrok agent will automatically look for updates when it starts up. This check can be disabled by adding `update_check: false` to your ngrok agent config. * `https://update.equinox.io` Equinox is fully owned by ngrok and used exclusively for building and distributing ngrok binaries. See [the FAQ page](/docs/faq#does-ngrok-own-bin-equinox-io) for more details. ## DNS resolution When the ngrok agent dials the ngrok service to establish its TLS connections, it resolves DNS for the [connection address](/docs/gateway/agent/connect-url/) which is defined by the [`connect_url`](/docs/gateway/agent/config/v3/#connect-url) configuration property. ngrok attempts to resolve DNS using multiple mechanisms so that it can establish connectivity even in network environments where DNS service is failing. ngrok attempts to resolve the IPs of its service using the following mechanisms: * Via the system's default DNS resolvers * Via Google's DNS servers (`8.8.8.8` and `8.8.4.4`) * Via Google's DNS-over-HTTPS service ([https://developers.google.com/speed/public-dns/docs/doh](https://developers.google.com/speed/public-dns/docs/doh)) * Via a [file hosted on an ngrok-controlled S3 bucket](https://s3.amazonaws.com/dns.ngrok.com/tunnel.json) Instead of using the system's default DNS resolvers, you can configure the DNS servers the ngrok agent uses for resolution with the [`dns_resolver_ips`](/docs/gateway/agent/config/v3/#dns-resolver-ips) configuration option. ## TLS verification The ngrok agent connects to the ngrok service over TLS connections. The agent verifies the TLS Certificate returned by the ngrok service. The certificates returned by the ngrok service are signed by ngrok's own root certificate authority. The ngrok agent verifies the returned certificate against certificate authorities bundled into the agent itself. Lastly, the ngrok agent makes a request to `crl.ngrok-agent.com` to verify that the certificate returned by the ngrok service has not been revoked. It is possible to skip this step by setting `crl_noverify: true` in your configuration file. ## Heartbeats Once the ngrok agent has established connectivity to the ngrok service, it periodically sends application-level heartbeat messages to validate the liveness of the connection. You may customize this behavior via the [`heartbeat_interval`](/docs/gateway/agent/config/v3/#heartbeat-interval) and [`heartbeat_tolerance`](/docs/gateway/agent/config/v3/#heartbeat-tolerance) configuration parameters. If the ngrok agent doesn't receive a response to its heartbeat within the tolerance window, it terminates the connection and begins reconnecting. ngrok's heartbeat mechanism allows it to recover from any type of network outage, even those caused by packet loss, dynamic IP changes, interface changes (for example, Wi-Fi to LTE) or complete network outages. The ngrok service also sends its own heartbeats to the agent which it uses to detect liveness and terminate dead connections. ## Reconnection If the ngrok agent is disconnected for any reason, it will automatically begin reconnecting. Reconnecting begins the entire connection process over again, beginning with DNS resolution. The ngrok agent attempts to recover quickly and slowly backs off its reconnection attempts but always attempts to re-establish connectivity unless the ngrok service explicitly instructs it to stop reconnecting. ## Troubleshooting If the agent can't reach ngrok it's often hard to tell why, because any of the steps above could be the one that failed. The `ngrok diagnose` command works through them in order and reports which one broke. ```bash theme={null} ngrok diagnose ``` The `ngrok diagnose` command runs a series of tests to diagnose potential connectivity issues between the ngrok agent and the remote ngrok service. See available flags [here](/docs/gateway/agent/cli/#ngrok-diagnose). ### Diagnostics The diagnose command gathers your environment information, including your sanitized configuration file. It then tests: * proxy connectivity, if `proxy_url` is configured * name resolution * general internet connectivity By default, `ngrok diagnose` tests IPv4 connectivity between the ngrok agent and the closest ngrok point of presence. To test IPv6 connectivity, add `--ipv6 true` to the command. To test connectivity between the ngrok agent and all ngrok points of presence, add `--region all`. #### Basic output The diagnose command prints a connectivity summary in your terminal. If you have full connectivity, your report will look like this: ```bash theme={null} Testing ngrok connectivity... Internet Connectivity Name Resolution [ OK ] TCP [ OK ] TLS [ OK ] Localhost Connectivity Name Resolution [ OK ] ngrok Connectivity - Region: Auto (lowest latency) Name Resolution [ OK ] TCP [ OK ] TLS [ OK ] Tunnel Protocol [ OK ] Successfully established ngrok connection! (region: 'auto', latency: 54.895145ms) ``` If there's a connectivity problem, your report will show warnings and errors that need to resolved before ngrok can work correctly: ``` Testing ngrok connectivity... Internet Connectivity Name Resolution [ WARN ] TCP [ OK ] TLS [ OK ] Errors and warnings encountered during diagnostics: * Diagnostics * Name Resolution * Resolver: system * Hostname: google.com - Err: Failed to resolve host google.com: lookup google.com on [::1]:53: read udp [::1]:48848->[::1]:53: read: connection refused (NGROK_ERR_8000) Error establishing ngrok connection: Failed to establish internet connectivity: resolver returned no IPs. ``` #### Verbose output For more detailed output, you can use the `--write-report`/`-w` flag to generate a verbose report: ```bash theme={null} ngrok diagnose -w out.txt ``` By default, `ngrok diagnose --region all` will generate a report. The `diagnose` output will print the report location: ``` Report written to /var/folders/yg/yzt44t813/T/ngrok-diagnose1080549118/diagnose.json ``` #### Kubernetes You can run `ngrok diagnose` within your Kubernetes cluster to troubleshoot network connectivity issues. See [Testing in a Kubernetes Cluster](/docs/gateway/running-behind-firewalls/#testing-in-a-kubernetes-cluster). # Agent Version Deprecation Notices Source: https://ngrok.com/docs/gateway/agent/deprecation-notices What to do if you received an email saying your ngrok agent version is deprecated, including how to check your version and the options for staying supported. ngrok periodically retires older agent versions to maintain the security, performance, and reliability of the service. When a version approaches end-of-life, free-tier users running that version receive an email notice with a deadline to act. ## Why did I receive this email? You received this notice because an ngrok agent running a version at or below the deprecated threshold connected to your account within the last 90 days. This is a heads-up: your service has not been interrupted yet. You have until the deadline in the email to take action. ## What happens if I don't act? After the deprecation date, agents running the affected version will **fail to connect** to the ngrok cloud service. Any endpoints they serve will become unavailable and return an error. No further warning will be issued. ## What are my options? ### Option 1: Update your agent (free) Updating to a supported version is free and takes only a few minutes. Update in place using the CLI: ```bash theme={null} ngrok update ``` Or download the latest version manually from the [ngrok downloads page](https://ngrok.com/download). Once updated, verify your version: ```bash theme={null} ngrok version ``` See the [Version Support Policy](/docs/gateway/agent/version-support-policy) for release and end-of-life dates. ### Option 2: Upgrade to a paid plan Paid accounts are not subject to agent version deprecations. If staying on an older agent version is a requirement for your environment, upgrading is a valid alternative. This exemption applies as long as your account remains on a paid plan. Downgrading back to a free plan will re-apply deprecation enforcement. [Choose a plan →](https://dashboard.ngrok.com/billing/choose-a-plan) ## How do I check which version I'm running? Run the following command on each machine where the ngrok agent is installed: ```bash theme={null} ngrok version ``` Example output: ``` ngrok version 3.19.1 ``` Cross-reference your version against the [Release Support Chart](/docs/gateway/agent/version-support-policy#release-support-chart) to confirm whether it is still within its active support window. *** ## Frequently asked questions Yes. Once you update to a supported version, your account will no longer trigger notices for that deprecation campaign. If you recently updated, allow a short period for the system to reflect the change before expecting emails to stop. No. Deprecation enforcement applies to free-tier accounts only. Paying customers can continue using older agent versions without interruption. Only agents running a deprecated version need to be updated. You can update agents remotely from the [ngrok dashboard](https://dashboard.ngrok.com/agents), or update each affected machine individually by running `ngrok update` or replacing the binary manually. Only if you plan to upgrade to a paid plan, or if you no longer actively use the agent on that account. Otherwise, your tunnels will stop working after the deadline. Deprecations follow the [ngrok Agent Version Support Policy](/docs/gateway/agent/version-support-policy). Retiring older versions allows ngrok to focus on new features and improvements to the security, performance, and reliability of the service without maintaining compatibility with legacy agent behavior indefinitely. *** ## Still need help? Reach out to [support@ngrok.com](mailto:support@ngrok.com) with any questions. # Expose a Service with the ngrok Agent Source: https://ngrok.com/docs/gateway/agent/expose-a-service Commands for putting an HTTP, TCP, or TLS service online with the ngrok agent, from a random URL to a reserved domain with authentication in front of it. Each example below starts an endpoint and forwards its traffic to a service you're already running. They are all the same shape: `ngrok `, with flags for anything you want to change about the endpoint. Before you begin, [install the Agent CLI](/docs/gateway/agent/install) and [add your authtoken](/docs/gateway/agent/authtokens). ## HTTP endpoint The common case: a web app on a local port, reachable at a URL ngrok picks for you. Serve your web app listening at port 8080 on a random public URL ```bash theme={null} ngrok http 8080 ``` ## Pre-defined domain Use a [reserved domain](/docs/gateway/domains/) so the URL survives restarts instead of changing each time. Serve a web app on example.ngrok.app ```bash theme={null} ngrok http 8080 --url https://example.ngrok.app ``` ## Basic Auth Put a username and password in front of the service without changing the service itself. Secure your web app with a username + password ```bash theme={null} ngrok http 80 --traffic-policy-file traffic-policy.yml ``` ##### `traffic-policy.yml` ```yaml theme={null} on_http_request: - actions: - type: basic-auth config: credentials: - username1:password1 - username2:password2 ``` ## Forward to non-local Point the agent at another host on the network, not just `localhost`. Forward to a service not listening on localhost ```bash theme={null} ngrok http 192.168.1.2:80 ``` ## Local HTTPS server Forward to an upstream that already speaks HTTPS, rather than plain HTTP. Forward to an upstream service listening for `https` ```bash theme={null} ngrok http https://localhost:8443 ``` ## Forwarding to an IPv6 address Forward to an upstream reachable only over IPv6. The ngrok agent can also forward to IPv6 addresses ```bash theme={null} ngrok http '[::1]:80' --url https://ipv6.example.com ``` ```bash theme={null} ngrok http 'https://[2001:db8::123.123.123.123]:8443' --url https://ipv6.example.com ``` ## TCP endpoint Expose anything that isn't HTTP, such as a database, SSH, or RDP. Accept traffic to a non-HTTP service. ```bash theme={null} ngrok tcp 22 ``` ## TLS endpoint Accept TLS traffic and pass it through, for protocols wrapped in TLS that aren't HTTPS. Listen on `your-name.ngrok.app` for TLS traffic. It could be HTTPS, but any protocol wrapped in TLS is accepted. ```bash theme={null} ngrok tls 80 \ --url tls://your-name.ngrok.app \ --traffic-policy-file traffic-policy.yml ``` ##### `traffic-policy.yml` ```yaml theme={null} on_tcp_connect: - actions: - type: terminate-tls ``` ## Multiple endpoints Run several services from one agent by naming them in a configuration file. Start multiple endpoints defined in the [configuration file](/docs/gateway/agent/config/). ```bash theme={null} ngrok start foo bar baz ``` # How ngrok Agents Work Source: https://ngrok.com/docs/gateway/agent/how-it-works What a session is, how agents create endpoints, and what happens when an agent stops or loses its connection. Every kind of agent does the same three things: it opens a session with ngrok, it creates endpoints on that session, and it carries traffic back to your upstream service. Those three pieces explain most of the agent's behavior, including the parts that surprise people. ## The session When an agent starts, it dials out to ngrok and establishes a long-lived TLS connection on port 443. That connection is the **session**, and it's the unit ngrok counts: the Agents page in your dashboard lists sessions, whether they were opened by the standalone agent, an application using an Agent SDK, a Kubernetes Operator, or a reverse SSH client. The session is outbound only. Nothing listens for inbound connections on your machine, which is why agents work behind NAT, corporate firewalls, and networks you don't control. It's also why there's nothing to port forward and no inbound firewall rule to add. The session is also the handle you manage an agent by. Because ngrok holds one end of it, you can stop, restart, or upgrade an agent from the dashboard or the [Tunnel Sessions API](/docs/api-reference/tunnelsessions/list/) even when you can't reach the machine itself. The agent keeps the session alive with periodic heartbeats and reconnects on its own if the network drops. [Connectivity](/docs/gateway/agent/connectivity) covers that machinery, and how to diagnose it when the session won't establish. ## Endpoints on that session Once the session is up, the agent asks ngrok to create **endpoints** on it. An endpoint is the URL traffic arrives at, and the agent tells ngrok which upstream service to forward it to. Endpoints an agent creates are [Agent Endpoints](/docs/gateway/endpoints/agent-endpoints), and they differ from [Cloud Endpoints](/docs/gateway/endpoints/cloud-endpoints/) in ways that follow directly from living on a session: * **They last as long as the session does.** Stop the agent and the endpoint stops answering. A Cloud Endpoint is configured centrally and stays up whether or not any process of yours is running. * **The agent is authoritative.** The agent that created an endpoint owns its configuration. The dashboard and API give you a read-only view. * **Forwarding is implicit.** Traffic goes to the agent that created the endpoint, because that's the connection it arrived on. * **Traffic Policy is optional.** Anything a policy doesn't terminate goes to the agent. One agent can hold many endpoints on a single session, which is why [a configuration file](/docs/gateway/agent/config/) is the usual way to run more than one service. ## When an agent stops Endpoints live on the session, so they go offline when the agent does. That's the single most important consequence of the model, and most agent operations exist to manage it: * Run the agent [as a native OS service](/docs/gateway/agent/service) so it starts on boot and restarts after a crash. * Give an endpoint a [reserved domain](/docs/gateway/domains/) so its URL survives a restart instead of being reassigned. * Put two agents behind [endpoint pooling](/docs/gateway/endpoints/endpoint-pooling/) so one can fail without taking the endpoint down. If you need the URL to stay up independently of any process you run, that's a Cloud Endpoint, not an agent. ## What the agent doesn't do The agent is a connection and forwarding component, not a proxy you configure with rules. Authentication, routing, rewriting, and rate limiting are described by [Traffic Policy](/docs/gateway/traffic-policy/), which ngrok applies in its own network before traffic reaches your agent. This is why the same policy works identically no matter which kind of agent created the endpoint. # ngrok Agents Overview Source: https://ngrok.com/docs/gateway/agent/index Learn what ngrok agents do, compare the four kinds, and choose the one that fits your service. An agent is anything that holds an active session with the ngrok service. That includes the `ngrok` command-line agent you run yourself, applications you build with an Agent SDK, Kubernetes Operators, and reverse SSH agents. They differ in how you run them, not in what they do: every agent dials out to ngrok, creates endpoints, and carries traffic back to your upstream services. ## Why use an ngrok agent The services you need to reach aren't always reachable. They sit behind a corporate firewall, on a device you shipped to a customer, inside a private cluster, or on a laptop that joins a different network twice a day. None of them can accept an inbound connection, and giving them one usually means a VPN, a port forward, or a public IP address you would rather not hand out. An agent reverses the direction instead. It runs next to your service, dials out to ngrok's network over TLS on port 443, and traffic addressed to your endpoint travels back down that same connection. Your service stays put, and nothing about its network has to change. ## What agents do * **Carry any protocol your service speaks.** HTTP, HTTPS, TCP, and TLS, so databases, SSH, and RDP work the same way web apps do. * **Create endpoints you control.** Apply [Traffic Policy](/docs/gateway/traffic-policy/) to authenticate, route, rewrite, and rate limit before traffic reaches your service. * **Use one outbound connection.** Everything travels over a single TLS connection on port 443, so there are no inbound ports to open. * **Give you visibility.** Sessions, endpoints, and traffic appear in the dashboard and the [Traffic Inspector](/docs/obs/traffic-inspection/) no matter which agent created them. ## The four kinds of agent A standalone executable you install and run next to your service. No runtime dependencies, and it can install itself as a native OS service. Libraries that embed ngrok directly in your application, so there's no separate process to bundle, ship, or supervise. Runs inside your cluster and turns Ingresses, Gateway API resources, and ngrok CRDs into endpoints. Uses the `ssh` binary you already have, with no ngrok software installed at all. ## Choosing one Start with the `ngrok` command-line agent. It is the fastest way to get a service online and runs on every major platform. Everything you learn about endpoints, configuration, and Traffic Policy carries over to the other three. Pick a different one if: * **You're shipping an application.** An Agent SDK runs ngrok inside your application, so there's no separate agent process to bundle, distribute, or keep alive. It also gives you programmatic control the CLI doesn't expose. * **Your workloads live in Kubernetes.** The Kubernetes Operator reads the resources your cluster already declares, so endpoints are created and reconciled the same way the rest of your infrastructure is. * **You can't install anything.** A reverse SSH agent needs only an `ssh` client, which is useful on locked-down hosts or for a one-off connection. * **The agent won't run there.** If your target platform is unsupported, or its memory budget is tighter than the agent's, an SDK or reverse SSH will still work. You may not need an agent at all. If what you're routing to is already reachable at a stable address, a [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/) routes to it directly and stays up whether or not any process of yours is running. Endpoints an agent creates are [Agent Endpoints](/docs/gateway/endpoints/agent-endpoints), and they exist only while that agent does. ## Common use cases Bridge localhost development with the public internet for AI coding tools, webhook testing, and real authentication flows. Reach hardware in the field that has no fixed address and no inbound ports. Connect to a customer's network without peering, a VPN, or a public IP address. Give each developer their own public URL to route traffic and webhooks into their local development environment. ## Agent pricing Agents are available to all ngrok users at no additional charge. You only incur costs if the resources they provision incur a cost. For more information, see the [ngrok Pricing page](https://ngrok.com/pricing). ## What's next? * [Install the ngrok agent](/docs/gateway/endpoints/agent-cli-quickstart/) and connect your first service. * [Learn how agents connect](/docs/gateway/agent/connectivity) so you can diagnose them when they don't. # Install the ngrok Agent CLI Source: https://ngrok.com/docs/gateway/agent/install Install the ngrok Agent CLI on your platform, turn on shell tab completion, and keep the binary up to date with the built-in update command. ## Install Follow the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/#1-install-the-ngrok-agent-cli) or visit the [download page](https://download.ngrok.com) to install and set up the ngrok agent. ## System requirements ### Supported platforms The ngrok Agent CLI runs on all major platforms including Linux, MacOS, Windows, and most CPU architectures. See the [ngrok agent download](https://download.ngrok.com) page for details. It's also distributed as a Docker container. | OS | Supported Architectures | | - | - | | Windows | 64-bit, 32-bit (`x86-64`, `x86`) | | MacOS | Intel, Apple Silicon (`x86-64`, `arm64`) | | Linux | `x86-64`, `x86`, `arm`, `arm64`, `mips`, `mips64`, `mips64le`, `mipsle`, `ppc64`, `ppc64le`, `s390x` | | FreeBSD | `x86-64`, `x86`, `arm` | If the Agent CLI does not run on your target platform, you can still use ngrok via the [Agent SDKs](/docs/agent-sdks/) or the [SSH Reverse Tunnel Agent](/docs/gateway/agent/ssh-reverse-tunnel-agent). ### Resource requirements The ngrok Agent CLI is lightweight enough to run most everywhere, even on many embedded platforms. Its resource usage depends on how much concurrent traffic you drive through it. If you have stricter resource requirements, you can still use ngrok via the [Agent SDKs](/docs/agent-sdks) or the [SSH Reverse Tunnel Agent](/docs/gateway/agent/ssh-reverse-tunnel-agent). | Resource | Value | | - | - | | Memory | 64MB minimum, 128MB recommended | | Disk | 25MB | | CPU | No requirement | You can reduce memory usage in the ngrok agent by disabling [inspection in the configuration file](/docs/gateway/agent/config/v3/#inspect-db-size). ## Tab completion The ngrok agent has built-in tab completion which makes it easy to navigate its command and flags by hitting tab in your terminal when using a `bash` or `zsh` shell. Add tab-completion to your shell session with: ```bash theme={null} . <(ngrok completion) ``` Install it permanently by following the documentation for the [`ngrok completion` command](/docs/gateway/agent/cli/#ngrok-completion). ## Updates The ngrok agent can update itself even if you didn't install it with a package manager. Update the ngrok agent with the [`ngrok update`](/docs/gateway/agent/cli/#ngrok-update) command. ```bash theme={null} ngrok update ``` By default, the ngrok agent automatically checks for available updates when it runs. If an update is available, it will prompt you to update by pressing `Ctrl+U`. You can configure whether the agent checks for updates via the [`update_check` config parameter](/docs/gateway/agent/config/v3/#update-check). You can configure which release channel (for example, `stable`, `beta`) the agent uses for updates via the [`update_channel` config parameter](/docs/gateway/agent/config/v3/#update-channel). # Restrict Where Agents Connect From Source: https://ngrok.com/docs/gateway/agent/ip-restrictions Restrict which IP addresses and CIDR ranges are allowed to open an agent session on your ngrok account, on top of authtoken authentication. Agents that authenticate with an authtoken can also be restricted with IP Policies that specify which IPs and CIDRs they may connect to your ngrok account from. The [Reverse SSH Agent](/docs/gateway/agent/ssh-reverse-tunnel-agent/#authentication) authenticates with an SSH public key instead. * [IP Restrictions on your ngrok dashboard](https://dashboard.ngrok.com/ip-restrictions) * [IP Restrictions API Resource](/docs/api-reference/iprestrictions/list/) # Run the ngrok Agent CLI as a Service Source: https://ngrok.com/docs/gateway/agent/service Install the ngrok Agent CLI as a native operating system service so it starts on boot and restarts after a crash, then manage it remotely from the API or dashboard. ## Running ngrok in the background ngrok includes commands to run itself in the background as a native operating system service, that is, as a daemon. When it runs as an operating system service it: * Starts when the machine boots * Automatically restarts after crashes * Can be managed via native OS service tooling * Sends logs to the OS's native logging service * Starts all endpoints defined in the configuration file, the equivalent of running `ngrok start --all` In most cases, installing ngrok as a service requires administrator privileges. | OS | Notes | | - | - | | Linux | The ngrok agent creates a `systemd` unit file. Errors and warnings are logged to `syslog`. | | Windows | The ngrok agent installs itself as a Windows service. It can be managed via Windows Services. Errors and warnings are logged to the Windows event log. | | MacOS | The ngrok agent creates a plist file and configures itself to run via `launchd`. Errors and warnings are logged to `syslog`. | #### Try it out Create an ngrok [configuration file](/docs/gateway/agent/config/). For this example, assume it's `C:\ngrok\ngrok.yml`. In your configuration file, make sure you include the [`authtoken`](/docs/gateway/agent/config/v3/#authtoken) property and define all of the endpoints you want to start. Then run: ```bash theme={null} ngrok service install --config C:\ngrok\ngrok.yml ``` This will validate that the configuration file is valid, and if so, install ngrok as a service using the given configuration file. The service installation includes the location of the ngrok executable and the configuration file, so don't move or delete either after you've installed the service. You can then start the ngrok agent service with: ```bash theme={null} ngrok service start ``` You can manage the agent with your OS's normal service tooling, but the agent also includes the [`ngrok service` commands](/docs/gateway/agent/cli/#ngrok-service) to manage the installed service: * `ngrok service start` * `ngrok service stop` * `ngrok service restart` * `ngrok service uninstall` If you see an exit code `5` or `Input/output error`, ngrok usually can't find the config file, or doesn't have permission to read it. Make sure you have installed the ngrok service with a valid config file and that when the service is run (either as you or an administrator) it has access to that same config file. ## Remote management The ngrok agent can be stopped, restarted, or upgraded remotely using the [Tunnel Sessions](/docs/api-reference/tunnelsessions/restart) API. You can also perform these actions from the ngrok Dashboard. On Windows, the Tunnel Sessions `restart` operation isn't supported. If the agent is installed as a Windows service, use the Tunnel Sessions [`stop`](/docs/api-reference/tunnelsessions/stop) operation and rely on your Windows service recovery policy to start the service again after it stops. Use this with caution if you have made changes to the config file. If ngrok can't restart for any reason, you may lose access to the agent completely. # SSH Reverse Tunnel Source: https://ngrok.com/docs/gateway/agent/ssh-reverse-tunnel-agent A reverse SSH tunnel (ssh -R) exposes a local service through ngrok without installing the ngrok agent. Learn how it works, the commands, and its limits versus the agent. A **reverse SSH tunnel** uses SSH's `-R` flag to expose a service running on your local machine through a remote server, so it can be reached from outside your network without opening inbound ports. ngrok supports this pattern directly: SSH reverse tunneling (`ssh -R`) is an alternative mechanism to deliver services via ngrok without running an [ngrok agent](/docs/gateway/agent/) or [Agent SDK](/docs/agent-sdks/). The SSH reverse tunnel agent should not be confused with creating remote access to an SSH server via ngrok. If you want to use ngrok to create access to your own SSH server for remote access, please refer to the [using ngrok with ssh](/docs/gateway/ssh-rdp/) documentation. You should only ngrok via SSH if you really can't use an Agent or Agent SDK. The SSH reverse tunnel agent has [many functional limitations compared to the ngrok agent](#differences). ## Example usage ### Random HTTP endpoint ```bash theme={null} ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http ``` ### Custom domain ```bash theme={null} ssh -R example.ngrok.app:443:localhost:8080 v2@connect.ngrok-agent.com http ``` ### Basic Auth ```bash theme={null} ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http \ --basic-auth "username1:password1" \ --basic-auth "username2:password2" ``` ### OAuth When creating a reverse tunnel, you must provide a publicly accessible URL to your traffic policy file, as shown in this example. First, create the `oauth-policy.yml` file: ```yaml theme={null} on_http_request: - actions: - type: oauth config: provider: google ``` Store this file at a publicly accessible address online. For example, you could add it to a github repository. Finally, add your traffic policy to your reverse tunnel using the `--traffic-policy-url` flag. ```bash theme={null} ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http --traffic-policy-url=https://some-website/oauth-policy.yml ``` ### Forward to non-local service ```bash theme={null} ssh -R 0:192.168.1.2:80 v2@connect.ngrok-agent.com http ``` ### Random TCP endpoint ```bash theme={null} ssh -R 0:localhost:22 v2@connect.ngrok-agent.com tcp ``` ### Fixed TCP endpoint ```bash theme={null} ssh -R 1.tcp.eu.ngrok.io:12345:localhost:3389 connect.eu.ngrok-agent.com tcp ``` ### TLS endpoint ```bash theme={null} ssh -R app.example.com:443:localhost:443 v2@connect.ngrok-agent.com tls ``` ### Explicit region selection Normally you will connect to ngrok's closest point of presence via the [Global Load Balancer](/docs/gateway/domains/global-load-balancer/), but you can also explicitly choose a region. ```bash theme={null} ssh -R 443:localhost:80 v2@connect.eu.ngrok-agent.com http ``` ## Authentication Instead of an ngrok authtoken, when you use ngrok via the SSH reverse tunnel agent, it uses a public key for authentication. You'll first need to upload yours to the [SSH Public Keys page on your ngrok dashboard](https://dashboard.ngrok.com/ssh-keys). Copy your default SSH public key with: ```bash theme={null} cat ~/.ssh/id_rsa.pub | pbcopy ``` or: ```bash theme={null} cat ~/.ssh/id_ed25519.pub | pbcopy ``` ```bash theme={null} cat ~/.ssh/id_rsa.pub ``` or: ```bash theme={null} cat ~/.ssh/id_ed25519.pub ``` ## ngrok's SSH public key fingerprints Public key fingerprints can be used to validate a connection to the ngrok point of presence you're connecting through. These are the RSA public key fingerprints: * connect.ap.ngrok-agent.com: `SHA256:K/3UwSeIg0JVf9uLVfl4QLEY11tyON/d+QmLfIU0fmk` * connect.au.ngrok-agent.com: `SHA256:RpCOpodROXqXy4d0SIm7rAqwEUsmmUHA6NAQ6T4EHXY` * connect.eu.ngrok-agent.com: `SHA256:OeywYk1/2w9cOg8Q3FjbsMOe2Hc9CvxbyBhDdUBBOlQ` * connect.in.ngrok-agent.com: `SHA256:acotuxa/+tJY2vmK+VeLQIoVOJLQz/VLTmHTJ/0LPaI` * connect.jp.ngrok-agent.com: `SHA256:/6j2cYqVbjO9YvEKKXTOqHlND72fCms0sdVWClHJAks` * connect.sa.ngrok-agent.com: `SHA256:Wh3W1ub0J/eda2QcEPbrVgS6mdGxIUrbao9G5zMBvdc` * `connect.us-cal-1.ngrok-agent.com`: `SHA256:UwLN719B+xJVKMtcsZL3cqiuY7iYpoxLNg1k5Pqdf2g` * `connect.us.ngrok-agent.com`: `SHA256:WuVeeGNOGVrcMe/GcdsTUB135MFCe1/aaVYXrpCxSEM` ## Command syntax ngrok does its best to honor the syntax of `ssh -R`. You may wish to consult `man ssh`, and the section devoted to the `-R` option for additional details. ngrok uses additional command line options to implement features that are not otherwise available via the `-R` syntax. Consider the following command: ```bash theme={null} ssh -R \ app.example.com:443:127.0.0.1:8080 \ v2@connect.ngrok-agent.com \ http --basic-auth 'user:password' ``` An `ssh -R` command has the following components: ```bash theme={null} ssh -R \ ":::" \ @connect.ngrok-agent.com \ [flags] ``` In this example: * Remote Name: `app.example.com`. ngrok will listen on the domain 'app.example.com'. You may omit this value. If you do, ngrok chooses a random endpoint name. * Remote Port: `443`. ngrok will listen for HTTPS traffic on port 443. The only valid values for HTTP endpoints are 80 and 443. For TLS endpoints it must be 443. You may `0` and ngrok will simply choose the appropriate port for you. * Local Name: `127.0.0.1`. This is the local hostname or IP address that traffic will be sent to. It's most commonly `localhost`. * Local Port: `8080`. This is the local port that traffic will be sent to. * User: `v2`. ngrok uses the user portion of the command to version the command options. You may omit this value. If you do, ngrok will use the latest version. * Command: `http`. This the type of endpoint to create. ngrok accepts either `http`, `tls` or `tcp`. This value is required. * Flags: `--basic-auth 'user:password'`. Run the same command with the `--help` flag to get the list of supported flags or consult the [Agent CLI reference](/docs/gateway/agent/cli/). ## Versioning ngrok uses the user portion of the SSH command to version the CLI syntax. The latest version is `v2`. ## Differences from the Agent When you use ngrok via SSH reverse tunnel, you will need to [upload an SSH public key](#authentication) to authenticate with instead of using an ngrok authtoken like the agent. Additionally, you'll find that using ngrok via SSH has many functional limitations compared to the experience with the agent. An incomplete list of differences from the ngrok agent includes: * Your endpoints won't automatically reconnect if there is a network interruption * There is no equivalent to the agent's [traffic inspection interface](/docs/gateway/agent/web-inspection-interface/) * You can't create endpoints for multiple services with the same command * You can't [forward to upstream https services](/docs/gateway/endpoints/http/#https-forwarding) * You can't create multiple endpoints over the same connection * You can't [serve file system directories](/docs/gateway/endpoints/http/#serving-file-directories) with the `file://` protocol * You can't terminate TLS at the agent when using [end-to-end encryption](/docs/gateway/domains/tls-termination/#end-to-end-encryption) ## SSH reverse tunnel agent pricing The SSH reverse tunnel agent is available to all ngrok users at no additional charge. You only incur costs if resources you provision via its usage incur a cost. For more information, see the [ngrok Pricing page](https://ngrok.com/pricing). # Upgrade to ngrok Agent v3 Source: https://ngrok.com/docs/gateway/agent/upgrade-v2-v3 Upgrade the ngrok agent from v2 to v3, including the breaking changes, the new configuration file format, and the command line flags that were renamed. Help me upgrade my ngrok agent from v2 to v3: 1. Run `ngrok version` to confirm my current version 2. Run `ngrok update` to install the latest agent 3. Run `ngrok config upgrade` to auto-migrate my configuration file to v3 format 4. Run `ngrok config check` to verify no unrecognized parameters remain 5. Update any scripts that use single-hyphen flags to double-hyphen (for example, `-hostname` → `--url`, `-subdomain` → `--url`, `-auth` → `--basic-auth`, `-bind-tls` → `--url`) 6. Note: HTTP tunnels now only open HTTPS by default — update configs that expect both HTTP and HTTPS endpoints Ask to see my `ngrok.yml` and any wrapper scripts before making changes. ## Upgrading the ngrok Agent to the latest version Before upgrading, it's beneficial to have the latest version of the ngrok agent installed. If you don't, please [update to the latest version](/docs/gateway/agent/cli#ngrok-update) before proceeding. The latest ngrok agent is packed with a ton of new capability but because it is a major release of the agent, there are also some breaking changes to watch out for. ## Breaking changes ### Configuration file The older configuration file is not compatible with the latest ngrok agent without a few changes. To automatically upgrade, run the following CLI command. ```bash theme={null} ngrok config upgrade ``` ### Unrecognized configuration parameters Both the previous ngrok agent silently ignored invalid config parameters. The latest ngrok agent will explicitly error when it sees a configuration parameter that it doesn't recognize. It's a good idea to run a check after upgrading to find incompatible arguments. ```bash theme={null} ngrok config check ``` This will automatically add `version` and `region` options as well as convert the names of legacy options to their new format. If you're only using ngrok with a configuration file, there's nothing else to do. For more information about the changes to the config file, see the [following section](#upgrading-the-ngrok-agent-configuration-file). ### Only HTTPS tunnels by default ngrok agent HTTP tunnels by default will only open a single HTTPS endpoint for your upstream service instead of both an HTTP and HTTPS endpoint. In order to setup both an HTTP and an HTTPS endpoint, configure multiple endpoints via the configuration file. ### Automation using the ngrok Agent If you have written any scripts or built wrappers around the ngrok agent using command line flags, there are additional changes you will need to make. * Installing the Authtoken is now done with the command `ngrok config add-authtoken TOKEN`. Check out the [ngrok config reference documentation](/docs/gateway/agent/cli#ngrok-config) for more information. * The ngrok agent only accepts long name flags prefixed with `--` and will error if a single hyphen is used. When updating your scripts, ensure flags like `--host-header` are using double hyphens. * The following command flags have changed: * `-auth` -> [`--basic-auth`](/docs/gateway/agent/cli/#ngrok-http) * `-bind-tls` -> [`--url`](/docs/gateway/agent/cli/#ngrok-http) * `-hostname` -> [`--url`](/docs/gateway/agent/cli/#ngrok-http) * `-subdomain` -> [`--url subdomain.ngrok-free.dev`](/docs/gateway/agent/cli/#ngrok-http) For a full list of changes to the agent for v3, see the [changelog](/docs/gateway/agent/changelog). ## Changes to choosing a region The ngrok agent v3 changes the default region you connect to when opening a tunnel. Instead of choosing the `us` region, the ngrok agent will attempt to connect to the best region for you, usually the one geographically closest to you. If you run the `ngrok config upgrade` command, it will automatically add a `region: us` if your config file was missing it, since that was previously the default. Check out the [full list of supported regions](/docs/gateway/points-of-presence/). ## Upgrading the ngrok Agent configuration file If you configure ngrok to start with a configuration file, the ngrok agent v3 makes it extremely easy to upgrade by providing the `ngrok config upgrade` command. Running this command will automatically migrate your configuration file to the latest format. * It adds a `version` field which is now required and identifies the configuration version to use. The latest is `version: 2` * It converts configuration parameters that have been renamed, such as `bind_tls` and `auth`, to their updated names ```sh theme={null} $ ngrok config upgrade -h NAME: upgrade - auto-upgrade configuration file USAGE: ngrok config upgrade [version] [flags] DESCRIPTION: Upgrade a configuration file to a version. The default configuration file is located at /home/ubuntu/.config/ngrok/ngrok.yml A backup file will be created with your original configuration file in the same directory. You can optionally pass a version to upgrade to. If the configuration file version is missing, the upgrade command will add it. It also applies all automatic transformations when upgrading versions. Attempting to downgrade will result in an error. By default this command will apply the transformations and display the final file. Use --dry-run to preview changes before applying OPTIONS: --config string upgrade this config file (default /home/ubuntu/.config/ngrok/ngrok.yml) --dry-run preview the proposed changes -h, --help help for upgrade --log string path to log file, 'stdout', 'stderr' or 'false' (default "false") --log-format string log record format: 'term', 'logfmt', 'json' (default "term") --log-level string logging level: 'debug', 'info', 'warn', 'error', 'crit' (default "info") --relocate relocates the config file to the default location ``` The `ngrok config upgrade` command also includes a flag `--relocate` which will move your configuration file to the updated default location for ngrok agent v3. ngrok follows the [XDG specification](https://wiki.archlinux.org/title/XDG_Base_Directory) for storing configuration files. Once you run that command, you should be ready to go with the latest ngrok agent v3. ## Upgrading command line scripts The latest version of the ngrok agent includes an updated command line argument parser which introduces a few breaking changes. * Installing the authtoken is now done with the command `ngrok config add-authtoken TOKEN` * The ngrok agent only accepts long name flags prefixed with `--` and will error if a single hyphen is used. When updating your scripts, ensure things like `--hostname` and `--host-header` are using double hyphens. * The following command flags have changed: * `-auth` -> [`--basic-auth`](/docs/gateway/agent/cli/#ngrok-http) * `-bind-tls` -> [`--url`](/docs/gateway/agent/cli/#ngrok-http) ## Next steps Now that you've upgraded to the latest configuration file, you're ready to start the latest ngrok agent. There's a lot of new stuff in there, but your old commands should work as well (with the changes from above). Run `ngrok -h` for help or check out the [ngrok agent reference documentation](/docs/gateway/agent/cli/). # ngrok Agent Version Support Policy Source: https://ngrok.com/docs/gateway/agent/version-support-policy Learn about ngrok's version support policy including semantic versioning, release cadence, and support periods. This document applies to the following products: * ngrok Agent ## Versioning ngrok follows [Semantic Versioning](https://semver.org/), and limits breaking changes to major upgrades. ## Cadence Typical minor releases happen at least quarterly while typical patch releases are at least monthly. Critical bug fixes may cause a more immediate release outside of the normal cadence. ngrok also aims not to make releases during major U.S. holiday periods. ## Support period In accordance with the release cadence, ngrok will support a minor release for 14 months. Support includes: * Patches * Critical Security Fixes Towards the end of the 14-month support period, the following will happen: * The two-month upgrade cycle will begin. During the last two months of a support period, the following will apply: * No more patches or critical security fixes will occur. * Announcements will be made attempting to notify users through the following channels: * Via email * Via social media * Status Page * Documentation Page * That release’s agent banner After the two-month upgrade cycle has ended, the following will happen: * The Agent version will enter end-of-life and will **no longer be able to connect to ngrok's cloud network**. Attempting to connect with an unsupported agent will result in an error message. ## Extended support Extended support beyond LTS is available to customers on a commercial basis. It includes critical security fixes only. [Contact Support](mailto:support@ngrok.com?subject=Extended%20Support%20Inquiry) for additional details. ## Release support chart | Support Status | Release | Release Date | Latest | Active Support | End of Life | | - | - | - | - | - | - | | Active | 3.39x | 2026-04-27 | 3.39.11 | 2027-04-28 | 2027-06-28 | | Active | 3.38x | 2026-04-17 | 3.38.0 | 2027-04-17 | 2027-06-17 | | Active | 3.37x | 2026-03-05 | 3.37.6 | 2027-03-05 | 2027-06-09 | | Active | 3.36x | 2026-02-03 | 3.36.1 | 2027-02-03 | 2027-04-03 | | Active | 3.35x | 2026-01-12 | 3.35.0 | 2027-01-12 | 2027-03-12 | | Active | 3.34x | 2025-12-05 | 3.34.1 | 2026-12-11 | 2027-02-11 | | Active | 3.33x | 2025-11-12 | 3.33.1 | 2026-11-12 | 2027-01-12 | | Active | 3.32x | 2025-10-23 | 3.32.0 | 2026-10-23 | 2027-12-23 | | Active | 3.31x | 2025-10-20 | 3.31.0 | 2026-10-20 | 2027-12-20 | | Active | 3.30x | 2025-09-23 | 3.30.0 | 2026-09-23 | 2027-12-23 | | Active | 3.29x | 2025-09-17 | 3.29.0 | 2026-09-17 | 2027-11-17 | | Active | 3.28x | 2025-09-11 | 3.28.0 | 2026-09-11 | 2027-11-11 | | Active | 3.27x | 2025-08-28 | 3.27.0 | 2026-08-28 | 2026-10-28 | | Active | 3.26x | 2025-08-07 | 3.26.0 | 2026-08-07 | 2026-10-07 | | Active | 3.25x | 2025-08-01 | 3.25.1 | 2026-08-01 | 2026-10-01 | | Active | 3.24x | 2025-07-17 | 3.24.0 | 2026-07-17 | 2026-09-17 | | Active | 3.23x | 2025-06-05 | 3.23.3 | 2026-06-26 | 2026-08-26 | | End of life | 3.22x | 2025-03-20 | 3.22.1 | 2026-03-20 | 2026-05-20 | | End of life | 3.21x | 2025-03-13 | 3.21.0 | 2026-03-13 | 2026-05-13 | | End of life | 3.20x | 2025-02-20 | 3.20.0 | 2026-02-20 | 2026-04-20 | | End of life | 3.19x | 2024-12-19 | 3.19.1 | 2025-12-19 | 2026-02-17 | | End of life | 3.18x | 2024-10-17 | 3.18.4 | 2025-10-17 | 2025-12-17 | | End of life | 3.17x | 2024-10-10 | 3.17.1 | 2025-10-10 | 2025-12-10 | | End of life | 3.16x | 2024-09-09 | 3.16.1 | 2025-09-09 | 2025-11-09 | | End of life | 3.15x | 2024-08-29 | 3.15.0 | 2025-08-29 | 2025-10-29 | | End of life | 3.14x | 2024-08-01 | 3.14.0 | 2025-08-01 | 2025-10-01 | | End of life | 3.13x | 2024-07-18 | 3.13.0 | 2025-07-18 | 2025-09-18 | | End of life | 3.12x | 2024-06-27 | 3.12.1 | 2025-06-27 | 2025-08-27 | | End of life | 3.11x | 2024-06-13 | 3.11.0 | 2025-06-13 | 2025-08-13 | | End of life | 3.10x | 2024-05-23 | 3.10.1 | 2025-05-23 | 2025-07-23 | | End of life | 3.9x | 2024-04-24 | 3.9.0 | 2025-04-24 | 2025-06-24 | | End of life | 3.8x | 2024-03-14 | 3.8.0 | 2025-03-14 | 2025-05-14 | | End of life | 3.7x | 2024-03-07 | 3.7.0 | 2025-03-07 | 2025-05-07 | | End of life | 3.6x | 2024-02-07 | 3.6.0 | 2025-02-07 | 2025-04-07 | | End of life | 3.5x | 2023-12-07 | 3.5.0 | 2024-12-07 | 2025-02-07 | | End of life | 3.4x | 2023-11-03 | 3.4.0 | 2024-11-03 | 2025-01-03 | | End of life | 3.3x | 2023-05-09 | 3.3.5 | 2024-05-09 | 2024-08-12 | | End of life | 3.2x | 2023-03-13 | 3.2.2 | 2024-03-13 | 2024-08-12 | | End of life | 3.1x | 2022-09-14 | 3.1.1 | 2023-09-14 | 2023-11-14 | | End of life | 3.0x | 2022-03-28 | 3.0.7 | 2023-03-28 | 2023-05-28 | | End of life | 2.3x | 2018-12-04 | 2.3.40 | 2019-12-04 | 2020-02-04 | # ngrok Agent CLI Web Inspection Interface Source: https://ngrok.com/docs/gateway/agent/web-inspection-interface Learn how to use the ngrok Agent CLI web inspection interface at localhost:4040 to view HTTP requests and responses in real-time. The ngrok Agent CLI ships with a realtime inspection interface which allows you to see what traffic is sent to your upstream service and what responses it's returning. The Web Inspection Interface is only available in the ngrok Agent CLI and not in the ngrok Agent SDKs. If you want to view traffic across all endpoints, longer retention periods, or sharing traffic events with other teammates, check out the [Traffic Inspector in the ngrok Dashboard](/docs/obs/traffic-inspection/#ngrok-traffic-inspector) instead. ## Inspecting requests Every HTTP request through your endpoints will be displayed in the inspection interface. After you start the ngrok agent, open [http://localhost:4040](http://localhost:4040) in a browser on the same machine. You will see all of the details of every request and response including the time, duration, source IP, headers, query parameters, request payload and response body as well as the raw bytes on the wire. The inspection interface has a few limitations. If an entity-body is too long, ngrok may only capture the initial portion of the request body. ngrok also doesn't display provisional 100 responses from a server. Inspection is only supported for HTTP endpoints. TCP and TLS endpoints don't support inspection and don't show up in the inspection interface. ###### Detailed introspection of HTTP requests and responses ngrok agent web inspection interface showing HTTP request and response detail with Summary, Headers, Raw, and Binary tabs ## Request body validation ngrok has special support for the most common data interchange formats in use on the web. Any XML or JSON data in request or response bodies is automatically pretty-printed and checked for syntax errors. ###### The location of a JSON syntax error is highlighted ngrok web inspection interface showing a JSON syntax error with the problematic key highlighted in orange ## Filtering requests Your upstream service may receive many requests, but you usually only want to inspect some of them. You can filter the requests that ngrok displays to you. You can filter based on the request path, response status code, size of the response body, duration of the request and the value of any header. ###### Click the filter bar for filtering options ngrok web inspection interface filter dropdown showing options including Path, ResponseSize, and StatusCode You may specify multiple filters. If you do, requests will only be shown if they much all filters. ###### Filter requests by path and status code ngrok web inspection interface showing filtered requests matching path and status code filters with one result ## Replaying requests Developing for webhooks issued by external APIs can often slow down your development cycle by requiring you do some work, like dialing a phone, to trigger the hook request. ngrok allows you to replay any request with a single click, dramatically speeding up your iteration cycle. Click the **Replay** button at the top-right corner of any request on the web inspection UI to replay it. Replay works via the local agent sending the request directly to your upstream service. As such, the replayed request isn't subject to any policies that exist on your Cloud Endpoint since those are applied prior to the request reaching the local agent. To replay the original request before the endpoint policies are applied, or to test new policies, use the [Traffic Inspector in the ngrok Dashboard](/docs/obs/traffic-inspection/#ngrok-traffic-inspector). ###### Replay any request against your web server with one click ngrok web inspection interface showing a captured request with the Replay button in the top right corner ## Replaying modified requests Sometimes you want to modify a request before you replay it to test a new behavior in your upstream service. ###### Click the dropdown arrow on the 'Replay' button to modify a request before it is replayed ngrok web inspection interface showing the Replay button dropdown with the Replay with Modifications option The replay editor allows you to modify every aspect of the HTTP request before replaying it, including the method, path, headers, trailers, and request body. ###### The request replay modification editor ngrok web inspection Modify Request dialog showing editable method, path, headers, and body fields ## ngrok Agent status page ngrok's local web interface has a dedicated status page that shows configuration and metrics information about the running ngrok process. You can access it at [http://localhost:4040/status](http://localhost:4040/status). The status page displays the configuration of each running endpoint and any global configuration options that ngrok has parsed from its configuration file. ###### Endpoint and global configuration ngrok agent status page showing Configuration section with tunnel URLs, addresses, and global settings The status page also display metrics about the traffic through each endpoint. It display connection rates and connection duration percentiles for all endpoints. For HTTP endpoints, it also displays http request rates and http response duration percentiles. ###### Endpoint traffic metrics ngrok agent status page showing Metrics section with connection counts, connection duration percentiles, and HTTP request rates # Get started with ngrok's API gateway Source: https://ngrok.com/docs/gateway/api-gateway/get-started Learn how to set up ngrok as an API gateway using a Cloud Endpoint that forwards traffic to internal Agent Endpoints with Traffic Policy. You've developed a world-class API, and now you want to make it available online. Aside from the challenges involved in any API gateway deployment, you're looking for a solution that allows you to: * Consistently apply security and traffic management policy in one place * Observe all your API requests and responses from a single dashboard with [Traffic Inspector](/docs/obs/traffic-inspection/#ngrok-traffic-inspector) * Add ingress to your APIs in any environment with the same agent configuration and Traffic Policy rules ## What you'll learn In this tutorial, you'll learn how to implement ngrok as an API gateway with these broad steps: 1. Set up the common pattern for ngrok's API gateway. 2. Create one or more internal Agent Endpoints for your upstream API services. 3. Create a Cloud Endpoint to centralize your traffic management policies, including how to forward traffic to your internal Agent Endpoint. ## What you'll need * **A domain**: This can be your free dev domain, or if you're on a paid plan, one that you choose [in the ngrok dashboard](https://dashboard.ngrok.com/domains) or with the [ngrok API](/docs/api-reference/reserveddomains/create). Pay-as-you-go plans allow you to bring your own [custom branded domain](/docs/gateway/domains/custom-domains), like `https://api.example.com`. * This guide refers to this domain as `$YOUR_DOMAIN`. * (optional) **An API key**: [Create an ngrok API key](https://dashboard.ngrok.com/api-keys/new) if you'd like to use the ngrok API to manage your Cloud Endpoints. ### Deploy a demo API service (optional) If you don't yet have API services you'd like to bring online with an API gateway, or just want to quickly wire up a POC using ngrok, use the [ngrok demo API](https://github.com/ngrok-samples/api-demo), which responds with details about the request. Assuming you have Docker installed on the systems where your API services run, you can deploy a container listening on port `4000`. ```bash theme={null} docker run -p 4000:4000 -e MESSAGE='Hello from service abc!' -d joelatngrok/api-demo ``` Start up a second container on port `5000`. ```bash theme={null} docker run -p 5000:4000 -e MESSAGE='Hello from service xyz!' -d joelatngrok/api-demo ``` ## 1. Create an internal Agent endpoint Your upstream API service needs a way of receiving traffic from the ngrok network, which you can establish with an Agent Endpoint on an internal URL like `https://abc.internal`. Replace `4000` if you've brought your own API service. ``` ngrok http 4000 --url https://abc.internal ``` Your API service isn't yet accessible on the public internet. To fix that, you need two things: 1. A Cloud Endpoint for traffic routing and centralized policy management. 2. A Traffic Policy rule that forwards traffic from your Cloud Endpoint to `https://abc.internal`. ## 2. Create a Cloud Endpoint Cloud endpoints are persistent, always-on endpoints that you can manage with the ngrok dashboard or API. You centrally control your traffic management and security policy on your cloud endpoint, then forward traffic to your endpoint pool. That's much easier than managing these policies for each service separately and trying to keep them in sync to ward off configuration drift. First, log into the [ngrok dashboard](https://dashboard.ngrok.com). Select **Endpoints → + New**. Leave the **Binding** value **Public**, then enter the domain name you reserved earlier. Select **Create Cloud Endpoint**. With your Cloud Endpoint created, you'll see a default Traffic Policy in the dashboard. Paste in the YAML below to apply the rule. ```yaml title="policy.yaml" mode=traffic-policy theme={null} on_http_request: - actions: - type: forward-internal config: url: https://abc.internal ``` Select **Save** to apply your changes. The `ngrok` CLI provides a helpful wrapper around the [ngrok API](/docs/api/index), which you can use to create a Cloud Endpoint and apply a file containing Traffic Policy rules. Create a new file named `policy.yaml` on your local workstation with the following YAML. ```yaml title="policy.yaml" mode=traffic-policy theme={null} on_http_request: - actions: - type: forward-internal config: url: https://abc.internal ``` Create a Cloud Endpoint on `$YOUR_DOMAIN`, passing your `policy.yaml` file as an option. ```bash theme={null} ngrok api endpoints create \ --bindings public \ --url https://$YOUR_DOMAIN --traffic-policy "$(cat policy.yaml)" ``` You'll get a `201` response—save the value of `id`, as you'll need it again later to continue configuring the Traffic Policy applied to your Cloud Endpoint. At this point, your API gateway is ready for traffic. Send the following request to test it out: ```bash theme={null} curl -X GET https://$YOUR_DOMAIN ``` If you're using the demo API, you'll see a response like: ```json theme={null} { "message": "Hello from ngrok!", "host": "localhost:4000", "req_headers": { "host": "localhost:4000", "user-agent": "curl/8.5.0", "accept": "*/*" }, "method": "GET", "url": "/", "time": "2025-02-06T21:44:23.116Z", "status": 200 } ``` ## 3. Route traffic to multiple services with Traffic Policy Your API gateway is ready, but chances are you need to handle ingress into more than a one API service. If you're using the demo API service, fire up another internal Agent Endpoint to route traffic to the second container on port `5000`. ```bash theme={null} ngrok http 5000 --url https://xyz.internal ``` Use the [Traffic Policy system](/docs/traffic-policy), which lets you filter traffic based on its properties and take action as it passes through ngrok's global network. Two important concepts of Traffic Policy to note: * [Phases](/docs/gateway/traffic-policy/how-it-works#phases) are the distinct points in the lifecycle of a request where you can filter and take action. For this use case, `on_http_request` is used, which activates when ngrok receives an HTTP request over an established connection. * [Expressions](/docs/gateway/traffic-policy/how-it-works#expressions) define when to run your actions. They're written in [Common Expression Language](https://github.com/google/cel-spec), and must evaluate to `true` to run the corresponding action. The rules below: 1. Filter for requests arriving on *only* `https://$YOUR_DOMAIN/abc` and forward them to your internal Agent Endpoint at `https://abc.internal`. 2. Filter for requests arriving on *only* `https://$YOUR_DOMAIN/xyz` and forward them to your internal Agent Endpoint at `https://xyz.internal`. You can also [route by other properties](/docs/gateway/traffic-policy/examples/route-requests), like subdomains and headers. Copy and paste the rules below into your Cloud Endpoint's Traffic Policy editor in the dashboard. If you're bringing your own API services instead of using the demo API, you'll need to change `/abc` and `/xyz` to match your services' paths and the `url` for your internal Agent Endpoints. ```yaml mode=traffic-policy title="policy.yaml" theme={null} on_http_request: - expressions: - "req.url.path.startsWith('/abc')" actions: - type: forward-internal config: url: https://abc.internal - expressions: - "req.url.path.startsWith('/xyz')" actions: - type: forward-internal config: url: https://xyz.internal ``` Hit **Save** to lock in the new policy. Update your existing `policy.yaml` file with the YAML below. If you're bringing your own API services instead of using the demo API, you'll need to change `/abc` and `/xyz` to match your services' paths and the `url` for your internal Agent Endpoints. ```yaml mode=traffic-policy title="policy.yaml" theme={null} on_http_request: - expressions: - "req.url.path.startsWith('/abc')" actions: - type: forward-internal config: url: https://abc.internal - expressions: - "req.url.path.startsWith('/xyz')" actions: - type: forward-internal config: url: https://xyz.internal ``` Update your Cloud Endpoint. ```bash theme={null} ngrok api endpoints update \ --traffic-policy "$(cat policy.yaml)" ``` You can now `curl` different paths to see your requests routed to the appropriate upstream API service. ```bash theme={null} curl https://$YOUR_DOMAIN/abc ``` You should get a response like: ``` {"message":"Hello from service abc!","host":"$YOUR_DOMAIN","req_headers":{"host":"$YOUR_DOMAIN","user-agent":"curl/8.7.1","accept":"*/*","accept-encoding":"gzip"},"method":"GET","url":"/abc","time":"2025-03-04T01:10:25.812Z","status":200} ``` And when you run `curl https://$YOUR_DOMAIN/xyz`? ``` {"message":"Hello from service xyz!","host":"$YOUR_DOMAIN","req_headers":{"host":"$YOUR_DOMAIN","user-agent":"curl/8.7.1","accept":"*/*","accept-encoding":"gzip"},"method":"GET","url":"/xyz","time":"2025-03-04T01:10:25.812Z","status":200} ``` ## 4. Add traffic management policies Your API gateway routes traffic, but doesn't yet do essential work of an API gateway: offload all the non-functional requirements away from your services. One great feature of ngrok's building blocks of endpoints and Traffic Policy rules is that they're composable—you can reuse them, chain them, and apply them at multiple stages in the lifecycle of an API request. With the shape you've already created, you can centrally manage certain policies, like authentication, on your Cloud Endpoint, then compose additional rules onto specific services. ### Validate JWTs on all APIs and requests API authentication is too important not to apply consistently across all your APIs and requests. That's where the always-on, *front door to all your routes* quality of a Cloud Endpoint comes in handy—you can apply the [`jwt-validation` action](/docs/gateway/traffic-policy/actions/jwt-validation) once for dependable AuthN no matter how many services you end up deploying behind your multicloud API gateway. ngrok's JWT validation action helps you: * Give your end users many ways to access your APIs. * Ensure only requests containing the correct access token, specified by an `Authorization: Bearer ...` header, can access any of your APIs. * Add claims to tokens for authorization and fine-grained access control where a specific token may only have access to a certain API (`service_access: abc`) or apply RBAC (`features: read`). * Use a single credential for end users who need to access multiple upstream services. * Offload all this logic from your API services and run it in ngrok's network. You can use any OAuth provider for JWT validation. The process with Auth0 is covered below. * Log in to your [Auth0 tenant dashboard](https://manage.auth0.com/dashboard). * Select **Applications > APIs**, then **+ Create API**. * Name your API whatever you'd like. * Replace the value of the Identifier field with `$YOUR_DOMAIN`. * Leave the default values for **JSON Web Token (JWT) Profile** and **JSON Web Token Signing Algorithm**. * Select **Create**. * Navigate to your application and select the **Test** tab, where you can find a signed, fully functional JWT and examples of how to programmatically generate more. The rule below builds on top of the previous Cloud Endpoint policy to: 1. Reject requests missing a token with a `401 Unauthorized` error. 2. Reject requests with an invalid token with a `403 Forbidden` error. 3. Forward requests with a valid token to one of your internal Agent Endpoints based on the pathname. You'll need to change the variables accordingly—if you're not sure where to find this information, see the full [integration guide](/docs/integrations/jwt-validation/auth0) with more details. ```yaml mode="traffic-policy" title="policy.yaml" theme={null} on_http_request: - actions: - type: jwt-validation config: issuer: allow_list: - value: "https://..auth0.com/" audience: allow_list: - value: "https://$YOUR_DOMAIN" http: tokens: - type: "jwt" method: "header" name: "Authorization" prefix: "Bearer " jws: allowed_algorithms: - "RS256" keys: sources: additional_jkus: - "https://..auth0.com/.well-known/jwks.json" - expressions: - "req.url.path.startsWith('/abc')" actions: - type: forward-internal config: url: https://abc.internal - expressions: - "req.url.path.startsWith('/xyz')" actions: - type: forward-internal config: url: https://xyz.internal ``` Apply in either the dashboard or ngrok API. ### 5. Rate limit specific API services For example, one of your services (like `abc` on port `4000`, if you're following along with the demo service) may need additional protection from unintentional misuse and malicious attacks. The [`rate-limit` Traffic Policy action](/docs/gateway/traffic-policy/actions/rate-limit) allows you to reject requests with a `429` error code once a user or group have exceeded your customizable threshold. Create a new file named `abc-policy.yaml` on the system where you're running your API services and ngrok agent and paste in the YAML below. The rule below creates new policy *at your agent* to: 1. Allow up to `10` requests per IP in a `60s` window. 2. Reject requests that exceed the rate limiting capacity with a `429` error response. ```yaml title="abc-policy.yaml" mode="file" theme={null} on_http_request: - actions: - type: rate-limit config: name: Only allow 10 requests per minute algorithm: sliding_window capacity: 10 rate: 60s bucket_key: - conn.client_ip ``` Restart the ngrok agent to enable `abc-policy.yaml`. ```bash theme={null} ngrok http 4000 --url https://abc.internal --traffic-policy-file /path/to/abc-policy.yaml ``` Ready to test your rate limit in action? Run the below command after replacing `$YOUR_DOMAIN` and the path, if relevant. ```bash theme={null} for i in `seq 1 50`; do curl -X GET -w '%{http_code}' https://$YOUR_DOMAIN/abc ; done ``` You'll see a few normal responses until you hit the rate limit, and then you'll see `429` errors. Run the same command on the `/xyz` path and you won't see the same errors, since you've applied this policy only to the `https://abc.internal` Agent Endpoint. If you want all your APIs to have a consistent rate limiting strategy, you can move the rule to your Cloud Endpoint above the `jwt-validation` action. ## What's next? You've now brought your APIs online with ngrok's API gateway, which also automatically gives you features like DDoS protection and global load balancing. Plus, you've added global AuthN with JWTs and explored composing Traffic Policy rules on multiple endpoints. That said, your journey into deploying and securing services with ngrok's API gateway is just beginning. Next up: 1. Check out your [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) ([documentation](/docs/obs/traffic-inspection#ngrok-traffic-inspector)) to observe, modify, and replay requests across your API gateway. 2. Explore other opportunities to manage and take action on API traffic in the [Traffic Policy documentation](/docs/traffic-policy). 3. Learn [how to monitor your API traffic](/docs/gateway/api-gateway/monitor-ngrok), so you'll know how your service is performing at all times, and be alerted to errors. # Expose an MCP Server to ChatGPT, Claude, Grok, and Cursor Source: https://ngrok.com/docs/gateway/api-gateway/mcp-gateway Give a local MCP server a stable public HTTPS URL with ngrok, secure it with Traffic Policy, and connect ChatGPT, Claude, Grok, and Cursor. AI apps like ChatGPT, Claude.ai, and Grok call MCP servers from their own cloud. They can't reach a server running on `localhost` on your laptop, VM, or container. This guide shows you how to give an MCP server a public HTTPS URL with ngrok, lock it down so only your AI client can call it, and connect it to each major MCP client. The URL is stable on every ngrok plan, including free, so you configure each client once. Claude Code and Cursor run on your machine, so they can already reach a server on the same machine at `localhost`. Use ngrok with them when the server runs on another machine, or to test the same URL and authentication a remote client will use. ## Example scenario You're building an MCP server that wraps your team's internal ticketing API. It runs on your laptop on port `3001` and serves MCP at `/mcp`. Before you deploy it anywhere, you want to try it from ChatGPT, Claude, Grok, and Cursor, and you don't want anyone else on the internet calling its tools. You need three things: 1. A public HTTPS URL that forwards to `localhost:3001`. 2. Authentication in front of that URL, so only your AI client gets through. 3. A way to see exactly what each client sends when something doesn't work. ## Architectural reference ```mermaid theme={null} sequenceDiagram participant Client as AI client (ChatGPT, Claude, Grok, Cursor) participant Cloud as ngrok cloud (your dev domain) participant Agent as ngrok agent (your machine) participant MCP as MCP server (localhost:3001/mcp) Client->>Cloud: POST https://your-dev-domain/mcp Cloud->>Cloud: Traffic Policy: check token, path, or IP Cloud->>Agent: Forward over the agent's outbound connection Agent->>MCP: POST /mcp MCP-->>Agent: JSON or SSE stream Agent-->>Cloud: Response Cloud-->>Client: Response ``` The ngrok agent opens an outbound connection from your machine to ngrok's cloud. You don't open any inbound ports, change your firewall, or deploy anything. Traffic Policy runs in ngrok's cloud, so requests that fail your rules never reach your machine. ### Which MCP transports work? | Transport | How the client connects | Works with remote AI apps through ngrok? | | - | - | - | | **stdio** | The client starts the server as a local process | No. Remote clients can't start a process on your machine. Run the server in HTTP mode or wrap it with a stdio-to-HTTP bridge. | | **HTTP + SSE** (older) | The client holds a long-lived Server-Sent Events stream | Yes | | **Streamable HTTP** (current) | The client POSTs JSON-RPC to one URL; the server replies with JSON or an SSE stream | Yes (recommended) | ### Which authentication fits your client? Claude Code and Cursor let you set custom headers on an MCP server, so they can send a bearer token. ChatGPT and Claude.ai connectors take a URL and support OAuth, but don't let you add a header. For those, use a [secret path](#use-a-secret-path) while you test, and [OAuth](#use-oauth-for-real-users-and-real-data) when real users rely on the server. ## Tutorial ### What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * The [ngrok agent](https://ngrok.com/download) installed on the machine that runs your MCP server. * An MCP server that serves Streamable HTTP or HTTP + SSE. This guide uses port `3001` and the path `/mcp`. ### 1. Check that your MCP server responds Send an MCP `initialize` request to your server: ```bash theme={null} curl -s -X POST http://localhost:3001/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}' ``` A JSON-RPC response means the server is up. Many MCP servers return `404` or `405` for a plain `GET /`, so don't test in a browser. ### 2. Create a Traffic Policy that requires a token Anyone with your URL can call your server, and MCP tools can act on your machine. Protect it before it goes online. This step uses a bearer token, which works for clients that can send a header. If your client only takes a URL, use the policy in [Use a secret path](#use-a-secret-path) instead, then continue with step 3. Generate a long random token: ```bash theme={null} openssl rand -hex 32 ``` Save this as `policy.yml`, replacing `YOUR_TOKEN` with the value you generated: ```yaml theme={null} on_http_request: - expressions: - "!hasReqHeader('Authorization') || getReqHeader('Authorization')[0] != 'Bearer YOUR_TOKEN'" actions: - type: deny config: status_code: 401 ``` This rule rejects any request without the right bearer token. On a paid plan, also limit how often each client can call your server with the [rate-limit](/docs/gateway/traffic-policy/actions/rate-limit/) action. Add this rule after the first one: ```yaml theme={null} - actions: - type: rate-limit config: name: mcp algorithm: sliding_window capacity: 60 rate: 60s bucket_key: - conn.client_ip ``` ### 3. Put your MCP server online ```bash theme={null} ngrok config add-authtoken ngrok http 3001 --traffic-policy-file policy.yml ``` ngrok prints your public URL, such as `https://your-assigned-name.ngrok-free.dev`. This is your account's [dev domain](/docs/pricing-limits/free-plan-limits/). It stays the same every time you restart `ngrok http`, so your client configs keep working. Your MCP URL is the public URL plus your server's path: ```text theme={null} https://your-assigned-name.ngrok-free.dev/mcp ``` Confirm the policy works from another terminal: ```bash theme={null} # Without the token: 401 curl -s -o /dev/null -w '%{http_code}\n' -X POST https://your-assigned-name.ngrok-free.dev/mcp # With the token: your server's response curl -s -X POST https://your-assigned-name.ngrok-free.dev/mcp \ -H 'Authorization: Bearer YOUR_TOKEN' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}' ``` ### 4. Connect your AI client ```bash theme={null} claude mcp add --transport http tickets https://your-assigned-name.ngrok-free.dev/mcp \ --header "Authorization: Bearer YOUR_TOKEN" ``` Add the server to `~/.cursor/mcp.json` for all projects, or `.cursor/mcp.json` for one project: ```json theme={null} { "mcpServers": { "tickets": { "url": "https://your-assigned-name.ngrok-free.dev/mcp", "headers": { "Authorization": "Bearer YOUR_TOKEN" } } } } ``` Turn on [developer mode](https://help.openai.com/en/articles/12584461), then create a connector. ChatGPT can't send a bearer token, so give it your [secret path](#use-a-secret-path) URL, or set up [OAuth](#use-oauth-for-real-users-and-real-data). Add a custom connector in **Settings** > **Connectors**. Claude.ai can't send a bearer token, so give it your [secret path](#use-a-secret-path) URL, or set up [OAuth](#use-oauth-for-real-users-and-real-data). Go to [grok.com/connectors](https://grok.com/connectors), click **New Connector**, choose **Custom**, and paste your MCP URL. On Grok Business and Enterprise, an admin provisions the connector first. If the connector settings don't let you add a header, use a [secret path](#use-a-secret-path) or [OAuth](#use-oauth-for-real-users-and-real-data) instead. Then ask your client: What tools do you have available from the tickets MCP server? ### 5. Watch the traffic Open `http://localhost:4040` while your client connects. The ngrok inspector shows every request your client sends and every response your server returns, including JSON-RPC bodies. When a client says it can't list tools, start here: you see whether the request arrived, what it contained, and what your server sent back. ## Secure clients that only take a URL Some connector settings only accept a URL. Use one of these instead of a bearer token. ### Use a secret path Serve your MCP endpoint under a long random secret, such as `/s/YOUR_SECRET/mcp`, by changing the path in your server's configuration. Then reject every other path at ngrok, so guesses never reach your machine: ```yaml theme={null} on_http_request: - expressions: - "!req.url.path.startsWith('/s/YOUR_SECRET/')" actions: - type: deny config: status_code: 404 ``` Give the client `https://your-assigned-name.ngrok-free.dev/s/YOUR_SECRET/mcp`. On a paid plan, you can leave your server on `/mcp` and strip the secret at ngrok with the [url-rewrite](/docs/gateway/traffic-policy/actions/url-rewrite/) action. Add this rule after the deny rule: ```yaml theme={null} - actions: - type: url-rewrite config: from: "/s/YOUR_SECRET/" to: "/" ``` A secret path is a shared secret, not an identity. Rotate it if it leaks, and don't use it for servers that touch production data. ### Allow only the AI provider's network ngrok maintains [IP categories](/docs/gateway/traffic-policy/variables/ip-intel/) for major services, including Anthropic and OpenAI. This rule admits only Anthropic's API servers: ```yaml theme={null} on_http_request: - expressions: - "!('com.anthropic.api' in conn.client_ip.categories)" actions: - type: deny config: status_code: 403 ``` Combine it with a token or secret path. IP rules alone don't tell you which user is calling. ### Use OAuth for real users and real data Implement OAuth in your MCP server according to the [MCP authorization spec](https://modelcontextprotocol.io/specification/draft/basic/authorization). Clients that support it run the sign-in flow when your server asks for it. ngrok forwards the OAuth discovery and token requests like any other request. If your identity provider issues JWTs, the [JWT validation](/docs/gateway/traffic-policy/actions/jwt-validation/) action can reject bad tokens at the edge, before they reach your machine. ## Security checklist Before you connect an MCP server to an AI app, check that you: * Require a token, secret path, or OAuth on every request. * Rate limit requests with the [rate-limit](/docs/gateway/traffic-policy/actions/rate-limit/) action, on a paid plan. * Expose only the tools the client needs. An AI app can call any tool your server lists. * Keep secrets like API keys in your server's environment, never in tool responses. * Stop the `ngrok` process when you're done testing. The URL goes offline with it. * Move to an [internal endpoint behind a Cloud Endpoint](#keep-the-server-private) before other people rely on it. ## Why not a Cloudflare quick tunnel? Quick tunnels (`cloudflared tunnel --url`) don't need an account. But [Cloudflare's docs](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/) say quick tunnels don't support Server-Sent Events (SSE), are limited to 200 in-flight requests, and are for testing only. MCP's Streamable HTTP transport uses SSE when a server streams a response, sends progress, or pushes notifications, so those servers break behind a quick tunnel. The quick-tunnel URL also changes every run, and it has no authentication in front of it. For a broader comparison, see [ngrok vs. Cloudflare Tunnel](https://ngrok.com/compare/cloudflare-tunnel). ## Keep the server private If other people or production systems will rely on the server, don't leave it on a public Agent Endpoint. Run it behind an [internal endpoint](/docs/gateway/endpoints/internal-endpoints/) and put an always-on [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/) in front of it. The Cloud Endpoint holds your Traffic Policy and forwards to the internal endpoint. The same pattern reaches an MCP server in a VPC, a homelab, or a customer's network. Architecture diagram showing an MCP Host AI app routing through a ngrok Public Cloud Endpoint with Traffic Policy to an Internal Agent Endpoint and ngrok Agent serving an MCP Server Process ### Declare an internal endpoint Internal endpoints only receive traffic forwarded by the [forward-internal](/docs/gateway/traffic-policy/actions/forward-internal/) action. Their hostnames must end with `.internal`. Define one in the agent configuration file on the machine that runs your MCP server: ```yaml theme={null} version: 3 agent: authtoken: endpoints: - name: tickets-mcp url: https://tickets-mcp.internal upstream: url: 3001 ``` ### Create a Cloud Endpoint Cloud Endpoints are always-on endpoints that you manage in the dashboard or API. They only use their attached Traffic Policy to handle connections. In your ngrok dashboard, go to [**Endpoints**](https://dashboard.ngrok.com/endpoints) and click **New**. Give it a public URL such as `https://mcp.example.com`. ### Attach Traffic Policy Replace the Cloud Endpoint's default Traffic Policy. This example admits only Anthropic's API servers, requires an `Authorization` header, rate limits per token, and forwards to the internal endpoint: ```yaml theme={null} on_http_request: - expressions: - "!('com.anthropic.api' in conn.client_ip.categories)" actions: - type: deny config: status_code: 403 - expressions: - "!hasReqHeader('Authorization')" actions: - type: deny config: status_code: 401 body: "Authorization required" - actions: - type: rate-limit config: name: Only allow 100 requests per minute algorithm: sliding_window capacity: 100 rate: 60s bucket_key: - "getReqHeader('Authorization')[0]" - type: forward-internal config: url: https://tickets-mcp.internal ``` The rate-limit action requires a paid plan. On the free plan, remove it and keep the `forward-internal` action. ### Test it * Remove the token from your client config. The request returns `401` with `Authorization required`. * `curl` the URL from your own machine. The request returns `403` because it doesn't come from an Anthropic IP. * Send a prompt from Claude with the token set. The request reaches your MCP server. ## Troubleshooting | Symptom | Likely cause | Fix | | - | - | - | | The client can't connect, and nothing appears at `localhost:4040` | ngrok isn't running, or the URL or path is wrong | Check that the URL ends with your server's path, such as `/mcp`. | | ngrok won't start and shows `ERR_NGROK_2263` | Your policy uses an action your plan doesn't include, such as rate-limit or url-rewrite | Remove that rule, or [upgrade your plan](https://ngrok.com/pricing). | | Every request returns `401` | The client isn't sending the header, or the token doesn't match | Check the header in the inspector at `localhost:4040`. The value must be `Bearer ` followed by the token. | | The request appears at `localhost:4040` with `ERR_NGROK_8012` | ngrok reached your machine but not your server | Your server stopped, is on a different port, or is bound to `127.0.0.1` inside a container. | | `GET /` returns `404` or `405` | This is normal for MCP servers | Test with the `initialize` request in step 1. | | The tools list is empty | The server only speaks stdio | Run the server in HTTP mode or behind a stdio-to-HTTP bridge. | | Streamed responses or progress updates never arrive | The tunnel doesn't support SSE, such as a Cloudflare quick tunnel | Use an ngrok Agent Endpoint. | | The URL changed after a restart | You're using a random-URL tunnel or an old ngrok agent | ngrok dev domains are stable. Run `ngrok update`. | ## Frequently asked questions Run your MCP server over HTTP, then run `ngrok http` with its port and a Traffic Policy that requires a token or secret path. Give the client your ngrok URL plus the server's path, such as `https://your-assigned-name.ngrok-free.dev/mcp`. No. Every ngrok account, including free, gets a dev domain that `ngrok http` reuses on every run. Not directly. Remote AI apps can't start a process on your machine. Run the server in HTTP mode, or wrap it with a stdio-to-HTTP bridge and point ngrok at the bridge's port. Only with authentication in front of it. Require a bearer token, secret path, or OAuth, and rate limit requests on a paid plan. ngrok applies these rules in its cloud, so rejected requests never reach your machine. Not to start. The free plan includes the dev domain, the bearer token and secret path rules, and the IP category rule in this guide. The rate-limit and url-rewrite actions require a paid plan. See [pricing](https://ngrok.com/pricing) for what each plan includes, and [free plan limits](/docs/pricing-limits/free-plan-limits/) for request limits. Yes. Run the ngrok agent next to the server with an internal endpoint, and put a Cloud Endpoint with your Traffic Policy in front of it. See [Keep the server private](#keep-the-server-private). ## What's next * Learn the [Traffic Policy](/docs/gateway/traffic-policy/) rules you can put in front of your server. * Reach MCP servers in other networks with [site-to-site connectivity](/docs/gateway/site-to-site-connectivity/). * Put ngrok's [AI Gateway](/docs/ai-gateway/overview/) in front of your inference providers for routing and failover. # Using Datadog Source: https://ngrok.com/docs/gateway/api-gateway/monitor-ngrok Learn how to observe your API gateway and the traffic passing through it to debug, create monitoring dashboards, and set alerts in Datadog. This guide explains how to monitor your API gateway and the traffic passing through it with ngrok by viewing traffic reports, using error request replays, and exporting logs and events to an external dashboard. ## What you'll need * A [Datadog account](https://www.datadoghq.com/) and API key. * A project with ngrok deployed as an API gateway. If you don't have one, you can use one of the following guides to get started: * [Get started with ngrok's API gateway](/docs/gateway/api-gateway/get-started/) * [Deploy an API gateway in one or multiple clouds](/docs/gateway/api-gateway/multicloud/) ## Options for monitoring your API gateway and services You can monitor API services in two ways in ngrok: with the [Traffic Inspector](/docs/obs/traffic-inspection/) and with [Logs](/docs/obs/events). The Traffic Inspector is a filterable list of your API's requests and responses, like structured logs in a human-readable format, available on the ngrok dashboard. The Traffic Inspector is useful for viewing error details and for replaying requests to test new policies and bug fixes. A traffic log is the data that ngrok aggregates about a request, which you can then export to a dedicated monitoring platform. Traffic logs offer an automated means of monitoring your API. They are also the only way for your team to get automatic error alerts (notifications) instead of manually checking for errors on a dashboard. At the time of writing, ngrok allows you to export logs to AWS, Azure, and Datadog. While this guide focuses on exporting to Datadog, we also have quickstarts for [AWS CloudWatch](/docs/integrations/event-destinations/amazon-cloudwatch-event-destination), [AWS Firehose](/docs/integrations/event-destinations/amazon-firehose-event-destination), [AWS Kinesis](/docs/integrations/event-destinations/amazon-kinesis-event-destination), and [Azure Logs](/docs/integrations/event-destinations/azure-logs-event-destination). Check out the [pricing page](https://ngrok.com/pricing) for information about pricing and availability of log exporting. ## Observe API traffic with Traffic Inspector In this section, you'll learn the basics of the Traffic Inspector and work through an example that uses it for rate limiting. * Browse to the ngrok [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) on your ngrok account dashboard. The list of recent requests provides you with basic information such as the time, origin, destination, duration, and response code of calls to your API. ngrok Traffic Inspector showing a list of captured HTTP requests with status codes and response times * You can filter requests by these fields—for instance, to show only server error responses and not successes. To see more details about a request or to replay it, you need to enable full capture, which permits ngrok to store up to 10 KB of your request data. * Click a request in the Traffic Inspector list, then click **Enable full capture** in the sidebar. ngrok Traffic Inspector with the Enable Full Capture toggle highlighted * This button takes you to your account settings, where you can enable full capture under **Observability**. ngrok account settings page with the Enable Full Capture option enabled * Then, return to your published API URL and refresh the browser page a few times to send fresh requests through ngrok. * In the ngrok Traffic Inspector, click the event at the top of the list. The event should now show all the request and response details and content. If the full capture details don't appear, you may need to restart your ngrok agent in the terminal. * Click the **Replay** button and notice that the request is replayed from the ngrok host. ngrok Traffic Inspector replay button for resending a captured HTTP request * You can also replay a request with changes to alter any of the headers or POST data. ngrok Traffic Inspector replay editor with modified request headers Replaying requests is useful for debugging. For example, you could find the request that caused an error in your API, deploy a fix for the API, and replay the request to confirm you've fixed the issue. ### Observe and debug Traffic Policy rules with request replay You can also replay requests to test new [Traffic Policy](/docs/traffic-policy) rules. ngrok applies the Traffic Policy rules in effect at the current time of replay, which may be different from the rules that were in effect when the original request was received. This replay function lets you test how new Traffic Policy rules behave against real-world traffic from the past, offering one more way to test or debug them. Since you came here from a previous API gateway guide, you've already seen how to set up the [`rate-limit` action](/docs/gateway/traffic-policy/actions/rate-limit). Now you can observe what happens to rate-limited requests and also use Traffic Inspector to dial in your threshold. * Hit your endpoint a few more times with a loop of the `curl` command: ```bash mode="cli" theme={null} for i in `seq 1 50`; do curl -X GET -w '%{http_code}' https://$YOUR_DOMAIN/abc ; done`. ``` * In Traffic Inspector, note that ngrok returned the error code `429` (rate limit) and that the duration of the request was instant, meaning it created no load on your API. ngrok Traffic Policy rate limit configuration using a sliding window algorithm on POST and PUT requests * Edit the `policy.yml` file and change `capacity` to `100` or another threshold you deem appropriate and restart your agent based on your form factor. * In the Traffic Inspector, click one of the `429` events, then click **Replay**. Note that the request now responds without error because you increased the rate limit for the given IP address of that request. ngrok Traffic Inspector replaying a request after adjusting the rate limit policy ## Monitor your API gateway traffic with Datadog In this section, you'll learn how to export ngrok events to the Datadog monitoring service. There are two [types of events](/docs/obs/events/reference): standard traffic events (requests to your API) and audit events (changes to secret keys and URLs). For this simple example, you'll monitor a traffic event. * In the ngrok dashboard, browse to [**Log Exporting**](https://dashboard.ngrok.com/log-exporting) and add a new Log Export. ngrok dashboard event subscription creation form with destination and description fields * In the **New Log Export** sidebar, enter `traffic` as the **Description** and then **Add** a new log source. Choose `http_request_complete`. ngrok event subscription form adding an HTTP request log source type * Add Datadog as a destination, add the Datadog site and API key you noted earlier, and send a test log. * Open the Datadog site and browse to the **Log Explorer** page from the navigation panel. Enable logs. You should see that the log from ngrok has appeared. Datadog Log Explorer showing incoming ngrok HTTP request events * In ngrok, click **Done** and **Save**. * Hit your API services a few times so that ngrok has sent new logs over to Datadog. ### Create a dashboard Now that events are being sent to Datadog, you can set up visualizations and notifications to allow your support team to monitor your API's performance. * In the Datadog site, browse to the **Dashboards > Dashboard List** page from the navigation panel. Click the **ngrok HTTP Events** item to view the default dashboard. Datadog dashboard list showing available monitoring dashboards You need to clone the default dashboard to add a new widget to the dashboard. * Click **Clone** at the top right of the page. Datadog default ngrok monitoring dashboard with request metrics and status code graphs * In the cloned dashboard, click **Add widgets**. Datadog dashboard in edit mode with the add widget panel open Configure the widget as follows. * 1. Choose **Query value** as your visualization. * 2. Change **Metrics** to **Logs** (ngrok does not export metrics) and enter the log filter, `@http.status_code:500`, so that Datadog only counts errors. * 3. Set the time preference to **Past 1 Hour**. * 4. Name the widget `Errors in the last hour`. Datadog widget editor showing query configuration for graphing ngrok request metrics Your new widget will be available in the dashboard, allowing your support staff to see instantly whether any errors have occurred in your API. Datadog custom dashboard displaying ngrok request rates and error metrics Since the sample API never returns errors, an easy way to test the `Errors in the last hour` widget is to stop the sample API Docker container and then try to browse to the site on the public ngrok endpoint. If you want to create widgets for other log information, you can see which fields are available by reading the JSON of any event you click in the log inspector. ### Create a notification To complete your monitoring system, you need to set up an alert pushed to your email or mobile app when an error occurs by adding a webhook integration to Datadog. This allows your support team to receive error notifications instead of repeatedly checking the dashboard. You can set up webhooks with the free notification service [ntfy.sh](https://ntfy.sh) as a quick demonstration. For production workloads, consider using email, Slack, mobile push notifications, or incident management tools like PagerDuty. * In Datadog, browse to **Integrations > Add integration > Webhooks**. Datadog webhook integration settings page for forwarding ngrok events * Under **Webhooks**, at the bottom of the configuration page, click **New**. Set the **Name** to `ntfy`, the **URL** to `https://ntfy.sh/$YOUR_COMPANY`, and the **Payload** to **Blank**. Click **Save**. Datadog webhook configuration form with URL and name fields for ngrok event delivery * In the Datadog navigation panel, browse to the **Monitors > New monitor**. Choose **Logs**. * In the monitor configuration, choose **Query** for your monitor scope, set the search query to `@http.status_code:500`, the time to the **last 1 hour**, and the notification message: ``` @webhook-ntfy Your API has errors. Investigate at https://app.datadoghq.eu/dashboard. ``` Datadog Logs monitor configuration with an HTTP 500 status code query and notification message It's important that the `@webhook` name matches the webhook integration you created earlier. If the names don't match, the notification won't arrive. * Browse to the [ngrok test channel on the ntfy website](https://ntfy.sh/ngrokTest). * Click **Test notifications** at the bottom of the Datadog page and send an alert with **Run tests**. * Notice that the alert appears on the ntfy page. You can install ntfy as a mobile app so that you are always aware of whether your API has errors. Datadog monitor notification settings showing a webhook notification channel * Save the monitor. Now, after a delay and once you've refreshed the ngrok endpoint for your API, you will receive a notification if an error occurs. ## What should you monitor at your API gateway? Now that you've seen how to connect your API traffic to Datadog, explore logs, create a dashboard with custom widgets, and send alerts, consider what exactly you should be monitoring. There are three categories to consider: security, errors, and performance. ### Security You need to monitor the security of your infrastructure and any attempts to hack users' accounts. As mentioned earlier, ngrok emits [audit logs](/docs/obs/events/reference/#audit-events) for any security changes. You should set up alerts for any changes to API keys, domains, and SSH certificates—anything that indicates attackers might gain access to your infrastructure. Events like stopping and starting agents and endpoints are likely to happen frequently, so they don't need alerts (especially when running Kubernetes and multiple containers). You still might want to add widgets to monitor these events on a dashboard, however, because surges in activity might indicate problems with your system Additionally, you can add monitors that trigger alerts when activity passes a certain threshold. In terms of user security, you need to monitor anything that indicates attackers might be accessing a user's account. This includes multiple requests from IP addresses in different countries in a short time span (though this has become common with the increasing use of VPNs), a high rate of failed login attempts, or an anomalous pattern of request access. To make a monitoring rule that recognizes anomalies, you first need to track the average number of requests the average user makes to various endpoints in your API. ### Errors Errors are the simplest category to monitor. Generally, if your API returns status codes in the `500` range instead of the `200` range, you have some debugging and remediation to attend to. Using Datadog monitoring, create an alert to send notifications to ntfy or Slack for all errors. If a `500` error occurs, use the ngrok replay function to see whether it can be reproduced on a QA server, and send the bug to your developers to fix it. ### Performance Performance metrics need monitoring but don't need to trigger alerts unless your service experiences a sudden spike in usage or growth. The most important things to monitor are uptime, latency (response time), and throughput. Unlike with audit events, there is only one traffic event to monitor: [HTTP Request Complete](/docs/obs/events/reference/#http-request-complete). This event, with multiple fields and the request and response bodies in the log, is enough to construct any metrics you need. General statistics practices apply to metric monitoring. Use medians instead of arithmetic averages, because extreme values disproportionately affect averages. For latency, keep an eye on the median duration of a request and outliers—separate requests by endpoint to identify which endpoints take excessive time and could be improved. For throughput, look at the median number of requests per minute. For both metrics, also consider monitoring variance in addition to averages. Even if an endpoint has a quick average response time, if the duration is highly variable, with many lower- and higher-than-average durations, your users will probably get annoyed. ## What's next? By the end of this guide, you've set up ngrok as an API gateway with the agent or Kubernetes Operator and established fundamentals for observing it as a holistic system. You're ready for production, but you still haven't reached the end of what's possible with Traffic Policy—explore the [Traffic Policy examples](/docs/gateway/traffic-policy/examples) for even more ways you can filter, manage, and orchestrate traffic. Looking for even more resilience? [Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling) offers a dead-simple path to load balancing across environments, clusters, and clouds. # Deploy a multicloud API gateway Source: https://ngrok.com/docs/gateway/api-gateway/multicloud In this tutorial, you'll learn how to load balance API services across multiple clouds and apply common multicloud traffic patterns. This guide walks you through a practical example of using the ngrok as an API gateway to securely expose and manage APIs running in Kubernetes clusters that span multiple cloud providers. In this scenario, you'll see how to unify access to services deployed in different environments, simplify connectivity, and enforce consistent security policies—all without the complexity of managing separate ingress solutions in each cloud. ## Example scenario You run Acme Corp, which has long operated its core infrastructure in AWS, where a Kubernetes cluster hosts a suite of APIs critical to your customers. Recently, Acme acquired a subsidiary (Betatech) that built its own platform in GCP, also relying on Kubernetes to power customer-facing services. Suddenly, Acme finds itself managing APIs in two separate clouds, each with its own networking stack, security controls, and exposure requirements. In EKS, Acme runs a Catalog API, which provides product listings to customers, and an Orders API, which partners and users rely on to place and manage orders. In GKE, Betatech maintains an Inventory API, exposing stock levels and reservation capabilities, along with a Webhook Receiver API, which ingests real-time events from third-party systems. You need a way to expose these APIs to the public internet without the complexity of juggling multiple cloud-specific solutions. This is where ngrok's API gateway comes into play with one secure and consistent entry point for all of your distributed services. ## Architectural reference Architecture diagram showing ngrok acting as a multicloud API gateway routing traffic from the public internet through a Traffic Policy layer to services running in AWS EKS and Google Cloud GKE clusters ### What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * Two K8s clusters running on separate VMs (ideally each within a distinct cloud provider). * Not running kubernetes? Try [installing the ngrok agent](/docs/gateway/endpoints/agent-cli-quickstart/) in your cloud infrastructure directly. * [An ngrok API Key](https://dashboard.ngrok.com/api-keys/new). You'll need an account first. ## 1. Install the ngrok operator within each K8s cluster The [ngrok Kubernetes Operator](https://ngrok.com/docs/k8s/index#kubernetes-operator) lets you declaratively expose cluster Services through ngrok by reconciling simple manifests into managed, secure endpoints. Before you install the operator, save your ngrok authtoken and API key as environment variables. To install the operator, run this command from inside each of your Kubernetes clusters that host your backend APIs: ```bash theme={null} helm install ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --create-namespace \ --set description="acme ngrok operator" \ # set this to "beta ngrok operator" for the other cluster --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN ``` ## 2. Create an **internal** endpoint for each API Use the [`AgentEndpoint` CRD](https://ngrok.com/docs/k8s/crds/agentendpoint) to register an **internal** endpoint per API in each cluster. Internal endpoints are not internet-reachable; they're only targetable from other ngrok endpoints via the `forward-internal` Traffic Policy action—perfect for wiring a single public gateway to multiple cluster services. To make an endpoint internal, set `spec.bindings: ["internal"]` **and** end `spec.url` with `.internal`. ```yaml title="agentendpoints-eks.yaml" theme={null} # Orders API + Catalog API apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: catalog-internal namespace: default spec: bindings: - internal url: https://catalog-eks.internal upstream: url: http://catalog-svc.default:80 --- apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: orders-internal namespace: default spec: bindings: - internal url: https://orders-eks.internal upstream: url: http://orders-svc.default:8443 ``` ```yaml title="agentendpoints-gke.yaml" theme={null} # agentendpoints-gke.yaml: Inventory API + Webhook API apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: inventory-internal namespace: default spec: bindings: - internal url: https://inventory-gke.internal upstream: url: http://inventory-svc.default:80 --- apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: webhooks-internal namespace: default spec: bindings: - internal url: https://webhook-gke.internal upstream: url: http://webhook-svc.default:9000 ``` Now, apply each YAML config in each cluster: ```bash theme={null} kubectl apply -f agentendpoints-eks.yaml --context eks kubectl apply -f agentendpoints-gke.yaml --context gke ``` ## 3. Create a public Cloud Endpoint and attach Traffic Policy Cloud Endpoints are persistent, always-on endpoints you manage centrally via the [dashboard](https://dashboard.ngrok.com/) or [API](https://ngrok.com/docs/api). Cloud Endpoints don't forward their traffic to an agent by default and instead only use their attached Traffic Policy to handle connections. In this case, this single Cloud Endpoint will act as the gateway to receive incoming requests and route them to the correct internal endpoint based on a header (`X-Acme-Api`) sent with the request. Create a public Cloud Endpoint in the ngrok dashboard by navigating to endpoints and clicking **new** as shown in the screenshot below: ngrok dashboard New Cloud Endpoint dialog with Public binding selected and a custom domain URL Now that you've created the Cloud Endpoint, you need to configure it to route to the correct internal endpoint based on the request header. Click on your Cloud Endpoint and replace the default Traffic Policy with: ```yaml theme={null} on_http_request: actions: - type: forward-internal config: url: https://${getReqHeader('X-Acme-Api')[0]}.internal ``` This Traffic Policy will read the value of the request header for `X-Acme-Api` and route it to that corresponding internal endpoint. For example, a request sent with the header `X-Acme-Api: catalog-eks` will route to `https://catalog-eks.internal`, which is configured to point to the Catalog API in your EKS cluster. ## 4. Attach Traffic Policy to authenticate your Cloud Endpoint Traffic Policy is ngrok's configuration language for endpoints. In this example, the Traffic Policy for your Cloud Endpoint handles routing rules (seen in step 3), rate limiting, authentication, and firewall rules. A full list of available Traffic Policy actions can be seen [here](https://ngrok.com/docs/traffic-policy/actions/). Use these actions by themselves or layered on top of each other (executed sequentially from top to bottom). Here's how your Traffic Policy YAML might look for your Cloud Endpoint: ```yaml theme={null} on_http_request: actions: # apply rate limiting - type: rate-limit config: name: Only allow 10 requests per second algorithm: sliding_window capacity: 10 rate: 1s bucket_key: - "conn.client_ip" # apply OWASP Core Rule Set on request - type: owasp-crs-request config: on_error: halt # Block specific AI Bots and IPs on blocklist categorizations with IP Intel - expressions: - ("com.anthropic" in conn.client_ip.categories) || ("com.openai" in conn.client_ip.categories) || ("com.perplexity" in conn.client_ip.categories) || ('tor' in conn.client_ip.categories) || ('tor.exit' in conn.client_ip.categories) || ('proxy' in conn.client_ip.categories) || ('proxy.anonymous' in conn.client_ip.categories) || ('vpn' in conn.client_ip.categories) actions: - type: deny config: status_code: 404 # forward traffic to internal endpoint based on header - type: forward-internal config: url: https://${getReqHeader('X-Acme-Api')[0]}.internal on_http_response: actions: # apply OWASP Core Rule Set on response - type: owasp-crs-response config: on_error: halt ``` ## 5. Load balance with endpoint pooling Load balancing at ngrok is done with [endpoint pooling](https://ngrok.com/docs/endpoints/endpoint-pooling/). When multiple ngrok endpoints are created with the same URL and with the *pooling\_enabled* flag, ngrok automatically balances traffic across them. For AgentEndpoint resources, pooling is always on: if more than one agent creates the same internal URL, requests are balanced across them. Nothing changes in your Cloud Endpoint config—and ngrok now spreads requests across the pooled internal endpoints. ## Recap You've successfully unified your APIs across EKS and GKE. A single public ngrok Cloud Endpoint points to 2 pools of internal endpoints, and Traffic Policy is handling routing, authorization, and security. To recap: * Installed the ngrok Kubernetes Operator in both clusters (EKS and GKE) * Created internal AgentEndpoints for Catalog, Orders, Inventory, and Webhooks using AgentEndpoint CRDs. * Deployed one public Cloud Endpoint and routed by request headers to the correct internal backends with per-API security (rate limits, IP allowlists, OWASP). * Achieved load balancing & resilience via endpoint pooling by running multiple agent replicas. # Configure Secure Access to Remote IoT Devices Source: https://ngrok.com/docs/gateway/device-gateway/agent Learn how to use ngrok to remotely access IoT devices and services. Connect remotely without VPN or SSH. ngrok's Gateway allows you to connect to any app, IoT device, or service without networking expertise. This guide will walk you through an example scenario using ngrok to set up a secure, controlled remote access solution for IoT devices. The solution will enable you to grant trusted parties access to critical systems without exposing those systems to the public internet or relying on complex VPN setups. ## What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * An ngrok agent configured on your local machine. See the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/) for instructions on how to install the ngrok agent. * [An ngrok API Key](https://dashboard.ngrok.com/api-keys/new). You'll need an account first. ## Example scenario Consider a situation where a network of smart factories is coming online, each with IoT-connected machines, telemetry sensors, and a real-time monitoring dashboard. In this scenario, each factory's network blocks inbound connections, but the technicians need temporary access to the dashboard. The telemetry API and sensor database must remain permanently accessible from the company's cloud, and access to the dashboard must be authenticated via Microsoft Entra ID. Each factory would only need one ngrok agent running. ### Why only one ngrok Agent per factory? Traditionally, you might assume that every device inside the factory needs its own ngrok agent, but this isn't necessary. A single ngrok agent is installed on a network-accessible machine inside the factory, and it: 1. Acts as a central gateway (jumpbox) that can reach any machine on the local network, eliminating the need for multiple agents. 2. Creates Internal Endpoints so that each API, database, and dashboard is securely exposed inside ngrok, never publicly visible. 3. Uses Cloud Endpoints for controlled access, where external cloud apps can access only what they need, and the dashboard is only started when requested. 4. Runs as a background service configured to automatically start on boot, restart after crashes, and log events. 5. Dynamically manages tunnels, as The agent API can start and stop tunnels as needed. This setup minimizes security risks, simplifies deployment, and ensures continuous uptime for mission-critical services. ## Understanding cloud and internal endpoints An [internal endpoint](/docs/gateway/endpoints/internal-endpoints/) enables a service inside the factory network to be reachable within ngrok, without being publicly exposed. They can: 1. Only receive traffic from Cloud Endpoints or internal services that explicitly route traffic to them. 2. Not be accessed directly from the internet. 3. Be used for telemetry APIs, databases, and dashboards. Here's an example: The factory's telemetry API runs on a local server (192.168.1.100:8080). Instead of exposing it publicly, you can create an internal endpoint: ```yaml theme={null} version: 3 agent: authtoken: YOUR_NGROK_AUTHTOKEN endpoints: - name: example url: https://api.internal upstream: url: http://192.168.1.100:8080 ``` Now this API is only accessible inside ngrok's private network. A [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/) is a permanent, externally accessible entry point into the factory network that's also: 1. Managed centrally via the ngrok API or dashboard. 2. Always on, not tied to the lifecycle of the agent. 3. Does not forward traffic to the agent by default—it must be configured to route traffic to internal endpoints. 4. Used for exposing services to external cloud apps securely. For example, the factory's telemetry API is accessible via [https://factory.example.com/api](https://factory.example.com/api), but instead of exposing the API directly, a Cloud Endpoint forwards traffic to its internal endpoint: ```yaml theme={null} on_http_request: - expressions: - req.url.path.startsWith("/api") actions: - type: forward-internal config: url: https://api.internal ``` ## Define internal endpoints in ngrok.yaml After installing the ngrok agent, define all required internal endpoints inside the ngrok configuration file, which is at `/etc/ngrok.yml` on Linux or `C:\ngrok\ngrok.yml` on Windows. ```yaml theme={null} version: 3 agent: authtoken: YOUR_NGROK_AUTHTOKEN endpoints: - name: telemetry-api url: https://telemetry-api.internal:8080 upstream: url: http://192.168.1.100 # API inside factory network - name: sensor-db url: tcp://database.internal:5432 upstream: url: tcp://192.168.1.101 # PostgreSQL database - name: factory-dashboard url: https://dashboard.internal upstream: url: http://192.168.1.102:3001 # Web dashboard - name: agent-api url: https://agent-api.internal upstream: url: http://localhost:4040 # Expose Agent API internally ``` ## Install ngrok as a background service Install and start the service: ```bash theme={null} ngrok service install --config /etc/ngrok.yml ngrok service start ``` In most cases, installing ngrok as a service requires administrator privileges. This will start all tunnels defined in the configuration file, ensure ngrok runs persistently in the background, and integrate with native OS service tooling. ## Reserve a TCP address for your TCP-based Cloud Endpoint When you reserve a TCP Address, you can create a TCP Cloud Endpoint that binds to that domain. Reserved TCP addresses are available on ngrok's pay-as-you-go plan. ```curl theme={null} curl -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"description":"SQLdb Address", "region":"us"}'\ https://api.ngrok.com/reserved_addrs ``` ## Reserve a domain for wildcard endpoints Reserving a wildcard domain will allow you to create wildcard endpoints and receive traffic on any subdomain of your domain. Wildcard endpoints are available on ngrok pay-as-you-go plans. For billing information, see [wildcard endpoints pricing](/docs/pricing-limits/#wildcard-endpoints). It can be helpful to create a separate subdomain for each factory you wish to connect to. ```curl theme={null} curl \ -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"domain":"*.api.acme.com","region":"us"}' \ https://api.ngrok.com/reserved_domains ``` ## Create a Service User and authtoken for the factory Create a Service User so that you can create an agent authtoken independent of any user account. A Service User represents a service account, and allows each customer network to have its own authtoken. In the case one authtoken is compromised, only that customer network may be affected rather than all of them. ```curl theme={null} curl \ -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"name":"Service User for factory 1"}' \ https://api.ngrok.com/service_users ``` Navigate to the [Authtokens](https://dashboard.ngrok.com/authtokens) section of your ngrok dashboard and create an authtoken for that Service User and assign it an ACL binding, so that it can only create endpoints bound to the reserved domain for that factory's telemetry API. ## Create Cloud Endpoints for always-on API and database access Since the telemetry API and database must be always accessible, create permanent Cloud Endpoints that route traffic to their internal endpoints. Create an HTTPS Cloud Endpoint for the API using the ngrok platform API. ```curl theme={null} curl -X POST https://api.ngrok.com/endpoints \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{ "type": "cloud", "url": "https://factory1.api.acme.com", "traffic_policy": "{ \"on_http_request\": [ { \"expressions\": [\"req.url.path.startsWith(\\\"/api\\\")\"], \"actions\": [ { \"type\": \"forward-internal\", \"config\": { \"url\": \"https://telemetry-api.internal\" } } ] } ] }" }' ``` Now factory1.api.acme.com/api permanently forwards traffic to api.internal inside the factory. Next, create a TCP Cloud Endpoint for the sensor database because it's on a TCP-based internal endpoint. ```curl theme={null} curl -X POST https://api.ngrok.com/endpoints \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{ "url": "YOUR_RESERVED_ADDR", "type": "cloud", "bindings": ["public"], "traffic_policy": "on_tcp_connect:\n - actions:\n - type: forward-internal\n config:\n url: \"tcp://database.internal:5432\"\n - type: restrict-ips\n config:\n enforce: true\n allow:\n - 203.0.113.0/24\n deny:\n - 192.0.2.0/24" }' ``` Now, your reserved TCP address forwards TCP connections to the factory database and only allows access from `203.0.113.0/24` and denies traffic from `192.0.2.0/24`. ## Enable on-demand web dashboard access Since the web dashboard should only be online when needed, use ngrok's agent API to dynamically start and stop tunnels. As a technician, start the tunnel by running: ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "name": "dashboard", "proto": "http", "addr": "3001", "domain": "app.factory.com" }' \ https://agent.factory1.com/api/tunnel ``` Ensure your chosen domain has the proper TLS/SSL certs provisioned to create a secure, working connection. Now, `app.factory.com` is live—only for this session. ## Secure API access with Google OAuth and mTLS with Traffic Policy Navigate to your newly created telemetry API Cloud Endpoint in the [endpoints](https://dashboard.ngrok.com/authtokens) tab on the dashboard, and apply a [Traffic Policy](/docs/gateway/traffic-policy/). Make sure you have a certificate on hand or generate them using the instructions in the [terminate-tls](/docs/gateway/traffic-policy/actions/terminate-tls/#generate-certificates) docs. ```yaml theme={null} on_tcp_connect: - actions: - type: terminate-tls config: mutual_tls_certificate_authorities: - |- -----BEGIN CERTIFICATE----- ... certificate ... -----END CERTIFICATE----- on_http_request: - actions: - type: oauth config: provider: google - type: forward-internal config: url: https://telemetry.api.internal:443 ``` Now, only Google authenticated users can access the web dashboard and the connection is secured with mTLS. ## Welcome to your secure, connected, and automated factory You have now integrated a system that allows you to seamlessly and securely access any and all remote services within your enterprise. To recap what you've built: 1. One ngrok agent per factory and no need for multiple installs. 2. Always-online API and dashboard, securely available via Cloud Endpoints. 3. A web dashboard that spins up on-demand with authentication via Google OAuth and mTLS security. 4. An Agent API that dynamically manages tunnels with automatic provisioning and deprovisioning. 5. ngrok runs as a background service, which means it's reliable and will always auto-restart. # Install ngrok on Linux (ARM64) Source: https://ngrok.com/docs/gateway/device-gateway/arm64 Learn how to install ngrok on any Linux ARM64 device to tunnel traffic to your upstream services or remotely administer it. In this guide, you'll learn how to install ngrok on any Linux ARM64 device to forward traffic from public endpoints to your upstream services or for remote management. You'll also create some a Traffic Policy to protect your device from unauthorized traffic. This guide is specific to ARM64 devices on Linux—if you're using a different device or CPU architecture, first check whether your platform meets the ngrok agent's [system and resource requirements](/docs/gateway/agent/install#system-requirements). Other guides are also available that may fit your use case more precisely: * [Linux AMD64 devices](/docs/gateway/device-gateway/linux/) * [Raspberry Pi](/docs/gateway/device-gateway/raspberry-pi/) * [Raspberry Pi OS](/docs/gateway/device-gateway/raspbian/) ## Prerequisites To follow this guide, you need: * An [ngrok account](https://dashboard.ngrok.com/signup) (some features require a [pay-as-you-go plan](https://ngrok.com/pricing)) * Any ARM64 device running Linux (see the [system requirements](/docs/gateway/agent/install#system-requirements) for all supported architectures) ## 1. Install the ngrok Agent 1. Open a shell session with your remote ARM64 device. 2. Download the latest ngrok agent binary for ARM64 devices. ```bash theme={null} wget https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-arm64.tgz ``` Equinox is fully owned by ngrok and used exclusively for building and distributing ngrok binaries. See [the FAQ page](/docs/faq#does-ngrok-own-bin-equinox-io) for more details. 3. Unzip the file and move it to a directory in your `PATH`, like `/usr/local/bin`. ```bash theme={null} sudo tar xvzf ./ngrok-v3-stable-linux-arm64.tgz -C /usr/local/bin ``` 4. Link the ngrok agent to your ngrok account with your authtoken, replacing `{NGROK_AUTHOKEN}` with the value found in your [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). ```bash theme={null} ngrok authtoken {NGROK_AUTHTOKEN} ``` ## 2. Enable SSH access If you want to perform remote administration on your ARM64 device using a reserved TCP address and an encrypted tunnel, instead of relying on IP addresses, you can create a [TCP tunnel](/docs/gateway/endpoints/tcp/) at port `22` on your ARM64 device. 1. Start the TCP tunnel with `ngrok`. ::::warning TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. If you get a different error, ensure your authtoken is configured correctly. :::: ```bash theme={null} ngrok tcp 22 ``` When the ngrok agent starts, you'll see a `Forwarding` line with similar information about the public endpoint for the TCP tunnel to your device: ```bash theme={null} Forwarding tcp://4.tcp.us-cal-1.ngrok.io:16420 -> localhost:22 ``` 2. On your local workstation, SSH into your ARM64 device with the command below, replacing the variables with the following: | | | | - | - | | `{NGROK_PORT}` | The port number assigned to your ngrok agent (for example, if the agent shows `tcp://4.tcp.us-cal-1.ngrok.io:12345`, your port number is `12345`). | | `{USER}` | An existing user on your remote device. | | `{NGROK_TCP_ADDRESS}` | The address of the ngrok agent (for example, if the agent shows `tcp://4.tcp.us-cal-1.ngrok.io:12345`, your TCP address is `4.tcp.us-cal-1.ngrok.io`). | ```bash theme={null} ssh -p {NGROK_PORT} {USER}@{NGROK_TCP_ADDRESS} ``` ## 3. Enable ingress to a service on your ARM64-based Linux device Separately from SSH access, you can also use ngrok to create an [HTTP tunnel](/docs/gateway/endpoints/http/) to route traffic to specific applications or services running on your ARM64 device's `localhost` network. If you already established a TCP tunnel for SSH access, you'll either need to create a second tunnel for the HTTP tunnel, or use the [agent configuration file](/docs/gateway/agent/config/v3/) to define multiple tunnels. 1. Use ngrok to create an HTTP tunnel at the port on which your service operates, for example, port `8080`. ```bash theme={null} ngrok http 8080 ``` When the ngrok agent starts, you'll see a `Forwarding` line with similar information about the public endpoint for the HTTP tunnel to your device: ```bash theme={null} Forwarding https://12345.ngrok.app -> http://localhost:8080 ``` 2. Optionally, you can reserve a [static subdomain](/docs/gateway/domains/) like so: ``` ngrok http 8080 --url https://example.ngrok.app ``` 3. Open a browser and navigate to the endpoint, for example, `https://12345.ngrok.app`, to access the service from any remote system. ## 4. Add a Traffic Policy to restrict IPs Now that you have SSH tunneling and service ingress handled via ngrok, you may also want to protect those services from unknown and untrusted users. You'll use the [Restrict IPs](/docs/gateway/traffic-policy/actions/restrict-ips) Traffic Policy action. 1. Create a new file on your ARM64 device, where you create ngrok endpoints, named `policy.yml`. 2. Open the file for editing and paste in the following contents, replacing `1.1.1.1` with the public IP of the workstation you use to access your ARM64 device: ```yaml theme={null} on_tcp_connect: - actions: - name: "Restrict all IPs except trusted CIDRs" type: "restrict-ips" config: enforce: true allow: - "1.1.1.1/32" ``` :::note ngrok's IP Restriction action uses [CIDRs](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) to specific IPs or IP ranges. The `/32` notation refers to a single IPv4 address, like \` ::: 3. Restart any existing tunnels, or create new ones, referencing the `policy.yml` file you just created, choosing between a TLS and HTTP tunnel below. ```bash theme={null} ngrok tcp 22 --traffic-policy-file /path/to/policy.yml ``` ```bash theme={null} ngrok http 8080 --traffic-policy-file /path/to/policy.yml ``` 4. When you re-establish your TLS or HTTP tunnels, ngrok will proxy requests from your allowed IP/CIDR through to your ARM64 device and reject all others *at its cloud service*, preventing your device from being constantly bombarded with automated and malicious attacks. ## What's next? Now that you can create ngrok endpoints on your Linux ARM64 device and understand the fundamentals of managing traffic with policies, you can extend your usage to make your device—or even a fleet of them—ready for production usage. * Bring a [custom domain](/docs/gateway/domains/custom-domains/) to ngrok to create static endpoints. * Learn how to write an [agent configuration file](/docs/gateway/agent/config/v3/) to define and create multiple tunnels from a single command line. * Install [ngrok as a service](/docs/gateway/agent/service#running-ngrok-in-the-background) to start after your ARM64 device boots and automatically restart after crashes. # Configure Remote Connectivity to your IoT Devices in the Field Source: https://ngrok.com/docs/gateway/device-gateway/cloud-to-device Use ngrok to remotely access IoT devices and services in the field without a VPN. ngrok's Gateway allows you to connect to any app, IoT device, or service without networking expertise. This guide walks you through an example scenario using ngrok to set up a secure, controlled remote access solution for IoT devices. The solution enables you to grant trusted parties access to critical systems without exposing those systems to the public internet or relying on complex VPN setups. ## Example scenario Consider a situation where a network of smart factories is coming online, each with IoT-connected controllers, security cameras, and machine sensors. In this scenario, each factory's network blocks inbound connections. However, your technicians need to do remote maintenance on these cameras via SSH, and your SaaS platform's cloud software needs access to device data via a web app for mission-critical operations. ## Architectural reference Architecture diagram: one ngrok agent per factory routing cloud traffic to internal devices ### Why only one ngrok agent per factory? Traditionally, you might assume that every device inside the factory needs its own ngrok agent, but this isn't necessary. A single ngrok agent is installed on a network-accessible machine inside the factory. The agent acts as a central gateway (jumpbox) that can reach any machine on the local network, eliminating the need for multiple agents. ## What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * An ngrok agent configured in a remote IoT-based network. See the [getting started guide](https://ngrok.com/docs/getting-started) for instructions on how to install the ngrok agent. ## 1. Create a service user and authtoken for isolated network access First, create a service user and an associated authtoken for each of your factories. A service user is intended for automated systems that programmatically interact with your ngrok account (other platforms sometimes call this concept a Service Account). Create a separate service user and associated authtoken for each of your factories so that: * Their usage of your ngrok account is isolated and scoped with a specific permission set * If an agent is compromised, you can revoke its access independently * Agent start/stop audit events are properly attributed to each customer * Your ngrok agents don't stop working if the human user who set them up leaves your ngrok account Navigate to the [Service Users](https://dashboard.ngrok.com/service-users) section of your dashboard and click **New Service User**. Service Users page with New Service User button Next, create an authtoken assigned to this specific service user. Authtokens page showing a new authtoken assigned to a service user ## 2. Install the ngrok agent within your remote network and configure internal Agent Endpoints in ngrok.yml Configure the agent to create internal Agent Endpoints that point to the devices you want to remotely access. This connects the devices to your ngrok account, but nothing can connect to them until you complete the subsequent steps. The configuration to achieve this is shown in the example agent configuration file below. Internal Agent Endpoints are private endpoints that only receive traffic when forwarded through the [forward-internal Traffic Policy action](https://ngrok.com/docs/traffic-policy/actions/forward-internal). This allows you to route traffic to an application through ngrok without making it publicly addressable. Internal Agent Endpoint URL hostnames must end with `.internal`. After installing the ngrok agent, define internal Agent Endpoints for each service you want to remotely access inside the ngrok configuration file. You can install ngrok and its configuration file at `/path/to/ngrok/ngrok.yml` and the executable at `/path/to/ngrok/ngrok`. ```yaml theme={null} version: 3 agent: authtoken: AUTHTOKEN_CREATED_IN_STEP_1 endpoints: - name: Internal Endpoint for Controller url: https://controller.internal upstream: url: 8080 - name: Internal Endpoint for Sensor url: https://sensor.internal upstream: url: 8081 - name: Internal Endpoint for Security Camera url: tcp://camera.internal:22 upstream: url: 22 ``` ## 3. Reserve a TCP address for security camera connectivity You need to reserve a TCP address to create a TCP Cloud Endpoint, which you'll do in the next step. Reserving the address holds it exclusively for your ngrok account. Reserved TCP addresses page with a newly reserved address ## 4. Create your Cloud Endpoints and attach a Traffic Policy Cloud Endpoints are persistent, always-on endpoints whose creation, deletion, and configuration is managed centrally via the dashboard or API. They exist permanently until explicitly deleted. Cloud Endpoints don't forward their traffic to an agent by default—they only use their attached Traffic Policy to handle connections. Create a public Cloud Endpoint in the ngrok dashboard by navigating to **Endpoints** and clicking **New**. Endpoints page with New button to create a Cloud Endpoint Additionally, create Cloud Endpoints for the sensor and camera: `https://sensor.acme.com` and `tcp://1.tcp.ngrok.io:12345` (using your reserved TCP address). Click on your newly created controller Cloud Endpoint and replace the default Traffic Policy with: ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://controller.internal ``` Click on your newly created sensor Cloud Endpoint and replace the default Traffic Policy with: ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://sensor.internal ``` Click on your newly created camera Cloud Endpoint and replace the default Traffic Policy with: ```yaml theme={null} on_tcp_connect: - actions: - type: forward-internal config: url: tcp://camera.internal:22 ``` ## 5. Secure your Cloud Endpoints with OAuth, TLS version control, and IP restrictions Navigate to your newly created Cloud Endpoints in the [Endpoints](https://dashboard.ngrok.com/endpoints) tab on your ngrok dashboard, and apply a Traffic Policy action to each. For this example, apply OAuth to your controller endpoint, enforce a minimum TLS version for your sensor endpoint, and enable IP restrictions for your camera endpoint. Browse the full list of [Traffic Policy actions](https://ngrok.com/docs/traffic-policy/actions). You can add each action directly to the Cloud Endpoint's YAML configuration. The final Traffic Policy config for each endpoint is shown below. `https://controller.acme.com` ```yaml theme={null} on_http_request: - actions: - type: oauth config: provider: google client_id: '{your app's oauth client id}' client_secret: '{your app's oauth client secret}' scopes: - https://www.googleapis.com/auth/userinfo.email - actions: - type: forward-internal config: url: https://controller.internal ``` `https://sensor.acme.com` ```yaml theme={null} on_tcp_connect: - actions: - type: terminate-tls config: min_version: '1.3' on_http_request: - actions: - type: forward-internal config: url: https://sensor.internal ``` `tcp://1.tcp.ngrok.io:12345` ```yaml theme={null} on_tcp_connect: - actions: - type: restrict-ips config: allow: - - actions: - type: forward-internal config: url: tcp://camera.internal:22 ``` ## What's next * [Install ngrok as a background service](/docs/gateway/site-to-site-connectivity/background-service) to ensure the agent starts on boot and recovers from failures. * [Eliminate single points of failure with redundant agents](/docs/gateway/site-to-site-connectivity/redundant-agents) to achieve high availability. # Device Gateway FAQ Source: https://ngrok.com/docs/gateway/device-gateway/faq Answers to common questions about using ngrok as a device gateway. ## How does ngrok reach devices in remote networks? The ngrok agent runs on a device or a gateway machine in the remote network and opens a single outbound TLS connection to the ngrok cloud on port 443. Your cloud services, technicians, and customers reach the device through standard URLs that ngrok manages for you. No inbound ports, firewall rules, or VPN setup required on the remote network. ## How is this different from a VPN? With a VPN, the network owner at the remote site must configure a VPN server, open inbound firewall ports, and grant access to the entire network segment. The ngrok agent connects outbound—no firewall changes, no IT coordination at the remote network, and each endpoint exposes exactly one service. See [Secure Tunnels](/docs/gateway/device-gateway/tunnels) for a deeper comparison. ## Do I need an agent on every device? No. One agent on a gateway machine can route to any device on the local network behind it. You can also install the agent per-device if your topology requires it. ## What protocols work through ngrok? Any TCP-based protocol. HTTP, TLS, SSH, RDP, Modbus, and other proprietary industrial protocols all travel through the same outbound connection. Your technicians keep using their existing tools; only the gateway machine running the ngrok agent needs to be updated. ## Where can the agent run? The ngrok agent is a lightweight, dependency-free binary available for [Linux](/docs/gateway/device-gateway/linux), [Windows](/docs/gateway/device-gateway/windows), [ARM64](/docs/gateway/device-gateway/arm64), [Raspberry Pi](/docs/gateway/device-gateway/raspberry-pi), [Docker](/docs/using-ngrok-with/docker), and [Kubernetes](/docs/k8s). You can also embed connectivity directly in your own application using the [ngrok SDK](/docs/agent-sdks), which is useful when your software already runs in your customer's environment. # Fleet Management Source: https://ngrok.com/docs/gateway/device-gateway/fleet-management Manage credentials, multi-tenancy, and observability across thousands of devices with ngrok. ngrok is built for managing connectivity across large device fleets. Every operation is available via API, so you can wire ngrok into your existing pipeline and manage your entire fleet programmatically. ## Centralized control Run a single ngrok account and isolate each customer or site with: * **Per-device auth tokens** scoped with [ACL bindings](/docs/gateway/site-to-site-connectivity/authtoken-acls) so each token can only create endpoints on its assigned domain * **Wildcard domains** for unique URLs per site (for example, `*.factory.example.com`) * **[Service Users](/docs/iam/service-users/)** to create authtokens independent of any user account ```bash theme={null} # Create a Service User for a specific site curl -X POST https://api.ngrok.com/bot_users \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"name":"Factory 1 Service User"}' ``` Then create an authtoken for that Service User with an ACL binding that restricts it to endpoints on `factory1.example.com`. ## Credential management Create, scope, rotate, and revoke authtokens from your cloud without physical access to devices: * **Create** tokens via the [Authtokens API](/docs/api-reference/credentials/) * **Scope** tokens with ACL bindings to limit which endpoints they can create * **Rotate** tokens by creating a new token and revoking the old one * **Revoke** a compromised token immediately without affecting other devices If one token is compromised, only that device or site is affected, not your entire fleet. ## On-demand tunnels Fire up endpoints when you need them and wind them down when you don't. On Pay-as-you-go plans, billing is per active endpoint hour—one where traffic is actually flowing—so you only pay when a device is in use. Use the [ngrok API](/docs/api/) or the [Agent API](/docs/gateway/agent/api/) to manage tunnel lifecycle programmatically from your cloud. ## Observability Publish tunnel status, connection events, and traffic metrics to your telemetry platform. When a device drops, you'll know before anyone files a ticket. * Use [Event Subscriptions](/docs/obs/events/) to stream events to your logging infrastructure * Monitor agent status and tunnel health via the [ngrok API](/docs/api/) ## Next steps * [Full walkthrough](/docs/gateway/device-gateway/agent): see multi-tenancy and credential management in action * [Security](/docs/gateway/device-gateway/security): credential rotation, IP restrictions, and compliance * [Python SDK guide](/docs/gateway/device-gateway/sdk): manage device connectivity programmatically with the SDK # Install ngrok on Linux Source: https://ngrok.com/docs/gateway/device-gateway/linux Learn how to install ngrok on a remote Linux device to provide secure access and management. ngrok allows you to create secure ingress to any app, device, or service without spending hours learning arcane networking technologies. You can get started with a single command or a single line of code. What is ngrok? ngrok is an all-in-one cloud networking platform that secures, transforms, and routes traffic to services running anywhere. In this guide, you'll walk through the process of installing the ngrok agent on a remote Linux device, ensuring the agent runs integrated into your operating system, restricting traffic to trusted origins, and preparing to scale from a single device to a full fleet. ## 1. Install the ngrok Agent To download and install the ngrok agent on your remote Linux device, follow these steps: 1. Open a terminal into your remote Linux device. 2. Download the latest ngrok binary for your Linux distribution. You can find the correct binary on the [ngrok download page](https://download.ngrok.com): Select your operating system, select the version, and copy the link that appears in the **Download** button. Below is an example for x86-64: ```bash theme={null} wget https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz ``` Equinox is fully owned by ngrok and used exclusively for building and distributing ngrok binaries. See [the FAQ page](/docs/faq#does-ngrok-own-bin-equinox-io) for more details. 1. Unzip the downloaded file and move it to a directory in your PATH. Below is an example for `/usr/local/bin`: ```bash theme={null} sudo tar xvzf ./ngrok-v3-stable-linux-amd64.tgz -C /usr/local/bin ``` 1. Now that you have installed ngrok on your Linux device, link it to your ngrok account by using your authtoken: ```bash theme={null} ngrok authtoken NGROK_AUTHTOKEN ``` **Note**: Replace `NGROK_AUTHTOKEN` with your unique ngrok authtoken found in the [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). ## 2. Enable SSH access To enable remote SSH access to your device via ngrok: 1. Test that the ngrok agent is configured correctly by starting a TCP tunnel on your remote device. TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. If you get a different error, ensure your authtoken is configured correctly. ```bash theme={null} ngrok tcp 22 ``` 1. The ngrok agent assigns you a TCP address and port. Use these values to test the SSH access via ngrok by running the following command from another server or from a desktop. ```bash theme={null} ssh -p NGROK_PORT USER@NGROK_TCP_ADDRESS ``` **Note**: Replace the variables in the command line with the following: * NGROK\_PORT: The port number of the ngrok agent (that is, if the agent shows `tcp://1.tcp.ngrok.io:12345`, your port number is `12345`. * USER: A valid ssh login to access your remote device's operating system. * NGROK\_TCP\_ADDRESS: The address of the ngrok agent (that is, if the agent shows `tcp://1.tcp.ngrok.io:12345`, your TCP address is `1.tcp.ngrok.io`. ## 3. Adding IP restrictions Once you confirmed that you have connectivity to the device, add some security so that you are the only one who can access it. **Note**: This capability requires ngrok's **IP Restrictions** feature, which is only available with a paid subscription. 1. On the remote Linux device terminal, stop the ngrok process using the `ctrl+c` command. 2. Add an allow rule to restrict access to your Linux device to an IP address or a range of IP addresses. ```bash theme={null} ngrok tcp 22 --cidr-allow ALLOWED_IP_ADDRESS_CIDR ``` **Note**: Replace `ALLOWED_IP_ADDRESS_CIDR` with the CIDR notation for the allowed IP Address(es) (that is, `123.123.123.0/24`). Alternatively, you can create an IP policy in the ngrok dashboard (under [Security > IP Restrictions](https://dashboard.ngrok.com/ip-restrictions)), and leverage the same policy to control access to your entire device fleet. ## 4. Run ngrok as a service so you don't need to manually restart Read more about the `ngrok service`command which allows you to run and control an ngrok service on the operating system. [Run ngrok as a service.](/docs/gateway/agent/cli/#ngrok-service)). The ngrok agent works with native OS services like `systemd`. This helps you ensure that the ngrok service is available even after the machine restarts. Before doing this, it's useful to reserve a TCP address in the ngrok dashboard which allows you to reuse the same address each time the device is restarted. 1. Navigate to the ngrok Dashboard and access [Gateway > TCP Addresses](https://dashboard.ngrok.com/tcp-addresses). Create a new TCP address with a description and click **Save**. Your new TCP address will look something like `1.tcp.ngrok.io:12345`. Update the ngrok config file in your Linux device to start the ngrok agent using this TCP address. 1. Open the ngrok config file: ```bash theme={null} ngrok config edit ``` 1. Add the following to the end of the file and then save it: ```yaml theme={null} endpoints: - name: device-ssh url: tcp://NGROK_TCP_ADDRESS upstream: url: 22 traffic_policy: on_tcp_connect: - actions: - type: restrict-ips config: allow: - ALLOWED_IP_ADDRESS_CIDR ``` **Note**: Make sure to replace **NGROK\_TCP\_ADDRESS** with the address you reserved earlier in the ngrok dashboard (that is, `1.tcp.ngrok.io:12345`) and **ALLOWED\_IP\_ADDRESS\_CIDR** with the CIDR notation of the allowed IP Address(es) (that is, `123.123.123.0/24`). **Note**: Make note of the location of the `ngrok.yml` file. 1. Enable ngrok in service mode: ```bash theme={null} ngrok service install --config $HOME/.config/ngrok/ngrok.yml ``` You may need to run this command using `sudo` depending on your system 1. Run the following command to ensure your operating system launches ngrok with the ssh ingress whenever your device starts: ```bash theme={null} ngrok service start ``` You may need to run this command using `sudo` depending on your system 1. With ngrok running on your device, you should be able to SSH into the device using the reserved address from the dashboard. ```bash theme={null} ssh -p NGROK_PORT user@NGROK_TCP_ADDRESS ``` ## What's next? You've connected one device. The next step is understanding how ngrok works when that one device becomes ten, a hundred, or ten thousand. ### Learn the device gateway model The [Device Gateway guide](/docs/gateway/device-gateway/overview) walks through the full picture: giving every device a secure, addressable endpoint, keeping access policy in the cloud so you can update it without firmware pushes or truck rolls, and managing your entire fleet programmatically through [the ngrok API](/docs/api/). ### Scaling from one device to a fleet When your devices live in networks you don't own—kiosks in stores, POS terminals, medical devices in clinics, industrial controllers on factory floors—ngrok is a better fit than a mesh VPN. Enrolling each device into a VPN mesh means placing your network inside someone else's, at fleet scale. ngrok's model avoids that entirely: * A lightweight agent on each device dials out, so it traverses CGNAT and hostile firewalls by default—no inbound ports or firewall changes on networks you don't control. * Each device gets one named endpoint, addressable by your cloud, technicians, and customers. * Each device gets its own credentials, which you can revoke individually without touching the rest of the fleet. * Everything is automatable via the API across thousands of units, including [remote health checks, stop, start, and updates](/docs/api-reference/tunnelsessions/restart) for every agent in your fleet. For a deeper comparison of the two models, see [ngrok vs. Tailscale](https://ngrok.com/compare/tailscale). ### Talk to us about fleet pricing Running ngrok across a fleet doesn't have to mean list pricing. [Contact our sales team](https://ngrok.com/contact) to discuss per-device and fleet pricing for your deployment. # Device Gateway Overview Source: https://ngrok.com/docs/gateway/device-gateway/overview Use ngrok to securely connect to devices deployed in remote networks without firewall changes, VPNs, or open ports. ngrok makes it easy to [securely connect to devices](https://ngrok.com/use-cases/device-gateway) deployed in remote networks. Whether you need to reach a sensor behind a firewall, provide remote admin access to a kiosk, or connect your cloud to thousands of field-deployed devices, ngrok gives every device a secure, addressable endpoint. No firewall changes, VPNs, or open ports required. ## Why use ngrok as a device gateway? * **Every device gets a URL:** Each device becomes addressable by your cloud, technicians, and customers without touching the remote network. * **Policy lives in the cloud:** Update access controls, authentication, and rate limits without firmware pushes or truck rolls. * **No VPN required:** Unlike a VPN, the ngrok agent connects outbound on port 443 and scopes access to specific services—not entire networks. No inbound ports, firewall rules, or network reconfiguration needed on the remote network. * **API-compatible:** Every feature and function is available via [the ngrok http API](/docs/api/) so you can manage your entire fleet programmatically. * **Pay only for what you use:** On [Pay-as-you-go](/docs/pricing-limits/#pay-as-you-go-faqs) plans, billing is per active endpoint hour. Spin endpoints up and down via API for security reasons, not cost ones. Still curious? [Read the FAQ](/docs/gateway/device-gateway/faq). ## Use cases Give technicians and end users SSH, RDP, or web access to devices in the field Connect your cloud services to device APIs and databases behind remote networks Manage credentials, multi-tenancy, and observability across thousands of devices ## The ngrok platform Learn how ngrok creates outbound-only encrypted tunnels from devices to the cloud Authenticate, rate limit, and control access at ngrok's edge before traffic reaches your devices Manage credentials, IP restrictions, mTLS, and compliance across your fleet ## Next steps Get a device reachable from the internet in minutes Follow a detailed example scenario configuring a smart factory with ngrok # Private Connectivity to Remote IoT Devices with ngrokd Source: https://ngrok.com/docs/gateway/device-gateway/private-connectivity Use ngrokd to configure private, remote access to IoT devices and services in the field without a VPN. ngrok's Gateway allows you to connect to any app, IoT device, or service without networking expertise. This guide walks you through how to configure `ngrokd`: an ngrok component that enables a private, end-to-end remote access solution for IoT devices. This solution lets you securely access critical systems remotely without exposing them through public, authenticated endpoints. Instead, it leverages private endpoints that are only reachable from your local environment, eliminating your external attack surface while maintaining full access. ## Architectural reference ngrokd device gateway architecture ## What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * An ngrok API key. You'll need an account first. * An ngrok agent installed in a remote IoT-based network. See the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/) for instructions on how to install the ngrok agent. ## 1. Install ngrokd in your local environment, start it, and set your API key `ngrokd` is a lightweight daemon that makes your ngrok endpoints privately accessible and locked down to your local services. It automatically discovers your endpoints, assigns them local addresses, and forwards traffic through ngrok's secure infrastructure, allowing your apps to reach remote services without any network configuration. Think of it as a local proxy that turns ngrok endpoints into services that feel like they're running on your machine. ```bash theme={null} curl -fsSL https://raw.githubusercontent.com/ngrok/ngrokd/main/install.sh | sudo bash sudo ngrokd install sudo ngrokd start ngrokctl set-api-key YOUR_NGROK_API_KEY ``` ```bash theme={null} curl -fsSL https://raw.githubusercontent.com/ngrok/ngrokd/main/install.sh | sudo bash sudo ngrokd install sudo ngrokd start ngrokctl set-api-key YOUR_NGROK_API_KEY ``` ```powershell theme={null} # Run as Administrator iwr -useb https://raw.githubusercontent.com/ngrok/ngrokd/main/install.ps1 | iex Start-Process ngrokd -ArgumentList '--config=C:\ProgramData\ngrokd\config.yml' -WindowStyle Hidden ngrokctl set-api-key YOUR_NGROK_API_KEY ``` ```bash theme={null} docker pull ngrok/ngrokd:latest docker run -d \ --name ngrokd \ --cap-add=NET_ADMIN \ -e NGROK_API_KEY= \ -v ngrokd-data:/etc/ngrokd \ ngrok/ngrokd:latest ``` ## 2. Create a service user and authtoken for isolated network access Create a service user and an associated authtoken for each of your remote networks. A service user is intended for automated systems that programmatically interact with your ngrok account (other platforms sometimes call this concept a Service Account). Create a separate service user and associated authtoken for each remote network so that: * Their usage of your ngrok account is isolated and scoped with a specific permission set * If an agent is compromised, you can revoke its access independently * Agent start/stop audit events are properly attributed to each network * Your ngrok agents don't stop working if the human user who set them up leaves your ngrok account Navigate to the [Service Users](https://dashboard.ngrok.com/service-users) section of your dashboard and click **New Service User**. Service Users page with New Service User button Next, create an authtoken assigned to this specific service user. Authtokens page showing a new authtoken assigned to a service user ## 3. Install the ngrok agent within your remote network and configure private Agent Endpoints in ngrok.yml First, install the agent in your remote network, either on a gateway or directly on a device. Follow the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/) for installation instructions. Then, configure the agent to create private Agent Endpoints pointing to the devices you want to remotely access. This connects the devices to your ngrok account, and the configuration is shown in the example agent configuration file below. **Private Agent Endpoints** are private endpoints that can **only** receive traffic from services on your local network where `ngrokd` is installed. These endpoints are not publicly addressable anywhere on the internet, and access is **completely locked down** to your local environment. After installing the ngrok agent, define private Agent Endpoints for each service you want to remotely access inside the ngrok configuration file at `/path/to/ngrok/ngrok.yml`: ```yaml theme={null} version: 3 agent: authtoken: AUTHTOKEN_CREATED_IN_STEP_2 endpoints: - name: Internal Endpoint for Controller url: http://controller.example.com upstream: url: 9080 bindings: - kubernetes - name: Internal Endpoint for Security Camera url: tcp://camera.example.com:22 upstream: url: 22 bindings: - kubernetes ``` Once you've saved the configuration, activate your endpoints by running: ```bash theme={null} ngrok start --all ``` ## 4. Test end-to-end connectivity from your local environment to upstream devices From the machine where `ngrokd` is running, run: ```bash theme={null} ngrokctl list ``` You should see your private endpoints listed: ngrokctl list output showing private endpoints From here, you can curl your HTTP endpoint and verify you get the expected response. The same can be done for your TCP endpoint by connecting at the assigned hostname and port: ```bash theme={null} ssh user@camera.example.com -p 22 ``` ## What's next * [Install ngrok as a background service](/docs/gateway/site-to-site-connectivity/background-service) to ensure the agent starts on boot and recovers from failures. * [Eliminate single points of failure with redundant agents](/docs/gateway/site-to-site-connectivity/redundant-agents) to achieve high availability. # Device Gateway Quickstart Source: https://ngrok.com/docs/gateway/device-gateway/quickstart Get a device securely reachable from the internet in minutes using the ngrok agent. This quickstart shows you how to make a service running on a remote device accessible from the internet using ngrok. ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/signup) * A device with network access (Linux, Windows, Raspberry Pi, or any system with a CPU and network connection) * A service running on the device (for example, an HTTP server on port 8080 or SSH on port 22) ## 1. Install the ngrok agent [Download ngrok](https://ngrok.com/download) on your device and authenticate: ```bash theme={null} ngrok config add-authtoken ``` ## 2. Start a tunnel Use the following command to start a tunnel with the ngrok agent. When the command runs, the agent reaches the ngrok cloud using a single outbound TLS connection on port 443, creating a secure tunnel to the service on your device. No inbound firewall rules or port forwarding are required on the remote network Expose an HTTP service running on the device. Replace `8080` with any port where your service is running: ```bash theme={null} ngrok http 8080 ``` Expose SSH access on a remote Linux device: ```bash theme={null} ngrok tcp 22 ``` Expose RDP access on a remote Windows device: ```bash theme={null} ngrok tcp 3389 ``` ## 3. Connect to your device Copy the public URL from the agent console and use it to reach your device from anywhere. For HTTP endpoints, open the URL in a browser. For TCP endpoints (like SSH), connect using the provided host and port: ```bash theme={null} ssh -p user@ ``` ## 4. Run ngrok as a service For production deployments, install ngrok as a background service so it starts automatically and recovers from crashes: ```bash theme={null} ngrok service install --config /path/to/ngrok.yml ngrok service start ``` ## Next steps * [Full walkthrough](/docs/gateway/device-gateway/agent): follow a detailed example configuring a smart factory with internal and Cloud Endpoints * [Installation guides](/docs/gateway/device-gateway/linux): platform-specific setup for Linux, ARM64, Raspberry Pi, and Windows * [Fleet management](/docs/gateway/device-gateway/fleet-management): manage credentials and multi-tenancy across thousands of devices * [Traffic Policy](/docs/gateway/device-gateway/traffic-policy): add authentication, rate limiting, and access control at ngrok's edge # Install ngrok on Raspberry Pi Source: https://ngrok.com/docs/gateway/device-gateway/raspberry-pi Learn how to install ngrok on a Raspberry Pi running Linux to provide secure remote access and management. ngrok allows you to create secure ingress to any app, device, or service without spending hours learning arcane networking technologies. You can get started with a single command or a single line of code. What is ngrok? ngrok is an all-in-one cloud networking platform that secures, transforms, and routes traffic to services running anywhere. In this guide, you'll walk through the process of installing the ngrok agent on a remote Raspberry Pi device running Linux, ensuring the agent runs integrated into your operating system, restricting traffic to trusted origins, and integrating traffic events with your preferred logging tool. ## 1. Install the ngrok Agent To download and install the ngrok agent on your remote Raspberry Pi device, follow these steps: 1. Open a terminal into your remote Raspberry Pi device. 2. Download the latest ngrok binary for your Linux distribution. You can find the correct binary on the [ngrok download page](https://download.ngrok.com): Select your operating system, select the version, and copy the link that appears in the **Download** button. Below is an example for ARM64: ```bash theme={null} wget https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-arm64.tgz ``` Equinox is fully owned by ngrok and used exclusively for building and distributing ngrok binaries. See [the FAQ page](/docs/faq#does-ngrok-own-bin-equinox-io) for more details. 3. Unzip the downloaded file and move it to a directory in your PATH. Below is an example for `/usr/local/bin`: ```bash theme={null} sudo tar xvzf ./ngrok-v3-stable-linux-arm64.tgz -C /usr/local/bin ``` 4. Now that you have installed ngrok on your Raspberry Pi device, link it to your ngrok account by using your authtoken: ```bash theme={null} ngrok authtoken NGROK_AUTHTOKEN ``` **Note**: Replace `NGROK_AUTHTOKEN` with your unique ngrok authtoken found in the [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). ## 2. Enable SSH access To enable remote SSH access to your device via ngrok: 1. Test that the ngrok agent is configured correctly by starting a TCP tunnel on your remove device. TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. If you get a different error, ensure your authtoken is configured correctly. ```bash theme={null} ngrok tcp 22 ``` 2. The ngrok agent assigns you a TCP address and port. Use these values to test the SSH access via ngrok by running the following command from another server or from a desktop. ```bash theme={null} ssh -p NGROK_PORT USER@NGROK_TCP_ADDRESS ``` **Note**: Replace the variables in the command line with the following: * NGROK\_PORT: The port number of the ngrok agent (that is, if the agent shows `tcp://1.tcp.ngrok.io:12345`, your port number is `12345`. * USER: A valid ssh login to access your remote device's operating system. * NGROK\_TCP\_ADDRESS: The address of the ngrok agent (that is, if the agent shows `tcp://1.tcp.ngrok.io:12345`, your TCP address is `1.tcp.ngrok.io`. ## 3. Adding IP restrictions Once you confirmed that you have connectivity to the device, add some security so that you are the only one who can access it. **Note**: This capability requires ngrok's **IP Restrictions** feature, which is only available with a paid subscription. 1. On the remote Raspberry Pi device terminal, stop the ngrok process using the `ctrl+c` command. 2. Add an allow rule to restrict access to your Raspberry Pi device to an IP address or a range of IP addresses. ```bash theme={null} ngrok tcp 22 --cidr-allow ALLOWED_IP_ADDRESS_CIDR ``` **Note**: Replace `ALLOWED_IP_ADDRESS_CIDR` with the CIDR notation for the allowed IP Address(es) (that is, `123.123.123.0/24`). Alternatively, you can create an IP policy in the ngrok dashboard (under [Security > IP Restrictions](https://dashboard.ngrok.com/ip-restrictions)), and leverage the same policy to control access to your entire device fleet. ## 4. Configure ngrok to recover on outages The ngrok agent works with native OS services like `systemd`. This helps you ensure that the ngrok service is available even after the machine restarts. Before doing this, it's useful to reserve a TCP address in the ngrok dashboard which allows you to reuse the same address each time the device is restarted. 1. Navigate to the ngrok Dashboard and access [Gateway > TCP Addresses](https://dashboard.ngrok.com/tcp-addresses). Create a new TCP address with a description and click **Save**. Your new TCP address will look something like `1.tcp.ngrok.io:12345`. Update the ngrok config file in your Raspberry Pi device to start the ngrok agent using this TCP address. 1. Open the ngrok config file: ```bash theme={null} ngrok config edit ``` 1. Add the following to the end of the file and then save it: ```yaml theme={null} endpoints: - name: device-ssh url: tcp://NGROK_TCP_ADDRESS upstream: url: 22 traffic_policy: on_tcp_connect: - actions: - type: restrict-ips config: allow: - ALLOWED_IP_ADDRESS_CIDR ``` **Note**: Make sure to replace **NGROK\_TCP\_ADDRESS** with the address you reserved earlier in the ngrok dashboard (that is, `1.tcp.ngrok.io:12345`) and **ALLOWED\_IP\_ADDRESS\_CIDR** with the CIDR notation of the allowed IP Address(es) (that is, `123.123.123.0/24`). **Note**: Make note of the location of the `ngrok.yml` file. 1. Enable ngrok in service mode: ```bash theme={null} ngrok service install --config $HOME/.config/ngrok/ngrok.yml ``` **Note**: You may need to run this command using `sudo` depending on your system 1. Run the following command to ensure your operating system launches ngrok with the ssh ingress whenever your device starts: ```bash theme={null} ngrok service start ``` **Note**: You may need to run this command using `sudo` depending on your system 1. With ngrok running on your device, you should be able to SSH into the device using the reserved address from the dashboard. ```bash theme={null} ssh -p NGROK_PORT user@NGROK_TCP_ADDRESS ``` ## What's next? Now that your device is integrated to ngrok, you can ​​execute tasks at the ngrok dashboard to operationalize your fleet: ### Logging traffic from ngrok Each action that happens in ngrok is published as a log, and [Log Exporting](/docs/obs/) allows you to subscribe to the logs that are relevant to you and write them to one or more destinations. A Log Export is made up of a set of log sources (some of which can be filtered), and log destinations. Each export can send logs to one or more destinations, such as Amazon CloudWatch Logs, Amazon Kinesis (as a data stream), or Amazon Kinesis Firehose (as a delivery stream). Log Exports can be configured through the [ngrok Dashboard](https://dashboard.ngrok.com/log-exporting) or the [ngrok API](/docs/api-reference/eventdestinations). You can also forward all or some of your traffic logs from [ngrok to your preferred logging tool](/docs/obs/). ### Remote checks, stop, start, and updates ngrok provides [APIs](/docs/api-reference/tunnelsessions/restart) and a [dashboard UI](https://dashboard.ngrok.com/agents) for you to monitor the health of ngrok agents running in your fleet. The interfaces also allow you to remotely stop, start, and update agents. # Install ngrok on Raspberry Pi OS (formerly Raspbian) Source: https://ngrok.com/docs/gateway/device-gateway/raspbian Learn how to install ngrok on a Raspberry Pi OS (formerly Raspbian) to provide secure remote access and management. ngrok allows you to create secure ingress to any app, device, or service without spending hours learning arcane networking technologies. You can get started with a single command or a single line of code. What is ngrok? ngrok is an all-in-one cloud networking platform that secures, transforms, and routes traffic to services running anywhere. In this guide, you'll walk through the process of installing the ngrok agent on a remote Raspberry Pi OS (formerly Raspbian) device, ensuring the agent runs integrated into your operating system, restricting traffic to trusted origins, and integrating traffic events with your preferred logging tool. ## 1. Install the ngrok Agent To download and install the ngrok agent on your remote LinuxRaspberry Pi OS device, follow these steps: 1. Open a terminal into your remote Raspberry Pi OS device. 2. Download the latest ngrok binary for your Linux distribution. You can find the correct binary on the [ngrok download page](https://download.ngrok.com): Select your operating system, select the version, and copy the link that appears in the **Download** button.: ```bash theme={null} wget https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-arm.tgz ``` Equinox is fully owned by ngrok and used exclusively for building and distributing ngrok binaries. See [the FAQ page](/docs/faq#does-ngrok-own-bin-equinox-io) for more details. 3. Unzip the downloaded file and move it to a directory in your PATH. Below is an example for `/usr/local/bin`: ```bash theme={null} sudo tar xvzf ./ngrok-v3-stable-linux-arm64.tgz -C /usr/local/bin ``` 4. Now that you have installed ngrok on your LinuxRaspberry Pi OS device, link it to your ngrok account by using your authtoken: ```bash theme={null} ngrok authtoken NGROK_AUTHTOKEN ``` **Note**: Replace `NGROK_AUTHTOKEN` with your unique ngrok authtoken found in the [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). ## 2. Enable SSH access To enable remote SSH access to your device via ngrok: 1. Test that the ngrok agent is configured correctly by starting a TCP tunnel on your remove device. TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. If you get a different error, ensure your authtoken is configured correctly. ```bash theme={null} ngrok tcp 22 ``` 2. The ngrok agent assigns you a TCP address and port. Use these values to test the SSH access via ngrok by running the following command from another server or from a desktop. ```bash theme={null} ssh -p NGROK_PORT USER@NGROK_TCP_ADDRESS ``` **Note**: Replace the variables in the command line with the following: * NGROK\_PORT: The port number of the ngrok agent (that is, if the agent shows `tcp://1.tcp.ngrok.io:12345`, your port number is `12345`. * USER: A valid ssh login to access your remote device's operating system. * NGROK\_TCP\_ADDRESS: The address of the ngrok agent (that is, if the agent shows `tcp://1.tcp.ngrok.io:12345`, your TCP address is `1.tcp.ngrok.io`. ## 3. Adding IP restrictions Once you confirmed that you have connectivity to the device, add some security so that you are the only one who can access it. **Note**: This capability requires ngrok's **IP Restrictions** feature, which is only available with a paid subscription. 1. On the remote LinuxRaspberry Pi OS device terminal, stop the ngrok process using the `ctrl+c` command. 2. Add an allow rule to restrict access to your LinuxRaspberry Pi OS device to an IP address or a range of IP addresses. ```bash theme={null} ngrok tcp 22 --cidr-allow ALLOWED_IP_ADDRESS_CIDR ``` **Note**: Replace `ALLOWED_IP_ADDRESS_CIDR` with the CIDR notation for the allowed IP Address(es) (that is, `123.123.123.0/24`). Alternatively, you can create an IP policy in the ngrok dashboard (under [Security > IP Restrictions](https://dashboard.ngrok.com/ip-restrictions)), and leverage the same policy to control access to your entire device fleet. ## 4. Configure ngrok to recover on outages The ngrok agent works with native OS services like `systemd`. This helps you ensure that the ngrok service is available even after the machine restarts. Before doing this, it's useful to reserve a TCP address in the ngrok dashboard which allows you to reuse the same address each time the device is restarted. 1. Navigate to the ngrok Dashboard and access [Gateway > TCP Addresses](https://dashboard.ngrok.com/tcp-addresses). Create a new TCP address with a description and click **Save**. Your new TCP address will look something like `1.tcp.ngrok.io:12345`. Update the ngrok config file in your LinuxRaspberry Pi OS device to start the ngrok agent using this TCP address. 1. Open the ngrok config file: ```bash theme={null} ngrok config edit ``` 1. Add the following to the end of the file and then save it: ```yaml theme={null} endpoints: - name: device-ssh url: tcp://NGROK_TCP_ADDRESS upstream: url: 22 traffic_policy: on_tcp_connect: - actions: - type: restrict-ips config: allow: - ALLOWED_IP_ADDRESS_CIDR ``` **Note**: Make sure to replace **NGROK\_TCP\_ADDRESS** with the address you reserved earlier in the ngrok dashboard (that is, `1.tcp.ngrok.io:12345`) and **ALLOWED\_IP\_ADDRESS\_CIDR** with the CIDR notation of the allowed IP Address(es) (that is, `123.123.123.0/24`). **Note**: Make note of the location of the `ngrok.yml` file. 1. Enable ngrok in service mode: ```bash theme={null} ngrok service install --config $HOME/.config/ngrok/ngrok.yml ``` **Note**: You may need to run this command using `sudo` depending on your system 1. Run the following command to ensure your operating system launches ngrok with the ssh ingress whenever your device starts: ```bash theme={null} ngrok service start ``` **Note**: You may need to run this command using `sudo` depending on your system 1. With ngrok running on your device, you should be able to SSH into the device using the reserved address from the dashboard. ```bash theme={null} ssh -p NGROK_PORT user@NGROK_TCP_ADDRESS ``` ## What's next? Now that your device is integrated to ngrok, you can ​​execute tasks at the ngrok dashboard to operationalize your fleet: ### Logging traffic from ngrok Each action that happens in ngrok is published as a log, and [Log Exporting](/docs/obs/) allows you to subscribe to the logs that are relevant to you and write them to one or more destinations. A Log Export is made up of a set of log sources (some of which can be filtered), and log destinations. Each export can send logs to one or more destinations, such as Amazon CloudWatch Logs, Amazon Kinesis (as a data stream), or Amazon Kinesis Firehose (as a delivery stream). Log Exports can be configured through the [ngrok Dashboard](https://dashboard.ngrok.com/log-exporting) or the [ngrok API](/docs/api-reference/eventdestinations). You can also forward all or some of your traffic logs from [ngrok to your preferred logging tool](/docs/obs/). ### Remote checks, stop, start, and updates ngrok provides [APIs](/docs/api-reference/tunnelsessions/restart) and a [dashboard UI](https://dashboard.ngrok.com/agents) for you to monitor the health of ngrok agents running in your fleet. The interfaces also allow you to remotely stop, start, and update agents. # Secure Remote Access with ngrok for SSH and RDP Source: https://ngrok.com/docs/gateway/device-gateway/remote-access Give technicians and end users SSH, RDP, or web access to devices deployed in remote networks using ngrok. By creating secure TCP endpoints, ngrok enables easy, centralized access for technicians, engineers, and IT admins to maintain and update remote devices and services. This guide helps you streamline connectivity without compromising security for remote access using SSH or RDP. Use cases include secure SSH into remote IoT devices or connecting to a Windows RDP server in a remote network. This guide demonstrates the setup for a single remote network. ## Architectural reference Architecture diagram: one ngrok agent per remote network routing cloud traffic to internal SSH and RDP devices ### Why only one ngrok agent per remote network? Traditionally, you might assume that every service or device inside the network needs its own ngrok agent, but this isn't necessary. A single ngrok agent is installed on a network-accessible server inside the remote network. The agent acts as a central gateway that can reach any service on the local network, eliminating the need for multiple agents. ## What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * An ngrok agent configured in a remote network or remote device. See the [getting started guide](https://ngrok.com/docs/getting-started/) for instructions on how to install the ngrok agent. ## 1. Create a service user and authtoken for isolated network access First, create a service user and an associated authtoken for each of your customers. A service user is intended for automated systems that programmatically interact with your ngrok account (other platforms sometimes call this concept a Service Account). Create a separate service user and associated authtoken for each of your customers so that: * Their usage of your ngrok account is isolated and scoped with a specific permission set * If a customer is compromised, you can revoke their access independently * Agent start/stop audit events are properly attributed to each customer * Your ngrok agents don't stop working if the human user who set them up leaves your ngrok account Navigate to the [Service Users](https://dashboard.ngrok.com/service-users) section of your dashboard and click **New Service User**. Service Users page with New Service User button Next, create an authtoken assigned to this specific service user. Authtokens page showing a new authtoken assigned to a service user ## 2. Install the ngrok agent within your remote network and configure internal Agent Endpoints in ngrok.yml Configure the agent to create internal Agent Endpoints that point to the devices you want to remotely access. This connects the devices to your ngrok account, but nothing can connect to them until you complete the subsequent steps. The configuration to achieve this is shown in the example agent configuration file below. Internal Agent Endpoints are private endpoints that only receive traffic when forwarded through the [forward-internal Traffic Policy action](https://ngrok.com/docs/traffic-policy/actions/forward-internal). This allows you to route traffic to an application through ngrok without making it publicly addressable. Internal Agent Endpoint URL hostnames must end with `.internal`. After installing the ngrok agent, define internal Agent Endpoints for each service you want to remotely access inside the ngrok configuration file. You can install ngrok and its configuration file at `/path/to/ngrok/ngrok.yml` and the executable at `/path/to/ngrok/ngrok`. ```yaml theme={null} version: 3 agent: authtoken: AUTHTOKEN_CREATED_IN_STEP_1 endpoints: - name: Internal Endpoint for Device 1 url: tcp://device1.internal:22 upstream: url: 22 - name: Internal Endpoint for Device 2 url: tcp://device2.internal:22 upstream: url: 22 - name: Internal Endpoint for RDP Server url: tcp://rdp-server.internal:3389 upstream: url: 3389 ``` ## 3. Reserve a TCP address for each device and server You need to reserve a TCP address to create a TCP Cloud Endpoint, which you'll do in the next step. Reserving the address holds it exclusively for your ngrok account. Do this for each device and for the RDP server. Reserved TCP addresses page with a newly reserved address ## 4. Create your TCP Cloud Endpoints and attach a Traffic Policy Cloud Endpoints are persistent, always-on endpoints whose creation, deletion, and configuration is managed centrally via the dashboard or API. They exist permanently until explicitly deleted. Cloud Endpoints don't forward their traffic to an agent by default—they only use their attached Traffic Policy to handle connections. Endpoints page with New button to create a TCP Cloud Endpoint Click on your newly created Cloud Endpoint and replace the default Traffic Policy with: ```yaml theme={null} on_tcp_connect: - actions: - type: forward-internal config: url: tcp://device1.internal:22 ``` ## 5. Secure your Cloud Endpoint with IP restrictions Navigate to your newly created Cloud Endpoint in the [Endpoints](https://dashboard.ngrok.com/endpoints) tab on your ngrok dashboard and apply a `restrict-ips` Traffic Policy action to enable a source IP allow list. IP restrictions let you directly filter who can use the endpoint and prevent port scanners or other malicious actors. You can add this action directly to the Cloud Endpoint's YAML configuration. The final config for this action is shown below: ```yaml theme={null} on_tcp_connect: - actions: - type: restrict-ips config: enforce: true allow: - e680:5791:be4c:5739:d959:7b94:6d54:d4b4/128 - 203.0.113.42/32 - actions: - type: forward-internal config: url: tcp://device1.internal:22 ``` ## 6. Use your ngrok TCP endpoints with your SSH and RDP clients Now that you've created and secured your TCP Cloud Endpoints, you can use them in your existing SSH and RDP client setups to test your remote connectivity. ## What's next * [Install ngrok as a background service](/docs/gateway/site-to-site-connectivity/background-service) to ensure the agent starts on boot and recovers from failures. * [Eliminate single points of failure with redundant agents](/docs/gateway/site-to-site-connectivity/redundant-agents) to achieve high availability. # Connect to Devices Using the Python SDK Source: https://ngrok.com/docs/gateway/device-gateway/sdk Connect to devices or your device gateway by embedding connectivity directly into your application with the Python SDK. This guide provides step-by-step instructions for using ngrok as a device gateway. This example shows you how to embed connectivity into an application using ngrok's Python SDK to access an API running on an IoT device. All the Python code in this example is available in [the ngrok-samples/ngrok-python-iot-agent repo](https://github.com/ngrok-samples/ngrok-python-iot-agent). ## Prerequisites To use the sample application, you need the following prerequisites on the device or device gateway you wish to connect to: * Python 3.11 * [venv](https://docs.python.org/3/library/venv.html#) or another Python virtual environment manager ## Get an ngrok API key [Create an ngrok API key](https://dashboard.ngrok.com/api-keys/new) using the ngrok dashboard. Make sure you save the API key before you leave the screen because it won't be displayed again. ## Reserve a domain for wildcard endpoints Reserve a [wildcard domain](/docs/api-reference/reserveddomains/list), which will allow you to create wildcard endpoints and receive traffic on any subdomain of your domain. For billing information, see [wildcard endpoints pricing](/docs/pricing-limits/#wildcard-endpoints). For example, you might create `*.sitea.{YOUR_DOMAIN}`. You would then be able to create endpoints on `device342.sitea.{YOUR_DOMAIN}` and `device896.sitea.{YOUR_DOMAIN}` without having to reserve them individually. It can be helpful to create a separate subdomain for each site you wish to connect to. Run the following command, substituting your API key for `{API_KEY}` and your domain for `{YOUR_DOMAIN}`: ``` curl \ -X POST \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"domain":"*.sitea.{YOUR_DOMAIN}","region":"us"}' \ https://api.ngrok.com/reserved_domains ``` You should receive a `201` response similar to the following: ```json theme={null} { "id": "rd_2euR3S7Mt07gRC6NlVGcXtghElK", "uri": "https://api.ngrok.com/reserved_domains/rd_2euR3S7Mt07gRC6NlVGcXtghElK", "created_at": "2024-04-10T12:31:16Z", "domain": "*.sitea.configurable-domain.com", "region": "", "cname_target": "mwz4q7fiabfcxbfy.4wsgv9l5wfesschhy.ngrok-cname.com", "http_endpoint_configuration": null, "https_endpoint_configuration": null, "certificate": null, "certificate_management_policy": { "authority": "letsencrypt", "private_key_type": "ecdsa" }, "certificate_management_status": { "renews_at": null, "provisioning_job": { "error_code": null, "msg": "Managed certificate provisioning in progress.", "started_at": "2024-04-10T12:31:16Z", "retries_at": null } }, "acme_challenge_cname_target": "f7ekwqdb.acme-challenge.ngrok-dns.com" } ``` You'll need values from the response in the next section. ### Update DNS records Next, add two `CNAME` records to your DNS provider's registry, providing values from the previous response. Add a CNAME record for the domain you just reserved. AWS Route 53 Quick create record form showing a wildcard CNAME record for the device gateway subdomain pointing to the ngrok CNAME target Then add another record for the [ACME (Automated Certificate Management Environment)](https://venafi.com/blog/what-acme-protocol-and-how-has-it-changed-pki/) validation. For the record, enter `_acme-challenge.{SUBDOMAIN}`, and use the value of the `acme_challenge_cname_target` property from previous response for the value. For the example above, the record name would be `_acme-challenge.sitea`. ### Verify DNS Use the ngrok dashboard to verify that you’ve configured DNS correctly. 1. Login to the [ngrok dashboard](https://dashboard.ngrok.com/). 2. Click **Domains** in the left-hand navigation menu. 3. Click on your domain under *Domain*. 4. Click **2 targets** next to *DNS Targets* in the panel displayed on the right-hand side of the screen, as denoted by the arrow in the screenshot below. ngrok dashboard Domains page showing a reserved domain's detail panel with the DNS Targets 2 targets link highlighted 5. Click **Check Status**, as denoted by the arrow in the screenshot below. ngrok dashboard DNS Targets panel showing CNAME and TLS record values with the Check Status button highlighted 6. You should see a successful response similar to the screenshot below. ngrok dashboard DNS Targets panel showing successful CNAME and TLS record validation with green checkmarks ## Create a Service User You'll create a Service User so that in the next section, you can create an agent authtoken independent of any user account. A Service User does not belong to a particular user account. Run the following command to create a new Service User, providing your API key and a description: ``` curl \ -X POST \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"name":"new Service User from API"}' \ https://api.ngrok.com/service_users ``` You should receive a `201` response similar to the following: ```json theme={null} { "id": "service_2fmwUXhnImKswMgZKCsx7cnbt2l", "uri": "https://api.ngrok.com/service_users/service_2fmwUXhnImKswMgZKCsx7cnbt2l", "name": "new Service User from API", "active": true, "created_at": "2024-04-29T19:39:36Z" } ``` ## Create the Agent authtoken You should start each agent using a separate authtoken, and that token should belong to a Service User. To create an authtoken, login to the ngrok dashboard and click [**Authtokens**](https://dashboard.ngrok.com/authtokens) under *Tunnels*, then click **Add a Tunnel Authtoken**. For **Owner**, select the Service User you use created, and select `bind:*.sitea.{YOUR_DOMAIN}` for the ACL Rules. This ACL will allow an agent with the authtoken to create tunnels on any subdomain of `sitea.{YOUR_DOMAIN}`. ngrok dashboard New Authtoken dialog with a bot user as owner and a wildcard domain ACL binding rule ## Configure the sample Agent The sample application uses the ngrok Python SDK to embed connectivity directly into the application rather than using the standalone ngrok agent, the CLI, or the ngrok Kubernetes Operator. It serves a local REST API you can use to create, delete, and list tunnels. ### Clone the repo Clone [the ngrok-samples/ngrok-python-iot-agent repo](https://github.com/ngrok-samples/ngrok-python-iot-agent) on the device or device gateway you wish to connect to. ### Create and activate a virtual environment Run the following to create a virtual environment, providing a directory for `{PATH_TO_VENV}`: `python -m venv {PATH_TO_VENV}` Run the appropriate command for your O/S to activate the virtual environment: * In cmd.exe: `{PATH_TO_VENV}\Scripts\activate.bat` * In PowerShell: `{PATH_TO_VENV}\Scripts\Activate.ps1` * Linux and macOS: `source {PATH_TO_VENV}/bin/activate` ### Install dependencies Run `pip install -r requirements` to install the dependencies. ### Update the domain Update line `109` with your domain. Since you reserved a wildcard domain, you can create endpoints on any subdomain of that domain without reserving them individually. For example, for the domain `*.sitea.configurable-domain.com`, you could create an endpoint at `api.sitea.configurable-domain.com`. ### Set your authtoken This sample application is designed to read your ngrok credentials from the environment, so run the following command, replacing `{YOUR_AUTHTOKEN}` with the authtoken you created in the previous step: ```bash theme={null} export NGROK_AUTHTOKEN={YOUR_AUTHTOKEN} ``` ## Use the sample app This sample application is a custom ngrok agent built using the Python SDK. In a previous step, you updated the domain on line `109` of `agent.py`. This domain will be used to serve a local REST API built into the sample application using FastAPI. You'll use this API to create, delete, and list tunnels remotely. The application persists tunnels in a local JSON file, `ngrok_config.json`, and recreates them when the application is restarted. You could extend this application to store tunnel configuration in a backend database. You may pass policy information directly in the body of the request. The application includes a JSON file, `test.json`, which contains the default [policies](/docs/gateway/traffic-policy/) it applies to any new request that doesn't specify policy information. You could update this file to control policies applied to new tunnels rather than passing the policy information in each request. ### Start the app Run the following to start the agent: ```python theme={null} python agent.py ``` ### List tunnels Run the following command to list tunnels started by this custom agent application, substituting the domain you set on line `109` for `{YOUR_AGENT_DOMAIN}`: ```curl theme={null} curl -k \ -X GET \ https://{YOUR_AGENT_DOMAIN}/tunnels ``` Initially, you should receive a `200` response with an empty array. ### Start a tunnel with default policies Run the following command to start a tunnel to the device or device gateway where the custom agent application is running, substituting the appropriate values for the variables: * `{HOST}` - the host where the API is running, leave blank and remove the colon for `localhost` * `{PORT}` - the port where the API is running * `{TUNNEL_DOMAIN}` - the domain you want to serve the API on the device or device gateway * `{YOUR_AGENT_DOMAIN}` - the domain you set on line `109` of the code ```curl theme={null} curl -k \ -X POST \ -H "Content-Type: application/json" \ -d '{"protocol": "http", "forwards_to": "{HOST}:{PORT}", "domain": "{TUNNEL_DOMAIN}"}' \ https://{YOUR_AGENT_DOMAIN}/tunnels ``` You should receive a `200` response similar to the following: ```json theme={null} { "url": "https://device123.sitea.configurable-domain.com", "protocol": "http", "forwards_to": "localhost:8001", "domain": "agent.sitea.configurable-domain.com", "policy": null } ``` You'll notice that the `policy` property is `null`. This is because the request didn't include any [policies](/docs/gateway/traffic-policy/). However, this sample application applies the default policies defined in `test.json` if no policies are included in the request. ### List tunnels If you run the request to list tunnels again, you should receive a `200` response similar to the following. Notice that it includes the policies defined in `test.json`. You can update the policies in this file to the defaults you want applied to new tunnels when no policies are specified in the request: ```json theme={null} [ { "url": "https://device123.sitea.configurable-domain.com", "protocol": "http", "forwards_to": "localhost:8001", "domain": "device123.sitea.configurable-domain.com", "traffic_policy": "{\"on_http_request\": [], \"on_http_response\": [{\"expressions\": [], \"name\": \"Add headers to requests\", \"actions\": [{\"type\": \"add-headers\", \"config\": {\"headers\": {\"is-ngrok\": \"0000000000\"}}}]}]}" } ] ``` ### Start a tunnel with policies Run the following command, substituting the appropriate values for the variables to create a new tunnel, specifying [policies](/docs/gateway/traffic-policy/) directly in the request. ```curl theme={null} curl -k \ -X POST \ -H "Content-Type: application/json" \ -d '{"protocol": "http", "forwards_to": "{HOST}:{PORT}","domain":"{YOUR_DOMAIN}", "policy": \ {"on_http_request":[],"on_http_response":[{"expressions":[],"name":"Add headers to requests","actions":[{"type":"add-headers","config":{"headers":{"is-ngrok":"444"}}}]}]}}' \ https://{YOUR_AGENT_DOMAIN}/tunnels ``` You should receive a `200` response similar to the following: ```json theme={null} { "url": "https://device123.sitea.configurable-domain.com", "protocol": "http", "forwards_to": "localhost:8001", "domain": "device123.sitea.configurable-domain.com", "policy": { "on_http_request": [], "on_http_response": [ { "expressions": [], "name": "Add headers to requests", "actions": [ { "type": "add-headers", "config": { "headers": { "is-ngrok": "444" } } } ] } ] } } ``` #### Delete tunnel Run the following command to delete a tunnel, substituting `{YOUR_AGENT_DOMAIN}` with the value you set on line `109` of the code and `{URL_PART}` with the value of the `url_part` property of the list tunnels request: ```curl theme={null} curl \ -X \ DELETE \ https://{YOUR_AGENT_DOMAIN}/tunnels/{URL_PART} ``` ## Access the API running on the device or device gateway You should now be able to access the API on the device or device gateway using the value from `{TUNNEL_DOMAIN}` in the request to remote manage your device fleet. # Device Security Source: https://ngrok.com/docs/gateway/device-gateway/security Manage credentials, IP restrictions, mTLS, and compliance across your device fleet with ngrok. ngrok provides multiple layers of security for your device fleet, from credential management to network-level access controls and compliance certifications. ## Credential management Each device or site gets its own authtoken scoped with [ACL bindings](/docs/gateway/site-to-site-connectivity/authtoken-acls). If one token is compromised, revoke it immediately without affecting the rest of your fleet: * **Create** per-device or per-site [auth tokens](/docs/gateway/agent/authtokens) * **Scope** tokens to [limit which endpoints they can create](/docs/gateway/agent/authtokens#authtoken-acls) * **Rotate** tokens by creating a replacement and revoking the old one * **Revoke** compromised tokens instantly from your cloud No physical access to the device is required for any of these operations. ## IP restrictions Limit access to your device endpoints to specific IP addresses or CIDR ranges [using Traffic Policy](/docs/gateway/traffic-policy/actions/restrict-ips/). The following example demonstrates an [agent config](/docs/gateway/agent/config/) which allows traffic only from a specific IP: ```yaml title="ngrok.yml" theme={null} endpoints: - name: device-api url: $YOUR_DOMAIN traffic_policy: on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - "203.0.113.0/24" upstream: url: 8080 protocol: http1 ``` ## Mutual TLS (mTLS) For environments that require client certificate verification, ngrok supports mutual TLS. Only clients presenting a valid certificate can connect to your device endpoints. See the [mTLS Termination](/docs/gateway/domains/mtls-termination) documentation for configuration details. ## Combining security controls Layer multiple security controls on a single endpoint. For example, combine IP restrictions with OAuth authentication: ```yaml title="ngrok.yml" theme={null} endpoints: - name: device-api url: $YOUR_DOMAIN traffic_policy: on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - "203.0.113.0/24" - type: oauth config: provider: google upstream: url: 8080 protocol: http1 ``` This example will work out of the box, but it uses ngrok's managed OAuth application. This means you won't be able to customize the provider's authentication behavior. For full control, [use your own OAuth application](/docs/gateway/traffic-policy/actions/oauth#using-a-custom-provider). ## Compliance ngrok maintains certifications and controls to help you pass your security review: | Certification | Details | | - | - | | SOC 2 Type II | Independently audited | | HIPAA & BAA | Business Associate Agreements available | | GDPR | EU data protection compliance | | CCPA | California consumer privacy | | EU-US DPF | Data Privacy Framework | Additional security features include [RBAC](/docs/iam/rbac), [SSO & SCIM](/docs/iam/sso), [audit logs](/docs/obs/events), [DDoS protection](/docs/gateway/domains/ddos-protection), and [data residency](/docs/gateway/domains/region-pinning/) options. Visit the [ngrok Trust Center](https://trust.ngrok.com/) for details. ## Where does traffic travel? When a device connects, the ngrok agent opens an outbound TLS connection to the ngrok edge. Traffic from your cloud services or users flows through the ngrok edge and is forwarded to the device through this encrypted tunnel. By default, ngrok does not store or persist request bodies or response data. (The opt-in [Traffic Inspector](/docs/obs/traffic-inspection/) Full Capture mode does store request and response headers and bodies for debugging purposes.) * **Encryption in transit:** All traffic between the ngrok edge and the agent is encrypted with TLS. * **Scoped access:** Each endpoint exposes one specific service on the device—not the entire network behind it. * **Data residency:** Control where traffic is processed and is available using [Region pinning](/docs/gateway/domains/region-pinning/), available on [Pay-as-you-go](/docs/pricing-limits/#pay-as-you-go-faqs) plans and above. Visit the [ngrok Trust Center](https://trust.ngrok.com/) for audit reports, compliance documentation, and answers to security review questions. ## Next steps * [Traffic Policy](/docs/gateway/device-gateway/traffic-policy): add authentication, rate limiting, and access control to your endpoints. * [Fleet management](/docs/gateway/device-gateway/fleet-management): manage multi-tenancy and credentials at scale * [Full walkthrough](/docs/gateway/device-gateway/agent): see security controls in action in a detailed example scenario # Traffic Policy for Devices Source: https://ngrok.com/docs/gateway/device-gateway/traffic-policy Authenticate, rate limit, and control access to your devices at ngrok's edge using Traffic Policy. [Traffic Policy](/docs/gateway/traffic-policy/) lets you configure access controls once and enforce them on every device in your fleet. No firmware pushes, OTA updates, or truck rolls required to update security rules. Use Traffic Policy to: * **Authenticate and restrict traffic before it reaches your devices.** Require JWTs, OAuth, or specific IP ranges so only authorized traffic can reach your fleet, and rate limit noisy consumers before they overwhelm a device. * **Access services, not entire devices.** Unlike VPNs, ngrok exposes exactly the service each consumer needs and not a single port or process more. Use [internal endpoints](/docs/gateway/endpoints/internal-endpoints/) combined with [Cloud Endpoints](/docs/gateway/endpoints/cloud-endpoints/) to control which services are accessible and to whom, and to [forward and load balance traffic](/docs/gateway/endpoints/cloud-endpoints/forwarding-and-load-balancing/) across them. * **Update policy without touching devices.** When a new security requirement comes in, update one Traffic Policy and the change applies across your entire fleet immediately. No need to push updates to individual devices. ## Restrict access by IP Limit access to specific IP addresses or CIDR ranges: The following [Traffic Policy](/docs/traffic-policy) configuration demonstrates how to restrict access to specific IP addresses using the `restrict-ips` action. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_tcp_connect: - actions: - type: restrict-ips config: enforce: true allow: - 1.1.1.1/32 deny: - 110.0.0.0/8 ``` ```json policy.json theme={null} { "on_tcp_connect": [ { "actions": [ { "type": "restrict-ips", "config": { "enforce": true, "allow": [ "1.1.1.1/32" ], "deny": [ "110.0.0.0/8" ] } } ] } ] } ``` This configuration will ensure that only requests from the IP `1.1.1.1` are allowed, while requests from the IP `110.0.0.0` are denied. #### Example request If the request comes from an allowed IP, the response will proceed as normal. If the request comes from a denied IP, the connection will be closed: ```bash theme={null} $ telnet 5.tcp.ngrok.io 22984 Trying... Connected to 5.tcp.ngrok.io. Connection closed by foreign host. ``` See the [restrict-ips action reference](/docs/gateway/traffic-policy/actions/restrict-ips/) for details. ## JWT validation Require a valid JWT token on every request: The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration is an example configuration of the `jwt-validation` action. For a more real-world example, check out the [Auth0 guide](/docs/integrations/jwt-validation/auth0/). #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: jwt-validation config: issuer: allow_list: - value: https://example.com/issuer audience: allow_list: - value: urn:example:api http: tokens: - type: access_token method: header name: Authorization prefix: 'Bearer ' - type: it+jwt method: body name: _id_token jws: allowed_algorithms: - RS256 - ES256 keys: sources: additional_jkus: - https://example.com/issuer/jku ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "jwt-validation", "config": { "issuer": { "allow_list": [ { "value": "https://example.com/issuer" } ] }, "audience": { "allow_list": [ { "value": "urn:example:api" } ] }, "http": { "tokens": [ { "type": "access_token", "method": "header", "name": "Authorization", "prefix": "Bearer " }, { "type": "it+jwt", "method": "body", "name": "_id_token" } ] }, "jws": { "allowed_algorithms": [ "RS256", "ES256" ], "keys": { "sources": { "additional_jkus": [ "https://example.com/issuer/jku" ] } } } } } ] } ] } ``` #### Example request ```bash theme={null} $ curl --request GET \ --url http://example-api.ngrok.app/ \ --header 'authorization: Bearer ' ``` ```http theme={null} HTTP/2 200 OK content-type: text/html ... ``` In this example, a request is sent to the API with a valid JWT token in the `Authorization` header with the `Bearer` prefix and getting back a `200 OK` response. See the [jwt-validation action reference](/docs/gateway/traffic-policy/actions/jwt-validation/) for full configuration options. ## OAuth Require login via Google, GitHub, Microsoft, or other providers: ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: google client_id: '{your oauth client id}' client_secret: '{your oauth client secret}' scopes: - https://www.googleapis.com/auth/userinfo.profile - https://www.googleapis.com/auth/userinfo.email ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google", "client_id": "{your oauth client id}", "client_secret": "{your oauth client secret}", "scopes": [ "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email" ] } } ] } ] } ``` See the [OAuth action reference](/docs/gateway/traffic-policy/actions/oauth/) for full configuration options. ## Rate limit by host header Prevent a misbehaving integrator, customer app, or internal service from overwhelming your devices: The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `rate-limit` action to rate limit all incoming requests by the `Host` header. #### Example Traffic Policy document ```yaml policy.yml {10} theme={null} on_http_request: - actions: - type: rate-limit config: name: Only allow 30 requests per minute algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - 'hasReqHeader(''host'') ? getReqHeader(''host'')[0] : ''unknown''' ``` ```json policy.json {13} theme={null} { "on_http_request": [ { "actions": [ { "type": "rate-limit", "config": { "name": "Only allow 30 requests per minute", "algorithm": "sliding_window", "capacity": 30, "rate": "60s", "bucket_key": [ "hasReqHeader('host') ? getReqHeader('host')[0] : 'unknown'" ] } } ] } ] } ``` For this example, assume that ngrok is pointing at the upstream service [https://httpbin.org](https://httpbin.org). #### Example request ```bash theme={null} $ curl https://httpbin.ngrok.app/get ``` ```http theme={null} HTTP/2 429 retry-after: 24 ``` In this example, a connection attempt to `httpbin.ngrok.app` using the `curl` command returns a `429` status code with a `retry-after` header indicating the number of seconds to wait before retrying the request. See the [rate-limit action reference](/docs/gateway/traffic-policy/actions/rate-limit/) for details. ## Forward traffic to internal services Route public traffic to internal endpoints so devices are never directly exposed. This example configuration will set up a public endpoint (`forward-internal-example.ngrok.app`) forwarding all traffic it receives to a internal endpoint (`example.internal`) that forwards the request to port `80` on your local machine. Since it is forwarding all traffic to the internal endpoint, no traffic will be sent to `8080` which is the upstream port for the public endpoint. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://example.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "forward-internal", "config": { "url": "https://example.internal" } } ] } ] } ``` #### Start an internal endpoint ```bash theme={null} ngrok http 80 --url example.internal ``` #### Start public endpoint with Traffic Policy ```bash theme={null} ngrok http 8080 --url forward-internal-example.ngrok.app --traffic-policy-file /path/to/policy.yml ``` ### Example request ```bash theme={null} $ curl https://forward-internal-example.ngrok.app -v ... > GET / HTTP/2 > Host: forward-internal-example.ngrok.app > User-Agent: curl/[version] > Accept: */* ... ``` This request will be forwarded to the internal endpoint `https://example.internal` which will then forward the request to port `80` on your local machine. ```http theme={null} GET / HTTP/1.1 Host: forward-internal-example.ngrok.app User-Agent: curl/[version] Accept: */* X-Forwarded-For: [ngrok IP] X-Forwarded-Host: forward-internal-example.ngrok.app X-Forwarded-Proto: https Accept-Encoding: gzip ``` See the [forward-internal action reference](/docs/gateway/traffic-policy/actions/forward-internal/) for details. ## Next steps * [Traffic Policy reference](/docs/gateway/traffic-policy/): explore all available actions * [Security](/docs/gateway/device-gateway/security): credential management, mTLS, and compliance * [Cloud-to-device](/docs/gateway/device-gateway/cloud-to-device): configure internal and Cloud Endpoints for your device services # Secure Tunnels for Devices Source: https://ngrok.com/docs/gateway/device-gateway/tunnels Learn how ngrok creates outbound-only encrypted tunnels from devices in remote networks to the cloud. ngrok establishes secure tunnels between devices in remote networks and the ngrok cloud. These tunnels allow your devices to be reachable from the internet without opening inbound ports, changing firewall rules, or configuring VPNs. ## How tunnels work When you run `ngrok http ` on a device, the ngrok agent opens an outbound TLS connection on port `443` to the ngrok cloud service. ngrok assigns a public URL and routes incoming requests to this url through the encrypted tunnel to the device. * **Outbound-only connections:** The agent connects outbound on port 443. No firewall changes or port forwarding required on the remote network. * **TLS encryption:** All traffic between the ngrok cloud and the agent is encrypted with TLS. * **Automatic HTTPS:** ngrok provisions and manages TLS certificates for your endpoints automatically. ## How this differs from a VPN With a VPN, the network owner at the remote site must configure and maintain a VPN server, open inbound firewall ports, and grant the connecting party access to the entire network segment—not just the service they need. Across many customer sites, that's a per-site IT coordination problem that scales badly. ngrok flips this model. The agent at the remote site connects outbound, requiring no firewall changes, no IT coordination at the remote network, and no broad network access. Each endpoint exposes exactly one service, so a compromised credential reaches one device, not your customer's entire network. ## Supported protocols | Protocol | Command | Use case | | - | - | - | | HTTP/S | `ngrok http ` | Web dashboards, REST APIs, webhooks | | TLS | `ngrok tls ` | TLS passthrough for custom certificates | | TCP | `ngrok tcp ` | SSH, RDP, databases, industrial protocols | Your technicians keep using their existing RDP clients, SSH terminals, and thick-client apps. Protocols like Modbus and other proprietary protocols work through TCP tunnels. ## One agent per gateway You don't need an agent on every sensor or controller. Run one agent on a single gateway to reach every device on the local network through [internal endpoints](/docs/gateway/endpoints/internal-endpoints/). See [the Remote Access docs](/docs/gateway/device-gateway/remote-access#one-gateway-for-an-entire-network) for a full example. ## Embed tunnels with the SDK For tighter integration, embed tunnels directly into your software with the [ngrok SDK](/docs/agent-sdks/). Ship connectivity inside your product rather than alongside it. ## Next steps * [Quickstart](/docs/gateway/device-gateway/quickstart): create your first device tunnel in minutes * [Cloud-to-device](/docs/gateway/device-gateway/cloud-to-device): connect cloud services to devices through internal and Cloud Endpoints * [Installation guides](/docs/gateway/device-gateway/linux): platform-specific setup for Linux, ARM64, Raspberry Pi, and Windows # Install ngrok on a remote Windows device Source: https://ngrok.com/docs/gateway/device-gateway/windows Learn how to install ngrok on a remote Windows device to provide secure access and management. ngrok allows you to create secure ingress to any app, device, or service without spending hours learning arcane networking technologies. You can get started with a single command or a single line of code. What is ngrok? ngrok is an all-in-one cloud networking platform that secures, transforms, and routes traffic to services running anywhere. In this guide, you'll walk through the process of installing the ngrok agent on a remote Windows device, ensuring the agent runs integrated into your operating system, restricting traffic to trusted origins, and integrating traffic events with your preferred logging tool. ## 1. Install the ngrok Agent To download and install the ngrok agent on your remote Windows device, follow these steps: 1. Open a terminal into your remote Windows device. 2. Download the latest ngrok binary for your Windows distribution. You can find the correct binary on the [ngrok download page](https://download.ngrok.com): Select your operating system, select the version, and copy the link that appears in the **Download** button. Below is an example using PowerShell: ```bash theme={null} Invoke-WebRequest -Uri https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-windows-amd64.zip -OutFile ngrok-v3-stable-windows-amd64.zip ``` Equinox is fully owned by ngrok and used exclusively for building and distributing ngrok binaries. See [the FAQ page](/docs/faq#does-ngrok-own-bin-equinox-io) for more details. 1. Unzip the downloaded file and move it to a directory in your PATH. Below is an example using PowerShell: ```bash theme={null} Expand-Archive .\ngrok-v3-stable-windows-amd64.zip -DestinationPath .\ ``` 1. Open a PowerShell and run the following command: ```bash theme={null} ngrok authtoken NGROK_AUTHTOKEN ``` **Note**: Replace `NGROK_AUTHTOKEN` with your unique ngrok authtoken found in the [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). ## 2. Enable SSH access To enable Remote Desktop Protocol (RDP) access to your device via ngrok: 1. Test that the ngrok agent is configured correctly by starting a TCP tunnel on your remote device to enable you to connect through RDP. TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. If you get a different error, ensure your authtoken is configured correctly. ```bash theme={null} ngrok tcp 3389 ``` 1. The ngrok agent assigns you a TCP address and port. Use these values to test the RDP access via ngrok by running the Remote Desktop Protocol app on your local Windows desktop. Below is an example using PowerShell: ```bash theme={null} Start-Process "$env:windir\system32\mstsc.exe" -ArgumentList "/v:NGROK_TCP_ADDRESS:NGROK_PORT" ``` **Note**: Replace the variables in the command line with the following: * NGROK\_TCP\_ADDRESS: The address of the ngrok agent (that is, if the agent shows `tcp://1.tcp.ngrok.io:12345`, your TCP address is `1.tcp.ngrok.io`. * NGROK\_PORT: The port number of the ngrok agent (that is, if the agent shows `tcp://1.tcp.ngrok.io:12345`, your port number is `12345`. ## 3. Adding IP restrictions Once you confirmed that you have connectivity to the device, add some security so that you are the only one who can access it. **Note**: This capability requires ngrok's **IP Restrictions** feature, which is only available with a paid subscription. 1. On the remote Windows device PowerShell terminal, stop the ngrok process using the `ctrl+c` command. 2. Add an allow rule to restrict access to your Windows device to an IP address or a range of IP addresses. ```bash theme={null} ngrok tcp 3389 --cidr-allow ALLOWED_IP_ADDRESS_CIDR ``` **Note**: Replace `ALLOWED_IP_ADDRESS_CIDR` with the CIDR notation for the allowed IP Address(es) (that is, `123.123.123.0/24`). Alternatively, you can create an IP policy in the ngrok dashboard (under [Security > IP Restrictions](https://dashboard.ngrok.com/ip-restrictions)), and leverage the same policy to control access to your entire device fleet. ## 4. Configure ngrok to recover on outages The ngrok agent works with native OS services like `systemd`. This helps you ensure that the ngrok service is available even after the machine restarts. Before doing this, it's useful to reserve a TCP address in the ngrok dashboard which allows you to reuse the same address each time the device is restarted. 1. Navigate to the ngrok Dashboard and access [Gateway > TCP Addresses](https://dashboard.ngrok.com/tcp-addresses). Create a new TCP address with a description and click **Save**. Your new TCP address will look something like `1.tcp.ngrok.io:12345`. Update the ngrok config file in your Windows device to start the ngrok agent using this TCP address. 1. Open the ngrok config file: ```bash theme={null} ngrok config edit ``` 1. Add the following to the end of the file and then save it: ```yaml theme={null} endpoints: - name: device-ssh url: tcp://NGROK_TCP_ADDRESS upstream: url: 3389 traffic_policy: on_tcp_connect: - actions: - type: restrict-ips config: allow: - ALLOWED_IP_ADDRESS_CIDR ``` **Note**: Make sure to replace **NGROK\_TCP\_ADDRESS** with the address you reserved earlier in the ngrok dashboard (that is, `1.tcp.ngrok.io:12345`) and **ALLOWED\_IP\_ADDRESS\_CIDR** with the CIDR notation of the allowed IP Address(es) (that is, `123.123.123.0/24`). **Note**: Make note of the location of the `ngrok.yml` file (that is, `%LocalAppData%\ngrok\`). 1. Enable ngrok in service mode: ```bash theme={null} ngrok service install --config "%LocalAppData%\ngrok\ngrok.yml" ``` You may need to run this command in a PowerShell terminal with administrator privileges. 1. Run the following command to ensure your operating system launches ngrok with the ssh ingress whenever your device starts: ```bash theme={null} ngrok service start ``` You may need to run this command in a PowerShell terminal with administrator privileges. 1. With ngrok running on your device, you should be able to RDP into the device using the reserved address from the dashboard. ```bash theme={null} Start-Process "$env:windir\system32\mstsc.exe" -ArgumentList "/v:NGROK_TCP_ADDRESS:NGROK_PORT" ``` ## What's next? Now that your device is integrated to ngrok, you can ​​execute tasks at the ngrok dashboard to operationalize your fleet: ### Logging traffic from ngrok Each action that happens in ngrok is published as a log, and [Log Exporting](/docs/obs/) allows you to subscribe to the logs that are relevant to you and write them to one or more destinations. A Log Export is made up of a set of log sources (some of which can be filtered), and log destinations. Each export can send logs to one or more destinations, such as Amazon CloudWatch Logs, Amazon Kinesis (as a data stream), or Amazon Kinesis Firehose (as a delivery stream). Log Exports can be configured through the [ngrok Dashboard](https://dashboard.ngrok.com/log-exporting) or the [ngrok API](/docs/api-reference/eventdestinations). You can also forward all or some of your traffic logs from [ngrok to your preferred logging tool](/docs/obs/). ### Remote checks, stop, start, and updates ngrok provides [APIs](/docs/api-reference/tunnelsessions/restart) and a [dashboard UI](https://dashboard.ngrok.com/agents) for you to monitor the health of ngrok agents running in your fleet. The interfaces also allow you to remotely stop, start, and update agents. # How to Use A Custom Domain Source: https://ngrok.com/docs/gateway/domains/custom-domains Learn how to use your own custom domain with your ngrok endpoints. This guide shows you how to use any domain name you already own, such as `app.your-domain.com`, with ngrok. ngrok is not a domain registrar; you must already own a domain name to use it with ngrok. Custom domains are only available on the [Pay-as-you-go](https://ngrok.com/pricing) plan. ## 1. Add your domain in ngrok On your dashboard's [Domain page](https://dashboard.ngrok.com/domains), click the **New Domain** button to add your domain to your ngrok account. After it's been added, you'll be given a `CNAME` value such as `exampledata.otherdata.ngrok-cname.com`. Copy this for the next step. You can also add a domain via the [ngrok API](/docs/api-reference/reserveddomains/list). The `CNAME` will be in the `"cname_target"` field of [the response object](/docs/api-reference/reserveddomains/list). ## 2. Configure DNS for your domain For a subdomain such as `app.example.com`, navigate to your DNS provider's dashboard and create a CNAME record in the `example.com` zone. (Check your provider's support content if you're unsure how to do this.) When configuring the record, use the following values: * **type**: `CNAME` * **name**: `app` (or `app.example.com` if your provider requires the full hostname) * **value**: the `CNAME` target you copied from ngrok For an apex domain such as `example.com`, follow the [apex domain instructions](#apex-domains) to configure an ALIAS, ANAME, or flattened CNAME record instead. A standard CNAME record cannot be used at a DNS zone apex. Using a tool like [`dig`](https://www.digwebinterface.com/) or [`nslookup`](https://www.nslookup.io/), you can test your DNS records in the terminal. For subdomains, check the CNAME record; for apex domains, check the A and AAAA responses, since aliasing or flattening returns IP addresses instead of a CNAME record. ## 3. Start an endpoint using your domain To start an Agent Endpoint at your domain, run the following command in your terminal, replacing `your-domain-here.example.com` with the domain you added in the ngrok dashboard: ```bash theme={null} ngrok http --url your-domain-here.example.com 8080 ``` ## 4. Wait for the TLS certificate to provision After you add a custom domain, ngrok provisions a TLS certificate for it from an ACME provider such as Let's Encrypt. This typically completes within a few minutes once your DNS records are resolvable, but can take longer depending on DNS propagation and the certificate authority. While the certificate is still provisioning, ngrok terminates HTTPS connections to your domain using a default ngrok wildcard certificate that does **not** match your custom hostname. If you start an endpoint and open the custom domain in a browser before the certificate is ready, you'll see a certificate warning indicating that the hostname doesn't match the certificate. This is expected during the provisioning window and resolves on its own as soon as the certificate is issued. There's no action to take other than waiting and reloading. You can see whether your certificate is still provisioning, ready, or blocked on a DNS error in two places: * **Dashboard**: open the [Domains page](https://dashboard.ngrok.com/domains) and select your domain. The certificate status panel shows the current provisioning state and any error. * **API**: call [`GET /reserved_domains/{id}`](/docs/api-reference/reserveddomains/get) and read `certificate_management_status.provisioning_job`. The `status` field tells you whether provisioning is in progress, valid, or has hit a recoverable or user-correctable error, and `retries_at` tells you when ngrok will try again. If `provisioning_job` reports a DNS error, double-check that the DNS records from [step 2](#2-configure-dns-for-your-domain) are published and resolvable with `dig` or `nslookup`. For wildcard custom domains, both CNAME records described in [TLS certificate provisioning](/docs/gateway/domains/tls-certificates/#wildcard-domains) must exist before the certificate can be issued. ## Wildcard domains If you bring your own [wildcard domain](/docs/domains#wildcard-domains) (such as `*.example.com`), you must create a second `CNAME` record with your domain host for [wildcard TLS Certificate provisioning](/docs/gateway/domains/tls-certificates/#wildcard-domains). ## Apex domains An apex domain is the root of a DNS zone, such as `example.com`; `app.example.com` is a subdomain. ngrok supports apex domains with DNS providers that offer ALIAS, ANAME, or CNAME flattening. Check that your provider supports pointing these records to external hostnames. Add your apex domain in ngrok as described in [step 1](#1-add-your-domain-in-ngrok). In your DNS provider, create an ALIAS, ANAME, or flattened CNAME record at the apex, using `@` or your provider's equivalent for the name. Point the record to the `CNAME` target ngrok supplies for your domain. Your DNS provider resolves this target and returns A or AAAA records. Once DNS is configured correctly, ngrok automatically provisions a TLS certificate as described in [step 4](#4-wait-for-the-tls-certificate-to-provision). If your DNS provider doesn't support aliasing or flattening to ngrok's target, use a subdomain such as `app.example.com` with a standard CNAME record, or move your domain's DNS hosting to a compatible provider. You can keep your domain registered with your existing registrar. ## Using custom domains with TCP endpoints Public TCP endpoints are assigned randomly on an ngrok-controlled hostname with a randomly assigned port. You cannot choose the hostname or select the port. However, you can simulate a customized hostname by creating a `CNAME` record to the hostname of your assigned TCP address. If you do so, be aware that all ports on that hostname—even those provisioned to other accounts—will then be available on your domain. For example, if your TCP address is `5.tcp.ngrok.io:12345`, you could create the following `CNAME` record: ```bash theme={null} CNAME tcp.mydomain.com -> 5.tcp.ngrok.io ``` Then you can access that TCP endpoint with: ```bash theme={null} telnet tcp.mydomain.com 12345 ``` # DDoS Protection Source: https://ngrok.com/docs/gateway/domains/ddos-protection Learn about ngrok's automatic DDoS protection features that protect your endpoints from distributed denial of service attacks. ngrok automatically protects your applications with out-of-the-box protection from distributed denial of service (DDoS) attacks. ## DDoS firewall The ngrok cloud service automatically protects all Endpoints from attacks with its proprietary DDoS Firewall. The DDoS Firewall scans traffic flows into your endpoints for malicious actors, patterns, and threats in real-time. When an attack is detected, the firewall proactively blocks incoming connections from the attackers IPs. ## Additional measures In addition to ngrok's out-of-the-box DDoS Firewall, also consider taking the following measures to help protect your endpoints from attacks: 1. Prevent attacks by enforcing authentication with Traffic Policy actions. Traffic Policy is enforced in the ngrok cloud service so that only legitimate traffic is sent to the upstream service in your network. ngrok's cloud service absorbs all of the unauthenticated traffic. You can use the following Traffic Policy actions to block unauthenticated traffic: * [Basic Auth](/docs/gateway/traffic-policy/actions/basic-auth) * [OAuth](/docs/gateway/traffic-policy/actions/oauth) * [IP Restriction](/docs/gateway/traffic-policy/actions/restrict-ips/) * [Webhook Verification](/docs/gateway/traffic-policy/actions/verify-webhook/) * [JWT](/docs/gateway/traffic-policy/actions/jwt-validation/) * [Mutual TLS](/docs/gateway/traffic-policy/actions/terminate-tls/) * [OpenID Connect](/docs/gateway/traffic-policy/actions/oidc/) 2. Use the Traffic Policy [Circuit Breaker action](/docs/gateway/traffic-policy/actions/circuit-breaker/) on your [Agent Endpoints](/docs/gateway/endpoints/agent-endpoints). This module protects your upstream applications when they become overloaded by blocking traffic to them in ngrok's cloud service until they can recover. # Dedicated IPs Source: https://ngrok.com/docs/gateway/domains/dedicated-ips Configure static, dedicated IP addresses for your domains and agent connections, exclusively assigned to your account. This feature is in Early Access and under active development. Behavior, supported fields, and limits may change before General Availability (GA). This guide provides forward-looking context for evaluation and feedback. [Visit your dashboard](https://dashboard.ngrok.com/early-access) to sign up for Early Access, or to send your feedback via the "Let us know" link. Dedicated IPs let you configure static IP addresses that are exclusively assigned to your account for receiving traffic to your [domains](/docs/gateway/domains/) and [agent connections](/docs/gateway/agent/connect-url/). By default, ngrok uses a shared, multi-tenant pool of dynamic IP addresses that may change frequently and without notice. Dedicated IPs provide: * **Static addresses** - IP addresses that don't change, making it easier to configure firewall rules and allowlists * **Account exclusivity** - IP addresses used only by your account, providing additional isolation * **Custom port support** - When used with domains, create [endpoints](/docs/gateway/endpoints/) on any inbound port (not restricted to ports 80 and 443) * **Protocol support** - Available for any [ngrok-supported protocol](/docs/gateway/endpoints/protocols/), including HTTP/S, TCP, and TLS ## Use cases Dedicated IPs are valuable for: * **Firewall allowlisting** - Your customers can allowlist specific IPs in their firewalls to permit traffic from your services * **Compliance requirements** - Meet security policies that require traffic from known, static IP addresses * **Network monitoring** - Simplify logs and traffic analysis with consistent source IPs * **Custom connect URLs** - Secure agent connections with account-specific IPs (see [Agent Dedicated IPs](#agent-dedicated-ips)) ## Dedicated IPs for domains ### How it works When you configure a domain with dedicated IPs, all matching [endpoints](/docs/gateway/endpoints/) for that domain receive traffic on your dedicated IP addresses instead of ngrok's shared multi-tenant IPs. Dedicated IPs and [Region Resolution](/docs/gateway/domains/region-pinning/) (region pinning) share the same `resolves_to` field on the domain resource: * Region pinning: `resolves_to: [{ "value": "" }]` * Dedicated IPs: `resolves_to: [{ "value": "" }]` You can combine both region pinning and dedicated IPs in the same configuration. ### Configuration Configure dedicated IPs through the [Domains API](/docs/api-reference/reserveddomains/) using the `resolves_to` field. Dedicated IPs are provisioned by ngrok after you sign up for Early Access. Contact [ngrok Sales](mailto:sales@ngrok.com?subject=Dedicated+IPs) to obtain your dedicated IP addresses before configuring your domains. #### Create a domain with dedicated IPs The format for dedicated IPs in the `resolves_to` field is `:`. The port is optional but can be specified if you want to receive traffic on a specific port. ```json theme={null} curl --request POST \ --url https://api.ngrok.com/reserved_domains \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" \ --header "ngrok-version: 2" \ --data '{ "domain": "api.example.com", "description": "Production API with dedicated IPs", "certificate_management_policy": { "authority": "letsencrypt", "private_key_type": "ecdsa" }, "resolves_to": [ { "value": "192.0.2.1:1234" } ] }' ``` #### Combine dedicated IPs with region pinning ```json theme={null} { "resolves_to": [ { "value": "us-cal-1" }, { "value": "192.0.2.1:1234" } ] } ``` #### Update an existing domain ```json theme={null} curl --request PATCH \ --url https://api.ngrok.com/reserved_domains/{domain_id} \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" \ --header "ngrok-version: 2" \ --data '{ "resolves_to": [ { "value": "192.0.2.1:1234" } ] }' ``` ### Custom ports When using dedicated IPs, you can create endpoints on any port, not just 80 and 443: ```bash theme={null} # Standard ports (available without dedicated IPs) ngrok http 8080 --url https://api.example.com # Custom ports (requires dedicated IPs) ngrok http 8080 --url https://api.example.com:8443 ngrok tcp 22 --url tcp://api.example.com:2222 ``` ## Agent Dedicated IPs Dedicated IPs are also available for [Agent Connect URLs](/docs/gateway/agent/connect-url/#dedicated-ips), providing account-unique IP addresses for your agents to connect to ngrok's service. This enhances security by: * Allowing you to allowlist only your dedicated connect IPs in your network * Preventing rogue agents from calling home to unexpected IPs * Providing white-labeling with a custom connect URL domain and dedicated IPs See [Agent Connect URL documentation](/docs/gateway/agent/connect-url/#dedicated-ips) for more information. ## Dedicated IPs pricing Dedicated IPs are available as an add-on feature for Pay-as-you-go plan customers. | Feature | Availability | Pricing | | - | - | - | | Dedicated Domain IPs | Early Access | Contact Sales | | Dedicated Agent Connect IPs | Available | \$900/month per region ([contact Sales](mailto:sales@ngrok.com?subject=Dedicated+Ingress+IPs)) | See the [Pricing page](https://ngrok.com/pricing) for details. ## Related resources * [Domains](/docs/gateway/domains/): Configure domains for your endpoints * [Region & IP Resolution](/docs/gateway/domains/region-pinning/): Pin traffic to specific geographic regions * [IP Addresses](/docs/gateway/domains/ip-addresses/): Learn about ngrok's IP addressing and dynamic IPs * [Agent Connect URLs](/docs/gateway/agent/connect-url/): Configure custom connect URLs for agents * [Traffic Policy](/docs/gateway/traffic-policy/): Apply rules and transformations to your traffic ## API reference * [Reserved Domains API](/docs/api-reference/reserveddomains/) ## FAQs ### How do I get dedicated IPs? Dedicated IPs are in Early Access. [Sign up for access](https://dashboard.ngrok.com/early-access) through your ngrok dashboard. ### Can I use dedicated IPs with TCP endpoints? Yes, dedicated IPs work with all [ngrok-supported protocols](/docs/gateway/endpoints/protocols/), including HTTP/S, TCP, and TLS. ### What's the difference between dedicated IPs and region pinning? * **Region pinning** restricts which ngrok Points of Presence handle your traffic for compliance and data residency requirements * **Dedicated IPs** provide static IP addresses exclusively for your account You can use both features together. ### Do dedicated IPs support IPv6? Currently, dedicated IPs for domains are IPv4 only. IPv6 support may be added in the future. ### How many dedicated IPs do I get? The number of dedicated IPs available depends on your configuration and requirements. Contact [ngrok Sales](mailto:sales@ngrok.com?subject=Dedicated+IPs) to discuss your needs. # Global Load Balancer Source: https://ngrok.com/docs/gateway/domains/global-load-balancer Learn about ngrok's Global Load Balancer for distributing traffic to the nearest healthy point of presence for improved performance. ## Overview ngrok's Global Load Balancer improves the performance and resiliency of your applications by distributing traffic to the nearest healthy point of presence, measured by latency, from the perspective of the connecting client. Every application you deliver with ngrok is automatically accelerated with the always-on, zero-configuration global load balancer. Global lobal balancing is sometimes called 'Global Server Load Balancing' or GSLB. Conceptually, a GSLB is simple. A GSLB accelerates traffic by adjusting DNS resolution so that when clients connect to your endpoints on the internet, they connect to the nearest point of presence (PoP). A GSLB adds resiliency by adjusting that same DNS resolution process to steer traffic away from failing PoPs. ## ngrok versus traditional GSLBs ngrok operates differently from a traditional GSLB to give you some important benefits. 1. In a traditional GSLB deployment, you are responsible for running geographically redundant copies of your application and configuring networking technologies to distribute and failover traffic among these deployments. By contrast, ngrok runs a [global delivery network of PoPs](/docs/gateway/points-of-presence/) for you. This means that even if you only deploy your upstream service to a single geography, your apps still get faster from [Connection Acceleration](#connection-acceleration) and [Module Acceleration](#module-acceleration). 2. Unlike a traditional GSLB deployment, when you deploy your application services to a new geographic location, ngrok automatically routes nearby traffic to those services without any configuration. Traditional GSLB deployments require many network configuration updates to bring these new locations online like IP provisioning and DNS record changes. With ngrok, there is zero configuration, just start your new application services with their ngrok agents and you're done. 3. Unlike a traditional reverse proxy, ngrok forwards traffic to your upstream services over secure connections established by the [ngrok agent](/docs/gateway/agent/), [Agent SDKs](/docs/agent-sdks) or [Kubernetes Controller](/docs/gateway/k8s/). So which PoPs do those agents connect to? ngrok also applies GSLB principles to those connections as well to ensure that your agents connect to the geographically closest PoPs. ## Endpoints ngrok's GSLB improves the performance and resiliency of connections to your ngrok endpoints (for example, `https://your-app.ngrok.app`). ### Connection acceleration When a client (like a web browser) connects to your ngrok endpoint it is routed to the closest point of presence. DNS resolution of your endpoint's address returns the IP addresses of ngrok's closest point of presence. The closest point of presence is determined by the shortest latency from the resolving DNS server to the points of presence. This ensures that clients are routed to the fastest point of presence even as internet routing conditions change. Connecting to the closest point of presence accelerates your traffic by reducing the initialization time for TCP and TLS connections. TCP and TLS connection set up requires network round-trips. ngrok accelerates connections by reducing the latency of these round-trip times (RTTs) between the client and your endpoint by routing them to the closest point of presence. ### Module acceleration In addition to accelerating connection initialization, ngrok also accelerates the execution of [Modules](/docs/gateway/traffic-policy/actions/) that you configure on your endpoints. Modules execute where the request is first received by the ngrok edge, at the geographically closest point of presence. This means that any behaviors you define in your modules are automatically globally accelerated for all customers. ### Geo-aware load balancing When you use [endpoint pooling](/docs/gateway/endpoints/endpoint-pooling), ngrok will automatically distribute traffic to the closest upstream if you run geographically distributed copies of your upstream application or service. First, consider a case *without* geo-aware load-balancing. For example, you have instances of your application running in Japan. When someone in Belgium makes a request to your app's endpoint, that request will first be routed to the closest ngrok point of presence in Europe, like Frankfurt. From Frankfurt, it will then be routed through ngrok's internal network and finally to your upstream services in Japan. Then, for example, you start additional copies of your application service in France. With zero changes in configuration, those same requests from Belgium will still be routed to ngrok's point of presence in Frankfurt, but instead of routing to your upstream services in Japan, they will instead be routed to your new upstreams in France. That's Geo-Aware Load Balancing. ### Losing an upstream Consider the example in [Geo-Aware Load Balancing](#geo-aware-load-balancing) where you are running upstream application services in both Japan and France. If all of your upstream services in Japan fail, requests that were being routed to your Japanese upstream services will instead be routed to your remaining upstream services in France. Requests that were already being routed to your upstreams in France will be unaffected. ### Losing a point of presence In [losing an upstream](#losing-an-upstream), a case was considered where your upstream services fail. But what if an ngrok point of presence fails? If an ngrok point of presence fails, health checks automatically detect that failure and update DNS resolution such that clients will attempt to connect to your endpoints via the next-closest healthy point of presence. ### Disabling GSLB for an endpoint There is no way to disable GSLB for HTTPS and TLS endpoints. All endpoints you create in ngrok are available on ngrok's worldwide global delivery network and inbound requests and connections are routed to the geographically closest point of presence. ### TCP endpoints TCP Endpoints do not support GSLB. Unlike domains, TCP addresses are provisioned for a specific point of presence. When you create a TCP Address, you must select the point of presence where it will accept traffic. Unlike ngrok's globally routed domains, traffic to TCP addresses always enters ngrok's network from that point of presence. ## Agents ngrok's GSLB automatically improves the performance and resiliency of your ngrok agents, agent SDKs and the Kubernetes Controller. Agents automatically connect to ngrok's geographically closest healthy point of presence. ### Acceleration Similar to endpoints, when an ngrok agent connects, DNS resolution returns the IPs of the closest ngrok point of presence. Connecting to the closest ngrok point of presence reduces agent initialization time and also reduces the latency of connections that are routed to that agent. ### Losing a point of presence If an ngrok point of presence fails, health checks automatically detect that failure and update DNS resolution such that agents will attempt to connect to the next-closest healthy point of presence. The ngrok agent connects to a single region at a time. If you want the ngrok agent to simultaneously connect to multiple regions, you must instead run multiple ngrok agents and [explicitly disable agent GSLB](#agents-disabling-gslb). ### Disabling GSLB in the Agent You should always prefer to allow the ngrok agent to use GSLB and connect to the closest point of presence. However, the ngrok agent does support disabling GSLB and explicitly choosing which point of presence to connect to. If you wish to configure the agent to use a specific point of presence you may do so with the following configuration: ### Legacy Agent behavior Beginning with the [ngrok v3 agent](/docs/gateway/agent/changelog#ngrok-agent-300---2022-03-28), the agent uses GSLB to connect to the lowest-latency point point of presence. Prior to that, v2 ngrok agents would always connect to the U.S. region by default. # Domains Source: https://ngrok.com/docs/gateway/domains/index Learn how to connect domains to your ngrok endpoints. ## Domains Domains enable you to create public endpoints with hostnames matching the domain. For example, after you create the domain `your-name.ngrok.app`, you may create the Endpoint `https://your-name.ngrok.app` Domain names may be a subdomain of an [ngrok-managed domain](#ngrok-managed-domains) like `foo.ngrok.app` or you can [bring your own domain](#branded-domains) like `example.your-domain.com` by creating a CNAME DNS record with your domain's DNS provider. Domains also enable you configure other domain-level behaviors including: * [**Global Load Balancer configuration**](#global-load-balancer) - Choose which points of presence handle traffic to a Domain's matching endpoints. * [**TLS Certificate management**](#tls-certificates) - Configure automatic certificate provisioning or select a certificate you uploaded yourself. * [**Dedicated IP address configuration**](/docs/gateway/domains/dedicated-ips/) - Attach static, dedicated IPs for a Domain's matching endpoints receive traffic on. You can manage Domains on your [ngrok Dashboard](https://dashboard.ngrok.com/domains) or via the [ngrok API](#api). ## Public endpoints A Domain's primary responsibility is to enable you to create [public endpoints](/docs/gateway/endpoints/public-endpoints/) with a hostname matching the domain. These are called "matching endpoints". For example, after you create the Domain `app.example.com`, you can create the Endpoint `https://app.example.com`. When you create a Domain, you may create matching public endpoints with the following constraints: | Endpoint Protocol | Allowed Endpoints | | - | - | | `http` | Matching endpoints on port `80` of the Domain. | | `https` | Matching endpoints on port `443` of the Domain. | | `tls` | Matching endpoints on port `443` of the Domain. | | `tcp` | Not allowed. Public TCP endpoints must match a [TCP Address](/docs/gateway/domains/tcp-addresses/). | If you configure your Domain to use [dedicated IPs](/docs/gateway/domains/dedicated-ips/), these restrictions are removed and you may create matching endpoints on any ports. ### Dev domains Every ngrok account comes with a free Dev Domain that can be used if you don't want to pick a domain. Its URL is automatically generated and cannot be changed. You can use this domain for creating a public endpoint to host your app, API, or any other service you'd like to put online. Dev domains are not billed for endpoint hours and do not count toward the domain limits of paid plans. On the **free plan**, you can only use your automatically assigned dev domain for public endpoints. You cannot choose or reserve custom domain names—that capability requires a paid plan. ### Wildcard domains You may create a Domain with a wildcard name, for example, `*.example.com`. A wildcard domain enables you to: * Create an endpoint which receives traffic for all of its subdomains, for example, `https://*.example.com`. Consult the documentation for [wildcard endpoints](/docs/gateway/endpoints/http/#wildcard-endpoints) to understand the rules for matching and precedence. * Create an endpoint on any subdomain which matches the wildcard, for example, `https://foo.example.com` or `https://foo.bar.baz.example.com` The wildcard `*` character may only be used as the first part of a domain, you may not create domains like `app.*.example.com` or `*-app.example.com`. Wildcard endpoints are available on our Pay-as-you-go plan self service. For billing information on wildcard endpoints, including how endpoint hours and Traffic Policy are charged when using wildcard Cloud Endpoints that forward to internal endpoints, see the [wildcard endpoints pricing documentation](/docs/pricing-limits/#wildcard-endpoints). Reserving subdomains of a wildcard domain within the ngrok dashboard count towards the number of reserved domains in your account. For example, if you reserve `foo.example.com` and `*.example.com`, you have reserved two domains. ### Auto-assigned domains For some applications, you may not care much about the actual domain for the endpoint. Rather than picking one yourself, you can use the value `https://` in the `url` field to have ngrok automatically assign your endpoint your account's [dev domain](#dev-domains): ```bash theme={null} ngrok http 80 --url 'https://' ``` The dev domain is a stable, auto-assigned hostname, so you get a usable public URL for your endpoint without reserving one yourself. ### Ownership Your account exclusively owns its Domains and all of their nested subdomains within ngrok. That means: * No account may create a Domain with a name that is a subdomain of your Domains. * No other account may create endpoints with a hostname matching your Domain or any of its nested subdomains. For example, if you create a Domain with the name `foo.ngrok.app`: * No other account create the Domains `foo.ngrok.app` or `bar.foo.ngrok.app`. * No other account may create the endpoints `https://foo.ngrok.app` or `https://bar.foo.ngrok.app`. ## Bring your own domain You can use any custom domain name that you already own with ngrok, for example, `app.your-domain.com`. To do so, see [the docs on using custom domains](/docs/gateway/domains/custom-domains). ## Global Load Balancer The [Global Load Balancer](/docs/gateway/domains/global-load-balancer) uses latency-aware DNS records to direct clients to the IPs of the nearest [points of presence](/docs/gateway/points-of-presence/). Domains allow you to configure which points of presence the Global Load Balancer may resolve clients to. This allows you to select which points of presence will receive traffic for the Domain's matching endpoints. To disable the Global Load Balancer, you may configure a domain to only resolve a single point of presence. Per-region global load balancer configuration is coming soon, [request Early Access](https://dashboard.ngrok.com/early-access). ## TLS certificates Domains manage the [TLS Certificate](/docs/gateway/domains/tls-certificates/) used to terminate TLS connections to the Domain's matching endpoints. When you create a Domain, you may choose to allow ngrok to automatically provision TLS certificates for you or to upload your own TLS certificate. Consult the documentation on [TLS Certificates](/docs/gateway/domains/tls-certificates/) for additional details on certificate provisioning and management. ## Dedicated IPs By default, a Domain's matching Endpoints receive traffic on a set of multi-tenant [IP addresses](/docs/gateway/domains/ip-addresses) that are shared among all ngrok accounts. You may instead configure a Domain's matching endpoints to receive traffic on IP addresses that are dedicated to your account. If your Domain uses dedicated IPs, you may create public endpoints on *any* port numbers, not just 80 and 443. See the [Dedicated IPs](/docs/gateway/domains/dedicated-ips/) documentation for detailed information on configuration, use cases, and pricing. Dedicated, static IPs for your domains are in Early Access. [Request Early Access](https://dashboard.ngrok.com/early-access). ## ngrok managed domains If you don't own a domain that you want to use with ngrok, you can create a Domain that is a subdomain of an ngrok-managed base domain. The ngrok-managed base domains are: | Domain | Availability | HSTS Global Preload | | - | - | - | | `ngrok.app` | Available to paying accounts | Yes | | `ngrok.dev` | Available to paying accounts | Yes | | `ngrok.pizza` | Available to paying accounts | No | | `ngrok-free.app` | Used by free accounts | Yes | | `ngrok-free.dev` | Used by free accounts | Yes | | `ngrok-free.pizza` | Used by free accounts (coming soon) | No | | `ngrok.io` | Discontinued and only available to older accounts | No | ### Public suffix list The ngrok-managed base domains are on the [Public Suffix List](https://publicsuffix.org/). Browsers use the Public Suffix List to guarantee that cookies from one subdomain cannot be accessed by other subdomains. ### HSTS preload Some of the ngrok-managed base domains are on the global [HSTS](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) preload list. If the domain of the URL is on the [global HSTS preload list](https://hstspreload.org/), modern browsers automatically convert requests with an `http` scheme to `https`. If you need to create unencrypted `http` endpoints, you should create them with hostnames that are not on the HSTS Global Preload like `ngrok.io` and `ngrok.pizza`. ngrok does not prohibit you from creating `http` endpoints with hostnames whose base domain is on the global HSTS preload list because non-browser clients (like `curl`) can still be used to make unencrypted HTTP requests to them. ## API Domains are programmatically managed via: * [`/reserved_domains` API Resource](/docs/api-reference/reserveddomains/list) ## Domains pricing Domains are available on all plans. Some Domain features require upgrades. See the [Pricing page](https://ngrok.com/pricing) for details. | Feature | Plans | | - | - | | Domains | All plans. The Domain name is automatically assigned on Free; you may choose it on paid plans. | | Bring-your-own domains | Pay-as-you-go | | Wildcard endpoints | Pay-as-you-go | | Auto-assigned domain (`https://`) | All plans. Assigns your account's dev domain to the endpoint. | # IP Addresses Source: https://ngrok.com/docs/gateway/domains/ip-addresses Learn about ngrok's dynamic IP addresses, dedicated IPs, IPv6 support, and global load balancing for endpoints and agent connections. ngrok's cloud service uses a dynamic, rotating set of multitenant IPs to serve your public Endpoints. Endpoint IPs may change frequently ***and without notice***. If you hardcode any of ngrok's IPs or rely on DNS answers past their TTL, your applications ***will break***. The information on this page about IPs also applies not only to Endpoint IPs but also to the IPs used for [Agent Connections](/docs/gateway/agent/connect-url/), the Dashboard, and the [API](/docs/api/). They each support Dedicated IPs, IPv6, and Global Load Balancing. ## Dedicated IPs For scenarios requiring static IP addresses, ngrok offers dedicated IPs that are exclusively assigned to your account. These can be configured for [Domains](/docs/gateway/domains/dedicated-ips/) and [Agent Connect URLs](/docs/gateway/agent/connect-url/#dedicated-ips). Dedicated, static IPs for public endpoints are in Early Access. [Request Early Access](https://dashboard.ngrok.com/early-access). ## IPv6 ngrok's cloud service universally supports IPv6. DNS queries for the hostnames of its public endpoints return `AAAA` DNS records. Don't forget that ngrok supports IPv6 when using the [`restrict-ips` Traffic Policy action](/docs/gateway/traffic-policy/actions/restrict-ips/). If you forget to allow IPv6 traffic, you may unintentionally cause connectivity failures if connections to your Endpoints use IPv6. ## Global Load Balancer When you resolve Endpoint IPs via DNS, you will receive a partial list of IPs because ngrok's [global load balancer](/docs/gateway/domains/global-load-balancer/) will return a different set of IPs depending on where you are located in the world. ## API There is no supported API to query ngrok's IPs at this time. # mTLS Termination at the ngrok edge Source: https://ngrok.com/docs/gateway/domains/mtls-termination Learn how to enable Mutual TLS (mTLS) termination at the ngrok edge using the terminate-tls Traffic Policy action. Mutual TLS Authentication (mTLS) is a network security protocol that ensures both the client *and* server authenticate each other using digital certificates, providing encrypted and trusted communication between both parties. This is in contrast to standard TLS, where only the server is authenticated by the client. By verifying the clients, the server owner is able to restrict access only to verified clients, strengthening security. This guide configures mTLS termination at the ngrok edge using a Traffic Policy. The ngrok cloud verifies the client certificate and terminates TLS before forwarding traffic on to your agent. If you want the ngrok **agent** to terminate TLS and verify client certificates itself, see [Agent TLS Termination](/docs/gateway/agent/agent-tls-termination#optional-require-client-certificates-mtls-at-the-agent) instead. The Traffic Policy shown below has the same effect whether attached to a local agent endpoint or to a cloud endpoint—either way, termination happens at the edge. Diagram showing the 7-step mutual TLS handshake between a client and the ngrok edge, including certificate exchange and verification This guide walks you through enabling mTLS on your ngrok hosted endpoints using [the `terminate-tls` Traffic Policy action](/docs/gateway/traffic-policy/actions/terminate-tls/). ## What you'll need * Ensure you have [openssl](https://docs.openiam.com/docs-4.2.1.3/appendix/2-openssl) installed. ## 1. Provision your certificates and credentials Although ngrok can [automatically provision your certificates](/docs/gateway/domains/tls-certificates) when you use standard TLS, you'll have to provide your own TLS certificate authority (CA) for mTLS. The CA is responsible for issuing, digitally signing, and verifying the authenticity of client certificates. Keep in mind: 1. ngrok uses the CA certificate to verify the client certificate. 2. Once the CA signs a client certificate, the client certificate is used to grant the client access your endpoints. 3. ngrok does not generate CA and client certificates for you when you use mTLS; you must generate them yourself. 4. Though your CA certificate must be uploaded and hosted on the ngrok SaaS platform, your client certificates need to be distributed to any client or device that needs access to your endpoints. ### Generate your own certificates Most organizations have their own certificate management infrastructure, so this step is optional if you already have a CA and client certificates to work with. To begin, create a new CA that will be used to sign the client certificate, as shown below. With this approach you can generate multiple client certificates that are trusted by the CA if needed. ```bash theme={null} # 1. Generate CA private key (ca.key) openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:2048 # 2. Generate CA certificate (ca.crt) openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=ExampleCA" # 3. Generate client private key (client.key) openssl genpkey -algorithm RSA -out client.key -pkeyopt rsa_keygen_bits:2048 # 4. Generate client certificate signing request (CSR) (client.csr) openssl req -new -key client.key -out client.csr -subj "/CN=ExampleClient" # 5. Generate client certificate (client.crt) openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256 ``` These commands result in five new files: `ca.key`, `ca.crt`, `client.key`, `client.csr`, and `client.key`. You will use the contents of `ca.crt` in your Traffic Policy to validate requests via `curl`, which, in turn, uses `client.key` and `client.crt`. ## 2. Create a Traffic Policy Using the CA certificate generated in the previous step, you can specify the `mutual_tls_certificate_authorities` field in a Traffic Policy to trust the CA that issued the client certificate. ```yaml policy.yml theme={null} on_tcp_connect: - actions: - type: terminate-tls config: mutual_tls_certificate_authorities: - |- -----BEGIN CERTIFICATE----- ... certificate ... -----END CERTIFICATE----- ``` ```json policy.json theme={null} { "on_tcp_connect": [ { "actions": [ { "type": "terminate-tls", "config": { "mutual_tls_certificate_authorities": [ "-----BEGIN CERTIFICATE-----\n... certificate ...\n-----END CERTIFICATE-----" ] } } ] } ] } ``` You may also optionally [upload the CA certificate](https://dashboard.ngrok.com/tls-cert-authorities) in the ngrok dashboard and use its ID in the `mutual_tls_certificate_authorities` array. ## 3. Start an endpoint with the Traffic Policy ```bash theme={null} ngrok http 8080 --url $YOUR_DOMAIN --traffic-policy-file /path/to/policy.yml ``` ## 4. Test your endpoint Test out that everything works by making a request to your endpoint using the `--cert` and `--key` flags to specify the client certificate and private key. ```bash theme={null} curl --cert client.crt --key client.key https://$YOUR_DOMAIN ``` # Region & IP Resolution Source: https://ngrok.com/docs/gateway/domains/region-pinning Customize where your traffic physically connects to ngrok's network, to meet data residency needs (such as keeping data within the EU). ## Region pinning Region & IP resolution lets you restrict (or "pin") which ngrok regions will handle traffic for a domain. Configure your [domain](/docs/domains) to resolve to the IP addresses of one (or several) of ngrok's globally distributed physical points of presence (PoPs). This feature is designed for customers with data residency and compliance requirements who need to ensure traffic is processed only within specific geographic boundaries. The same configuration will soon support [dedicated IP addresses](/docs/gateway/domains/dedicated-ips/) \[preview feature], in addition to Points of Presence. ## How it works 1. Choose which ngrok points of presence your domain can use. 2. All requests to [endpoints](/docs/endpoints) on that domain will *only* be routed through your chosen PoPs. Flowchart showing users in US, EU, and Asia all routing through a domain to the nearest allowed ngrok Point of Presence from a pinned set of regions ## Configuring region resolution To configure (or "pin") which regions will resolve your traffic: update your domain resource with your preferred set of target regions. You can do this through the API or the dashboard, both of which are described below. ### Using the API The `resolves_to` property tells your domain how to resolve requests. You can specify a list of ngrok physical PoPs (like "Frankfurt" or "California"), [dedicated IPs](/docs/gateway/domains/dedicated-ips/), or both. The following sections describe how to create, update, and un-pin a domain. #### Create a pinned domain You can create a new domain and customize region "pinning" (Region Resolution) in a single step. Use the [`POST /reserved_domains` endpoint](/docs/api-reference/reserveddomains/create) and include the optional `resolves_to` property. ```json theme={null} curl --request POST \ --url https://api.ngrok.com/reserved_domains \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" \ --header "ngrok-version: 2" \ --data '{ "domain": "api.example.com", "description": "Pinned to US and Europe for compliance reasons", "certificate_management_policy": { "authority": "letsencrypt", "private_key_type": "ecdsa" }, "resolves_to": [ { "value": "us-cal-1"}, { "value": "eu"} ] }' ``` #### Update an existing domain You can add Region Resolution settings to an existing domain. You can also modify the existing list, for example, by adding a third point of presence (example below). No DNS changes are required. Use `PATCH` to [update the list of pinned regions](/docs/api-reference/reserveddomains/update). ```json theme={null} curl --request PATCH \ --url https://api.ngrok.com/reserved_domains/{domain_id} \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" \ --header "ngrok-version: 2" \ --data '{ "domain": "api.example.com", "description": "Pinned to US and Europe for compliance reasons", "certificate_management_policy": { "authority": "letsencrypt", "private_key_type": "ecdsa" }, "resolves_to": [ { "value": "us-cal-1"}, { "value": "eu"}, { "value": "jp"} ] }' ``` #### Un-pin a domain You can "un-pin" a domain. Removing Regional Resolution settings restores the domain to its *default* IP resolution behavior: [global load balancing](https://ngrok.com/docs/domains/global-load-balancer) across *all* ngrok points of presence. Use `PATCH` to [remove the list of pinned regions](/docs/api-reference/reserveddomains/update), by setting the `resolves_to` property to an empty list. ```json theme={null} # Revert to Global Routing curl --request PATCH \ --url https://api.ngrok.com/reserved_domains/{id} \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" \ --header "ngrok-version: 2" \ --data '{ "resolves_to": [] }' ``` #### API error handling If a request contains invalid syntax or an invalid region code, the API request fails. The request returns **HTTP 400 Bad Request** with a structured error body, as shown in the example below: ```json theme={null} HTTP/1.1 400 Bad Request Content-Type: application/json; charset=utf-8 { "status_code": 400, "error_code": "invalid_region_target", "msg": "The region you specified, 'banana' is invalid. use one of the following acceptable values: us,us-cal-1,eu,au,ap,jp,sa,in", "details": {} } ``` ### Using the dashboard You can also make these changes through the [ngrok dashboard](https://dashboard.ngrok.com/domains). To configure your Region Resolution targets: 1. View [your existing domains in the dashboard](https://dashboard.ngrok.com/domains), or create a new one. 2. Click the domain you wish to modify. 3. Enter a comma-separated list of PoPs, region aliases that cover multiple PoPs, or both. ## Dedicated IPs (preview) This feature is in Early Access and under active development. Behavior, supported fields, and limits may change before General Availability (GA). This guide provides forward-looking context for evaluation and feedback. [Visit your dashboard](https://dashboard.ngrok.com/early-access) to sign up for Early Access, or to send your feedback via the "Let us know" link. Dedicated IPs let you configure a domain to receive traffic on static IP addresses that are exclusively dedicated to your account (instead of ngrok's shared multi-tenant IPs). When a domain uses dedicated IPs, you can create matching [endpoints](/docs/gateway/endpoints/) on any inbound port. Today, you are [restricted](/docs/faq/#can-i-choose-a-different-port-to-receive-traffic-on-my-ngrok-domains) to ports 80 and 443. ### How to use dedicated IPs Dedicated IPs and Regional Resolution ("region pinning") share the same `resolves_to` field on the domain. See the [Dedicated IPs](/docs/gateway/domains/dedicated-ips/) documentation for configuration examples, use cases, and pricing: * Region pinning: `resolves_to: [{ "value": "" }]` * Dedicated IPs: `resolves_to: [{ "value": "" }]` You can include both, as shown in the example below. ```json theme={null} { "resolves_to": [ { "value": "us-cal-1" }, { "value": "8.8.8.8:1234" } ] } ``` ## Frequently asked questions ### Does this affect TCP endpoints? Slightly different answer for each sub-feature: * Region pinning: no, since this feature applies only to matching HTTP/HTTPS endpoints. TCP uses [reserved addresses](/docs/api-reference/reservedaddrs), which already include a region. * Dedicated IPs: yes, dedicated IPs are available for any [ngrok-supported protocol](/docs/gateway/endpoints/protocols), including HTTP/S, TCP, and TLS. ### What happens if the `resolves_to` field isn't added when creating a domain? Nothing. That is, it uses the standard domain behavior: global load balancing, which automatically routes traffic to the best point of presence for that end-user. This is the baseline behavior for domains, and is used by the vast majority of customers. ### Is it necessary to specify every region manually? No, ngrok provides regional aliases as a shortcut to select all PoPs in a given geographic area. Aliases are in Preview (see list below). Initial regions will include `european-union` and `united-states`, depending on [customer feedback (click "Let us know")](https://dashboard.ngrok.com/early-access). ## Points of presence reference The `resolves_to` parameter uses the following PoP names: | **Geographical region** | **Location** | **Code (to use in API)** | | - | - | - | | **United States** | Ohio | `us` | | | California | `us-cal-1` | | **Europe** | Frankfurt | `eu` | | **Asia/Pacific** | Singapore | `ap` | | | Sydney | `au` | | | Mumbai | `in` | | | Tokyo | `jp` | | **South America** | São Paulo | `sa` | | **Regional Aliases** | Global | `global` | | | Europe *\[coming soon]* | `european-union` | | | United States *\[coming soon]* | `united-states` | ## Limits and pricing This feature is only available on the Pay-as-you-Go plan. [Learn more about ngrok plans](https://ngrok.com/pricing). You can have up to 100 region-pinned domains. This is the same as our total domain limit, so most customers should be fine. Feel free to reach out via support if you need more. [Learn more about limits](https://ngrok.com/docs/pricing-limits) Region pinning costs **\$0.02 per active endpoint hour** (in addition to the standard endpoint rate), per endpoint on a "pinned" domain. An endpoint is considered active if it receives any traffic in a given hour. If your endpoints with pinning enabled don't receive any traffic, you won't incur charges for this feature. [Learn more about pricing](https://ngrok.com/pricing). # TCP Addresses Source: https://ngrok.com/docs/gateway/domains/tcp-addresses Learn how to create and manage TCP addresses, enabling you to use fixed hostnames and ports for your TCP endpoints. ## TCP Addresses TCP Addresses enable you to create public [TCP Endpoints](/docs/gateway/endpoints/tcp/) on a fixed address. TCP Addresses are a host and port tuple, for example `1.tcp.eu.ngrok.io:12345`. You can manage TCP Addresses on your [ngrok Dashboard](https://dashboard.ngrok.com/tcp-addresses) or via the [ngrok API](#api). ## Public endpoint creation A TCP Address's primary responsibility is to enable you to create public TCP endpoints with a hostname and port matching the address. These are called "matching endpoints". For example, after you create the TCP Address `1.tcp.jp.ngrok.io:12345`, you can create the Endpoint `tcp://1.tcp.jp.ngrok.io:12345`. Only TCP endpoints may be created on TCP Addresses. To create other endpoint types, create a [Domain](/docs/gateway/domains/). ## Address assignment The hostname and port of the TCP Address are randomly assigned when you create it, after which it will not change. You may not choose the hostname or port. If you delete a TCP Address, there is no way to provision the same one again. TCP Addresses may be managed via the dashboard and via API. The port of your TCP Address is uniquely assigned to your account, but other ports on that same hostname may be assigned to other accounts. While the hostname and port of a TCP Address is fixed, they do not have static IPs. ## Global Load Balancer TCP Addresses are not supported by the [global load balancer](/docs/gateway/domains/global-load-balancer). You must select a [Point of Presence](/docs/gateway/points-of-presence) when you create a TCP Address. Connections to matching TCP Endpoints on a TCP Address will always route to the TCP Address's point of presence, no matter where those connections originated in the world. You may create Agent Endpoints from an agent connected to a different point of presence than the matching TCP Address. For example, the following will work properly: ```sh theme={null} ngrok tcp 3389 --region jp --url tcp://1.tcp.eu.ngrok.io:12345 ``` ## API TCP Addresses are programmatically managed via: * [`/reserved_addrs` API Resource](/docs/api-reference/reservedaddrs/list) ## TCP Addresses pricing TCP Addresses are available on the Hobbyist and Pay-as-you-go plans. Both plans include 1 TCP Address. Each additional address is billed at \$0.005 per hour, which can be paid for with the usage credit included in your plan. Pay-as-you-go accounts may reserve up to 100 TCP Addresses. [Contact us](https://ngrok.com/contact) if you need more. TCP Addresses are not available on the free plan. Free accounts may create TCP endpoints on randomly assigned addresses after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification)—see [TCP endpoint pricing](/docs/gateway/endpoints/tcp/#pricing). # TLS Certificates Source: https://ngrok.com/docs/gateway/domains/tls-certificates Learn how ngrok manages TLS certificates automatically, or how you can manually configure them yourself. ngrok automatically handles TLS (SSL) certificate management and termination for you. There is typically nothing to set up, configure, or manage. You can manage TLS certificates on your [ngrok dashboard](https://dashboard.ngrok.com/tls-certs) or via the [ngrok API](#api). ## Overview TLS certificates are a tuple of a private key and certificate that are presented by the ngrok cloud service during a TLS handshakes to terminate TLS connections to [HTTPS endpoints](/docs/gateway/endpoints/http) and [TLS endpoints](/docs/gateway/endpoints/tls). TLS certificates are attached to [domains](/docs/gateway/domains/) for termination of TLS connections to endpoints whose URLs match that domain. ngrok can automatically provision certificates for you or you can upload your own. See the [TLS Termination](/docs/gateway/domains/tls-termination/) documentation for details on how and when ngrok terminates TLS connections. ## Certificate selection When TLS connections to an endpoint are terminated, ngrok uses the TLS Certificate attached to the [domain](/docs/domains) matching the endpoint's URL for termination. Each [domain](/docs/gateway/domains/) has an attached TLS certificate that is referenced by its [`certificate_id` field](/docs/api-reference/reserveddomains/get#response-certificate-id). If the domain object does reference any certificate, or if there is no matching domain object for an endpoint's URL, ngrok selects its own TLS certificate for termination. You may override the certificate used for TLS termination by explicitly terminating TLS yourself with the [`terminate-tls` Traffic Policy action](/docs/gateway/traffic-policy/actions/terminate-tls/). ## Certificate provisioning Whenever you create a [domain](/docs/gateway/domains/) you choose how a TLS certificate will be provisioned. Either: * [Automatic TLS certificates](#automated) (default) - ngrok automatically provisions a TLS certificate from an ACME provider like Let's Encrypt. * [Bring your own TLS certificates](#custom) - Upload your own TLS certificate It is recommended that you choose automatic TLS certificates unless you have special requirements. ## Automatic provisioning When you choose automatic TLS certificate management for a domain (this is the default), ngrok automatically provisions **and renews** TLS certificates from an ACME-compliant certificate authority like Let's Encrypt. ngrok uses an HTTP01 challenge for TLS certificate provisioning of most domains. When you create a [wildcard domain](#wildcard-domains) ngrok uses a DNS01 challenge instead. ngrok is a [Let's Encrypt sponsor](https://letsencrypt.org/sponsors/). ### Status TLS certificates are provisioned asynchronously. Depending on DNS propagation and the speed of the ACME certificate authority, provisioning typically takes from a few seconds up to several minutes, and occasionally longer if a DNS challenge is slow to validate. The domain's `certificate_management_status` field on the [Reserved Domain API Resource](/docs/api-reference/reserveddomains/get) contains the status of the provisioning job, including the current job state, the time it started, and when ngrok will next retry if it has hit a recoverable error. You can also see this status on the [Domains page](https://dashboard.ngrok.com/domains) of your dashboard. ### Expected behavior while a certificate is provisioning While a certificate is still being provisioned, ngrok terminates HTTPS connections to the domain using a default ngrok wildcard certificate. Because that certificate is not issued for your hostname, browsers and other TLS clients will show a certificate warning indicating that the hostname doesn't match the certificate if they connect to your endpoint before provisioning finishes. This is expected, and clears automatically as soon as the certificate is issued, usually within a few minutes. If the warning persists, check `certificate_management_status.provisioning_job` on the reserved domain (or the cert status panel on the dashboard) to see whether the job is still in progress or has hit an error that requires action, such as a missing or misconfigured CNAME record. ### Bring your own domain When you [bring your own domain](/docs/gateway/domains/#branded-domains) (such as `api.your-domain.com`) you must create a CNAME DNS record in your DNS provider. Certificate provisioning will not begin until your create this CNAME DNS record. ### Wildcard domains When you bring your own wildcard domain (such as `*.app.example.com`), ngrok uses a DNS01 challenge for TLS certificate provisioning which means that you must create two CNAME records when creating branded wildcard domains instead of just one. For example, if your domain is `*.app.example.com`, you will be required to create the following two CNAME records: * `*.app.example.com` * `_acme-challenge.app.example.com` Certificate provisioning will not begin until you have created both DNS records. ### Private keys When using automatic TLS certificates, ngrok generates a private key for your domain and encrypts it at rest with [NaCL](https://nacl.cr.yp.to/). When you configure a domain to use automated certificates, you may electively choose whether ngrok will create an ECDSA private key (the default) or RSA private key. If you choose an RSA private key, it is created with a size of 4096 bits. ## Bring your own certificates You may upload your own TLS certificates if you don't want to use the TLS certificates that ngrok automatically provisions for you. Uploading your own certificates may be the right choice if you are issuing certificates from your own certificate authority or if you are using an EV certificate. To bring your own TLS certificate, upload a [certificate bundle](#certificate-bundles) and a [private key](#private-keys). Keep in mind that after you create your TLS certificate, you must attach it to one or more domains before it will be used for termination. When working with the API, this is done by updating the `certificate_id` property of the [Domain API Resource](/docs/api-reference/reserveddomains/get#response-certificate-id). Unlike automatic TLS certificates, when you are using custom TLS certificates, you are responsible for managing the renewal and rotation of new certificates. ### Certificate bundles When uploading your own TLS certificates to ngrok, you are expected to provide a certificate bundle of all certificates necessary to establish a chain of trust to a trusted root certificate authority. Many TLS certificate vendors will provide you with a constructed certificate bundle, but some will return the leaf certificate and the intermediate certificates separately and you must concatenate them to construct the bundle yourself. This is the `certificate_pem` field in the [TLS Certificate Create API](/docs/api-reference/tlscertificates/list/). Certificate bundles are a series of PEM-encoded X.509 certificates that have been concatenated together. **The order of this bundle is significant.** The first certificate in the bundle must be the leaf certificate. The leaf certificate is the one which is signed for your domain and [the private key](#private-keys) you will upload. After the leaf certificate are the intermediates certificates, if any. Each intermediate certificate signs the certificate preceding it in the bundle. As an example, the first intermediate will sign the leaf, and that signature is part of the leaf certificate itself. The final certificate will be signed by the root certificate authority. You may also included the root certificate in the bundle as well, but it is not necessary or common practice to do so. A certificate bundle will look like the following: ```pem theme={null} -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- ``` ### Private keys ngrok accepts the following formats for the private key of an uploaded TLS certificate. This is the `private_key_pem` field in the [TLS Certificate Create API](/docs/api-reference/tlscertificates/create/). * RSA, in either PKCS#1 or PKCS#8 form. * ECDSA, in either SEC 1 or PKCS#8 form. * Ed25519, in PKCS#8 form. Regardless of the format you choose, the private key must be formatted as ASN.1 DER, encoded as PEM. ngrok does not accept any private keys that are encrypted (for example, with DES). ngrok encrypts your uploaded private keys at rest with [NaCL](https://nacl.cr.yp.to/). ## API TLS certificates are managed programmatically via: * [`/tls_certificates` API Resource](/docs/api-reference/tlscertificates/get) ## TLS certificates pricing TLS certificates are available on all plans. Bringing your own certificates is available on the Pay-as-you-go plan. Bring your own certificates are billed at a flat rate of \$0.27/hour for any clock hour in which at least one endpoint using that certificate is active. Wildcard domains are not charged extra per matching subdomain. For example, if you upload a certificate for `*.acme.com`, endpoints on both `foo.acme.com` and `bar.acme.com` are covered by the same certificate and billed together as a single \$0.27/hour charge, not billed separately per domain. # TLS Termination Source: https://ngrok.com/docs/gateway/domains/tls-termination Learn how ngrok automatically handles TLS termination for you, and enables you to manage it yourself. ngrok automatically handles TLS (SSL) termination and certificate management for you. There is typically nothing to set up, configure, or manage. ## Overview ngrok's TLS termination behavior is determined by an endpoint's protocol and Traffic Policy. You may customize each endpoint to choose where TLS is terminated, how it is terminated and even whether it is terminated at all. When ngrok's cloud service terminates TLS, it: * Uses latest and most secure version of TLS * Uses the [TLS Certificate](/docs/gateway/domains/tls-certificates/) attached to the [Domain](/docs/gateway/domains/) which matches the Endpoint URL's hostname * Accelerates your traffic by using the global load balancer to terminate at its closest point of presence ngrok supports [end-to-end encryption](#end-to-end-encryption) where the ngrok cloud service does not terminate TLS connections and only sees enciphered traffic. When configured this way, you are responsible for configuring TLS termination in your upstream service or at the ngrok agent. ## Termination location TLS connections to your ngrok endpoints are terminated at one of three locations. * **ngrok's cloud service**: This is the easiest and most common. All HTTPS endpoints terminate TLS at ngrok's cloud service. When connections are terminated by ngrok's cloud service, they are re-encrypted before they are transmitted over a Secure Tunnel to an agent. * **ngrok agent**: This is a form of [end-to-end encryption](#end-to-end-encryption) where the ngrok cloud service does not terminate TLS and you instead configure the ngrok agent to terminate TLS connections for you. * **your upstream service**: This is another form of [end-to-end encryption](#end-to-end-encryption) where neither the cloud service nor an agent terminates TLS connections. Instead, your upstream application service is responsible for TLS termination. An endpoint's protocol determines the ngrok cloud service's default TLS termination behavior. | Endpoint Protocol | TLS Termination | | - | - | | [HTTP](/docs/gateway/endpoints/http/) | None | | [HTTPS](/docs/gateway/endpoints/http/) | Always at ngrok's cloud service. | | [TLS](/docs/gateway/endpoints/tls/) | Default no termination, configurable with `terminate-tls` | | [TCP](/docs/gateway/endpoints/tcp/) | Default no termination, configurable with `terminate-tls` | ### Examples #### Cloud service ```bash theme={null} ngrok tls 80 \ --url tls://your-name.ngrok.app \ --traffic-policy-file traffic-policy.yml ``` ##### `traffic-policy.yml` ```yaml theme={null} on_tcp_connect: - actions: - type: terminate-tls ``` ```yaml theme={null} version: 3 endpoints: - name: example url: tls://your-name.ngrok.app upstream: url: 80 traffic_policy: on_tcp_connect: - actions: - type: terminate-tls ``` SSH does not support termination at the edge ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) trafficPolicy := ` on_tls_request: - actions: - type: terminate-tls ` func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Listen(ctx, ngrok.WithURL("tls://your-name.ngrok.app"), ngrok.WithTrafficPolicy(trafficPolicy), ) } ``` * [https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithTrafficPolicy](https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithTrafficPolicy) ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); const fs = require("fs"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, proto: "tls", domain: "app.example.com", crt: fs.readFileSync("/path/to/app-example-com-crt.pem", "utf8"), key: fs.readFileSync("/path/to/app-example-com-key.pem", "utf8"), }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain) * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#crt](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#crt) * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#key](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#key) * [https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#domain](https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#domain) * [https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#termination](https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#termination) ```python theme={null} import ngrok def load_file(name): with open(name, "r") as crt: return bytearray(crt.read().encode()) listener = ngrok.forward("localhost:8080", authtoken_from_env=True, proto="tls", domain="app.example.com", crt=load_file("/path/to/app-example-com-crt.pem"), key=load_file("/path/to/app-example-com-key.pem")) print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/tls\_listener\_builder.html#ngrok.TlsListenerBuilder.domain](https://ngrok.github.io/ngrok-python/tls_listener_builder.html#ngrok.TlsListenerBuilder.domain) * [https://ngrok.github.io/ngrok-python/tls\_listener\_builder.html#ngrok.TlsListenerBuilder.termination](https://ngrok.github.io/ngrok-python/tls_listener_builder.html#ngrok.TlsListenerBuilder.termination) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) The Rust SDK does not support TLS termination at the ngrok edge TLS endpoints are not supported by the ngrok Kubernetes Operator
#### Terminate at Agent See [Zero-Knowledge TLS at the Agent](/docs/gateway/agent/agent-tls-termination/) for additional details. ```bash theme={null} ngrok tls 80 \ --terminate-at agent \ --url tls://app.example.com \ --crt /path/to/app-example-com-crt.pem \ --key /path/to/app-example-com-key.pem ``` ```yaml theme={null} version: 3 endpoints: - name: example url: tls://app.example.com" upstream: url: 80 agent_tls_termination: server_certificate: "/path/to/app-example-com-crt.pem" server_private_key: "/path/to/app-example-com-crt.key" ``` SSH does not support termination at the agent ```go theme={null} import ( "context" "crypto/tls" "net" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { cert, err := tls.LoadX509KeyPair("/path/to/cert.pem", "/path/to/key.pem") if err != nil { return nil, err } tlsConfig := &tls.Config{ Certificates: []tls.Certificate{cert}, } return ngrok.Listen(ctx, ngrok.WithURL("tls://"), ngrok.WithAgentTLSTermination(tlsConfig), ) } ``` The Javascript SDK does not support TLS termination at the agent. The Python SDK does not support TLS termination at the agent. The Rust SDK does not support TLS termination at the agent. TLS endpoints are not supported by the ngrok Kubernetes Operator
#### Terminate at Upstream ```bash theme={null} ngrok tls 443 \ --terminate-at upstream \ --url tls://app.example.com ``` ```yaml theme={null} version: 3 endpoints: - name: example url: tls://app.example.com upstream: url: localhost:443 ``` ```bash theme={null} ssh -R app.example.com:443:localhost:443 v2@connect.ngrok-agent.com tls ``` ```go theme={null} import ( "context" "golang.ngrok.com/ngrok/v2" ) func ngrokForwarder(ctx context.Context) (ngrok.EndpointForwarder, error) { return ngrok.Forward(ctx, ngrok.WithUpstream("tls://localhost:443"), ngrok.WithURL("tls://app.example.com"), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, proto: "tls", domain: "app.example.com", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain) * [https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#domain](https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#domain) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, proto="tls", domain="app.example.com") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/tls\_listener\_builder.html#ngrok.TlsListenerBuilder.domain](https://ngrok.github.io/ngrok-python/tls_listener_builder.html#ngrok.TlsListenerBuilder.domain) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .tls_endpoint() .domain("app.example.com") .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.TlsTunnelBuilder.html#method.domain](https://docs.rs/ngrok/latest/ngrok/config/struct.TlsTunnelBuilder.html#method.domain) TLS endpoints are not supported by the ngrok Kubernetes Operator ## `terminate-tls` The [`terminate-tls` Traffic Policy Action](/docs/gateway/traffic-policy/actions/terminate-tls) enables you to terminate TLS connections at ngrok's cloud service for TCP and TLS endpoints. You may also use this action on HTTPS endpoints to customize how TLS is terminated. When you use the `terminate-tls` action on an HTTPS endpoint, ngrok will skip the default TLS termination step that it runs for all HTTPS endpoints so as not to terminate TLS twice. ## Acceleration The ngrok cloud service improves the performance of your endpoints by accelerating TLS termination using ngrok's [global points of presence](/docs/gateway/points-of-presence). TLS connection set up requires multiple network round-trips. When round-trip times (RTTs) are long, TLS connection establishment slows down. ngrok reduces the latency of these round-trip times between the client and your endpoint by terminating connections at the closest [point of presence](/docs/gateway/points-of-presence/) via its [global load balancer](/docs/gateway/domains/global-load-balancer). ## Certificates When the ngrok cloud service terminates TLS connections, it does so with the [TLS Certificate](/docs/gateway/domains/tls-certificates/) attached to the [Domain](/docs/gateway/domains/) which matches the Endpoint URL's hostname. See the documentation on [TLS Certificates](/docs/gateway/domains/tls-certificates/) for more details on how they are [selected](/docs/gateway/domains/tls-certificates/#certificate-selection), managed, provisioned and renewed. You may customize which TLS certificate is chosen for termination with the [`terminate-tls`](/docs/gateway/traffic-policy/actions/terminate-tls) Traffic Policy action. ## Mutual TLS Mutual TLS is supported when terminating TLS at ngrok's cloud service via the `mutual_tls_certificate_authorities` field of the [`terminate-tls`](/docs/gateway/traffic-policy/actions/terminate-tls) Traffic Policy action. Mutual TLS is supported when terminating TLS at the agent via the `mutual_tls_certificate_authorities` property of the [`agent_tls_termination` section of an endpoint configuration](/docs/gateway/agent/config/v3/#endpoint-configuration-options) in the agent configuration file. ## Handshake ### TLS version ngrok uses TLS 1.3 (the latest version) by default. If a client does not support TLS 1.3, ngrok will use the highest possible version that the client supports, down to TLS 1.2. You may customize the minimum and maximum supported versions of TLS with the [`terminate-tls`](/docs/gateway/traffic-policy/actions/terminate-tls) Traffic Policy action. ### ALPN `https` endpoints negotiate the next protocol via ALPN with the following default list in order of preference: ``` ["h2", "http/1.1"] ``` ### SNI ngrok endpoints do not support legacy clients which do not set the SNI extension. For example, the following clients (and others) will fail to work with ngrok endpoints: * Microsoft Internet Explorer 6.0 * Microsoft Internet Explorer 7 & 8 on Windows XP or earlier * Native browser on Android 2.X * Java \<=1.6 * [Python 2.X, 3.0, 3.1 if required modules are not installed](https://stackoverflow.com/questions/18578439/using-requests-with-tls-doesnt-give-sni-support/18579484#18579484) ### Encrypted client hello ngrok endpoints do not yet support the draft implementation of [Encrypted Client Hello](https://datatracker.ietf.org/doc/draft-ietf-tls-esni/). ## FIPS compliance ngrok does not use a FIPS-compliant TLS implementation by default, but one can be enabled for your endpoints. [Contact Support](mailto:support@ngrok.com) if you require a FIPS-compliant TLS implementation. ## End-to-end encryption You may choose to terminate TLS at your upstream service or at the ngrok agent to achieve end-to-end encryption (E2EE), often referred to as Zero-knowledge TLS. When your endpoints operate in this mode, the ngrok cloud service can not see the payloads that transfer through your endpoints. Creating an endpoint with end-to-end encryption is simple: * Create a TLS or TCP endpoint * **Do not** add a `terminate-tls` action to its Traffic Policy. Read the [Agent TLS Termination Guide](/docs/gateway/agent/agent-tls-termination/) for a step-by-step approach to set it up. To set up the agent to terminate TLS for you, consult the following table because the configuration depends on which kind of agent you are using. | Agent | Documentation | | - | - | | Agent Config File | [`agent_tls_termination`](/docs/gateway/agent/config/v3/#endpoint-configuration-options) | | Go SDK | [WithTLSTerminationKeyPair](https://pkg.go.dev/golang.ngrok.com/ngrok/config#WithTLSTerminationKeyPair) | | Other SDKs | not supported | | Kubernetes Operator | not supported | ## Limits and timeouts # Agent CLI Quickstart Source: https://ngrok.com/docs/gateway/endpoints/agent-cli-quickstart Learn how to get started with ngrok Agent CLI to create secure tunnels from your local applications to the internet. Complete the ngrok Agent CLI quickstart on my machine: 1. Install the ngrok Agent CLI for my operating system 2. Connect my ngrok account by running `ngrok config add-authtoken ` — ask me for the token 3. Start a minimal HTTP server on port 8080 in whatever language is easiest 4. Expose it publicly: `ngrok http 8080` 5. Secure it with Google OAuth by editing ngrok.yml to add a traffic policy 6. Restart with `ngrok start cli-quickstart` and confirm a login prompt appears Ask me for my auth token and reserved domain before starting. The simplest way to get started is to create an Agent Endpoint that forwards public traffic to your localhost using the [ngrok Agent CLI](/docs/gateway/agent/). This quickstart walks you through that process, as well as how to implement basic security measures by requiring visitors to log in with a Google account to access your app. ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/signup). * Your [ngrok auth token](https://dashboard.ngrok.com/get-started/your-authtoken). * On Mac OS, you'll need [homebrew](https://brew.sh/). ## 1. Install the ngrok Agent CLI Run the command that corresponds to your operating system to install the Agent CLI: Install ngrok via [Homebrew](https://brew.sh/). ```bash theme={null} brew install ngrok ``` ```bash theme={null} curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \ | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \ && echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \ | sudo tee /etc/apt/sources.list.d/ngrok.list \ && sudo apt update \ && sudo apt install ngrok ``` Install via the Windows App Store Or [follow the direct installation guide](https://download.ngrok.com?tab=download) if you can't use one of the options above. To test that it's been installed correctly, run the following command in your terminal and confirm that ngrok prints its help text. ```bash theme={null} ngrok help ``` ## 2. Connect your account Connect your agent to your ngrok account by providing your auth token as shown below—replace `$YOUR_TOKEN` with the string given to you [in the dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). ```bash theme={null} ngrok config add-authtoken $YOUR_TOKEN ``` ## 3. Start your app or service Start up the app or service you'd like to put online. This is the app that your Agent Endpoint will forward online traffic to. If you don't have an app to work with, you can create a minimal app in your language of choice using the following code to set up a basic HTTP server at port `8080`. ```javascript title="service.js" theme={null} const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/html' }); const html = ` Test Page

Hello from Node.js HTTP Server!

`; res.end(html); }); const port = 8080; server.listen(port, () => { console.log(`Serving custom HTML at http://localhost:${port}`); }); ``` ```go title="service.go" theme={null} package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go HTTP Server!") } func main() { http.HandleFunc("/", handler) fmt.Println("Starting server at http://localhost:8080") err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Println("Server failed:", err) } } ``` ```python title="service.py" theme={null} from http.server import BaseHTTPRequestHandler, HTTPServer class MyHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() html = b""" Test Page

Hello from Python HTTP Server!

""" self.wfile.write(html) def run(server_class=HTTPServer, handler_class=MyHandler, port=8080): server_address = ('', port) httpd = server_class(server_address, handler_class) print(f"Serving custom HTML at http://localhost:{port}") httpd.serve_forever() if __name__ == "__main__": run() ```
Navigate to the directory where this file is located and start the server. ## 4. Put your app online Start the ngrok agent by running the following command (replace `8080` if your app is running on a different port). ```bash theme={null} ngrok http 8080 ``` All accounts come with a free dev domain that is automatically chosen when you start an endpoint. You can add the optional `--url` flag to customize this domain on paid plans. The agent should print a console UI to your terminal to confirm that it's online and forwarding as intended. Open the forwarding URL in your browser to see your web application, which is now available over HTTPS with a valid certificate that ngrok automatically manages for you. ## 5. Secure your app ngrok makes it simple to add authentication to your app by implementing a [Traffic Policy](/docs/gateway/traffic-policy/). Using the Agent CLI, you can add a Traffic Policy to your endpoint by editing your ngrok config file, which was automatically created when you provided your auth token in step 3. Terminate the Agent Endpoint that you started up in step 5, then run the following command in your terminal to open `ngrok.yml`: ```bash theme={null} ngrok config edit ``` You should see a new console UI with your config version and auth token already set. Below the auth token, paste the following snippet into the editor and then save and exit the config file. This policy states that whenever an HTTP/S request is made to `$YOUR_DOMAIN`, the Agent Endpoint should redirect the user to Google OAuth for authentication before proceeding to the app running on port `8080`. If the email is not from the specified domain, in this case `@acme.com`, the visitor will be denied access. ```yaml title="ngrok.yml" theme={null} endpoints: - name: cli-quickstart url: $YOUR_DOMAIN traffic_policy: on_http_request: - actions: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@acme.com')" actions: - type: deny upstream: url: 8080 protocol: http1 ``` This example uses ngrok's default Google OAuth application. To use your own, see [the OAuth Traffic Policy Action documentation](/docs/gateway/traffic-policy/actions/oauth/#google-example). Now you can start up your endpoint using its name: ```bash theme={null} ngrok start cli-quickstart ``` When you visit your public URL you should be prompted to authenticate by logging in with a Google account before you can access your app. ## What's next? In this guide, you learned how to create an Agent Endpoint to forward traffic from the internet to an app running on your local device using the ngrok agent CLI. You were introduced to some of the most common commands you should know, and you implemented a Traffic Policy, which can be used for many kinds of actions beyond the basic authentication example here. You also saw how to edit your config file to make your Agent Endpoints repeatable and scalable by assigning a name you can refer back to with URLs, protocols, and actions already defined. What else can you do with these features? * If you need to interact with ngrok Agent Endpoints programmatically, use one of the Agent SDKs which are available for [JavaScript (Node.js)](/docs/getting-started/javascript/), [Go](/docs/getting-started/go/), [Python](/docs/getting-started/python/), and [Rust](/docs/getting-started/rust/). * If your use case calls for a centrally managed, always-on endpoint instead of one that is only available when an agent is running, you should proceed to [getting started with Cloud Endpoints](/docs/getting-started/cloud-endpoints-quickstart/). * This quickstart barely scratches the surface of what's possible with a Traffic Policy—check out the [Actions overview](/docs/gateway/traffic-policy/actions/) to see what else you can do. * Visit the [Agent CLI command reference](/docs/gateway/agent/cli/) for a complete list of available commands. # Agent Endpoints Source: https://ngrok.com/docs/gateway/endpoints/agent-endpoints Learn about creating endpoints managed by Secure Tunnel agents. Agent Endpoints are public or internal Endpoints created by Secure Tunnel agents, which include any of the following: * [The Agent CLI](/docs/gateway/agent/) * [The Kubernetes Operator](/docs/gateway/k8s/) * [Agent SDKs](/docs/agent-sdks) in multiple supported languages Agent Endpoints live for the lifetime of, and forward connections to, the agent process that started them. ## Get started To set up your first Agent endpoint, see [the Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/) guide. ## Use cases Agent endpoints are a good choice when you want to: * **Put applications running anywhere online**. * Your app could be running in a anywhere, for example, AWS, Digital Ocean, in a container or a VM, on your laptop, in your home lab, etc. * **Create connectivity to apps running in ephemeral environments like CI**. * **Ensure that an endpoint goes offline when your process or machine is disconnected**. * **You can use Agent Endpoints to create connectivity to apps running on your localhost, an IoT device in the field, or an EC2 instance**. * Because Agent Endpoints are created via [Secure Tunnel agents](/docs/gateway/agent/), they are excellent for connecting to apps running on all networks, even those behind a NAT (including double NAT). ## Differences from Cloud Endpoints See the [Endpoint Types](/docs/gateway/types) page for a full comparison between Agent and Cloud Endpoints. * **Lifetime** - Unlike Cloud Endpoints, Agent Endpoints are online only as long as the agent process that started them remains running and connected to the ngrok cloud service. * **Agent Authoritative** - Agent Endpoints are created, managed and configured by the Secure Tunnels agent that started them. The agent is authoritative about Agent Endpoints it has started and the dashboard or API provides a read-only view of them. Cloud Endpoints, by contrast, are created, managed, and configured centrally by the API or dashboard. * **URLs** - When creating an Agent Endpoint, you may ask the ngrok cloud service to choose a random URL for it. * **Forwarding** - Agent Endpoints implicitly forward their traffic to the agent that started them. * **Traffic Policy** - Traffic Policy is optional for Agent Endpoints. When you do specify a Traffic Policy for an Agent Endpoint, any traffic that is not terminated by the policy is sent to the agent that created the endpoint. * **API** - Agents Endpoints' API representation specifies `type: agent` and its `tunnel_session` reference points to the Secure Tunnels session that it was created by. ## Protocols, binding and pooling * **Protocols** - Agent endpoints support all [Endpoint Protocols](/docs/gateway/endpoints/protocols) (`http`, `https`, `tcp` and `tls`). * **Bindings** - Agent endpoints support all [Endpoint Bindings](/docs/gateway/endpoints/bindings) (`public`, `internal` and `kubernetes`). * **Pooling** - Agent endpoints support [Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling/). They may also pool with themselves and with Cloud Endpoints. ## API Agent Endpoints can be managed programmatically via: * [Agent SDKs](/docs/agent-sdks/) * [Agent API](/docs/gateway/agent/api/) * [Kubernetes Operator](/docs/gateway/k8s/installation/helm) Agent Endpoints are also included in a read-only capacity via: * [`/endpoints` API Resource](/docs/api-reference/endpoints/list) of the [API](/docs/api) ## Agent Endpoints pricing Agent endpoints are available on all plans. Consult the [Endpoints Pricing](/docs/gateway/endpoints/#pricing) documentation for billing details. # Endpoint Bindings Source: https://ngrok.com/docs/gateway/endpoints/bindings Learn about ngrok's public, internal, and kubernetes endpoint bindings. [Endpoints](/docs/endpoints) have a **binding**, which dictates how traffic can access it. ## Public Endpoints Endpoints with a `public` binding have a publicly addressable URL that receives traffic from the internet via the ngrok cloud service's global points of presence. For example, these endpoints might use an ngrok subdomain or a custom domain. Example URLs: * `https://example.ngrok.app` * `https://blog.example.com`. Learn more about [Public Endpoints](/docs/gateway/endpoints/public-endpoints). ## Internal Endpoints Endpoints with an `internal` binding can only receive traffic forwarded to them from other Endpoints in your ngrok account via the [`forward-internal` Traffic Policy action](/docs/gateway/traffic-policy/actions/forward-internal/). Internal endpoints have URLs that end in `.internal`. Example URLs: * `https://example.internal` * `tcp://ssh.internal:22` Learn more about [Internal Endpoints](/docs/gateway/endpoints/internal-endpoints). ## Kubernetes Endpoints Endpoints with a `kubernetes` binding are private endpoints that are only available inside of Kubernetes clusters where you installed the [ngrok Kubernetes Operator](/docs/k8s). Example URLs: * `http://service.namespace` * `tcp://db.controlplane:5432` Learn more about [Kubernetes Endpoints](/docs/gateway/endpoints/kubernetes-endpoints). # How to Change a Cloud Endpoint Domain Source: https://ngrok.com/docs/gateway/endpoints/cloud-endpoints/change-domain-endpoint How to use the ngrok API to change the URL of your Cloud Endpoint with zero downtime. If you want to change the [matching domain](/docs/gateway/domains/) for your active Cloud Endpoint without any downtime to your services, you can use the [ngrok API](/docs/api/) to update its values instead of deleting and recreating the endpoint in the dashboard. ## What you'll need * An [ngrok API key](https://dashboard.ngrok.com/api-keys). * An active Cloud Endpoint. ## 1. Configure your API key If this is your first time using the ngrok API, add your API key to your config file: ```bash theme={null} ngrok config add-api-key $YOUR_API_KEY ``` You can verify that the API key is set by running: ```bash theme={null} ngrok api credentials list ``` You should see a response with information about your authtoken. ## 2. Get the ID of the endpoint you want to modify Next you'll need your endpoint's ID. To see a complete list of your endpoints, run: ```bash theme={null} ngrok api endpoints list ``` Find the endpoint you want to modify and note its ID, which is a long character string beginning with `ep_`. You can also find your endpoint ID in the [Endpoints section of the dashboard](https://dashboard.ngrok.com/endpoints). ## 3. Update the endpoint with the new domain Run the following command to change your Cloud Endpoint's URL, replacing `$YOUR_ENDPOINT_ID` and `$YOUR_DOMAIN` with their respective values from the previous steps: ```bash theme={null} ngrok api endpoints update $YOUR_ENDPOINT_ID --url $YOUR_DOMAIN ``` ## 4. Test your new endpoint URL To verify that the change was successful, run: ```bash theme={null} ngrok api endpoints get $YOUR_ENDPOINT_ID ``` You should see the new URL in the output, confirming that your Cloud Endpoint is now running there. # Forwarding Traffic to and Load Balancing Internal Endpoints with Cloud Endpoints Source: https://ngrok.com/docs/gateway/endpoints/cloud-endpoints/forwarding-and-load-balancing How to use ngrok Cloud Endpoints to do round-robin load balancing. This guide provides an example of using an ngrok Cloud Endpoint to route traffic between multiple internal endpoints. ## Core concepts * [Cloud endpoints](/docs/gateway/endpoints/cloud-endpoints) are centrally managed endpoints in the cloud that can be used to route traffic to Agent Endpoints. * [Internal endpoints](/docs/gateway/endpoints/internal-endpoints) are endpoints that are not publicly accessible and are only reachable from within your network by Cloud Endpoints via the forward action. * **Load balancing** improves application performance and reduces load by distributing incoming traffic across servers. This leads to faster response times for user-facing applications. ## Prerequisites To follow this guide, you will need a local computer with `ngrok` installed. [You can download ngrok here.](https://download.ngrok.com). If you are going to be following along using the **ngrok CLI**, you will need: * An [ngrok API key](https://dashboard.ngrok.com/api-keys) configured on your [ngrok agent](/docs/gateway/agent/api-keys). If you are going to be following along using **CURL**, you will need: * An [ngrok API key](https://dashboard.ngrok.com/api-keys) as an environment variable named `NGROK_API_KEY`. ## 1. Create an internal endpoint Start by creating an internal endpoint, initiated from the agent. The endpoints will be in HTTP for this guide, but you can also use TCP or TLS. ```bash theme={null} ngrok http 80 \ --url https://your-domain-name.internal ``` After running the above command, you should see an internal endpoint with an "online" status for your domain. This endpoint will route traffic to the app running at your local port 80. You can set up any HTTP server to run on this port locally, and it the endpoint will internally route traffic to that server. ## 2. Create a domain Next, create the domain that the Cloud Endpoint will reside on. For the purpose of this guide, create an ngrok HTTP domain. ```bash theme={null} ngrok api reserved-domains create \ --domain ${NGROK_SUBDOMAIN}.ngrok.app ``` * Replace or set `${NGROK_SUBDOMAIN}` as the subdomain you'd like to use for this guide. After running, you should see the following: ```json skip-validation theme={null} 200 OK { "id":"rd_2MT5Bqt0UzU0mFQ0zr8m1UQWCfm", ... } ``` ```bash theme={null} curl \ -X POST https://api.ngrok.com/reserved_domains \ -H "Authorization: Bearer ${NGROK_API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d @- < You can reserve a subdomain via [ngrok dashboard](https://dashboard.ngrok.com/domains/new). After successfully reserving a domain, you should see it listed in the domains table. When you have completed this step, you can move on to the next step. ## 3. Create a Traffic Policy for your Cloud Endpoint Unlike Agent Endpoints in which Traffic Policies are optional, Cloud Endpoints require a Traffic Policy so they know how to handle incoming traffic. This guide will showcase one of the simplest and most common use cases: forwarding traffic to other endpoints (the internal endpoint created in Step 1) via the forward action. ```yaml policy.yml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://your-domain-name.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "forward-internal", "config": { "url": "https://your-domain-name.internal" } } ] } ] } ``` ## 4. Create an ngrok Cloud Endpoint Now create an ngrok Cloud Endpoint that points to the newly created internal endpoint. Cloud Endpoints can be created via the API or ngrok dashboard. We use the URL from Step 2 as the `url` value in the Traffic Policy, and the Traffic Policy from Step 3 as the `traffic-policy` value. The binding is set to public so anyone can access our Cloud Endpoint. ```bash theme={null} ngrok api endpoints create \ --api-key YOUR_NGROK_API_KEY \ --bindings public \ --description "sample description" \ --metadata sample-metadata \ --url https://${NGROK_SUBDOMAIN}.ngrok.app --traffic-policy '{"on_http_request":[{"actions":[{"type":"forward-internal","config":{"url":"https://clep.internal","binding":"internal"}}]}]}' ``` * Replace NGROK\_SUBDOMAIN with the value used in step 2. Coming soon! ## 5. Load balancing with Endpoint Pools Load balancing is simple with ngrok Endpoint Pools. Start another agent with the same internal endpoint URL, and the traffic will be automatically load balanced between the two agents in the pool. ```bash theme={null} ngrok http 80 \ --url https://your-domain-name.internal ``` ## Conclusion You're done. You've successfully created a Cloud Endpoint that can be accessed from the URL you reserved: `${NGROK_SUBDOMAIN}.ngrok.app` The Cloud Endpoint will refer to its Traffic Policy and forward traffic to the internal endpoint. The internal endpoint finally exposes the local port on which it was created on in Step 1. # Cloud Endpoints Source: https://ngrok.com/docs/gateway/endpoints/cloud-endpoints/index Create and manage persistent, always-on endpoints in the cloud. Cloud Endpoints are persistent, always-on endpoints whose creation, deletion, and configuration is managed centrally via the Dashboard or API. They exist permanently until they are explicitly deleted, and can be configured to filter, route, load-balance, or authenticate traffic to endpoints running on your servers or local machines. ## Get started You can create a Cloud Endpoint in the Dashboard or using the API. See [the Cloud Endpoints Quickstart](/docs/getting-started/cloud-endpoints-quickstart) to learn more. ## Use cases Cloud endpoints are a good choice when you want to: * Centrally manage an endpoint's Traffic Policy configuration * Create always-on endpoints that aren't tied to the lifetime of an agent process * Route incoming traffic to multiple other endpoints based on path, IP, header, etc. * Create a loose coupling between the traffic-receiving endpoint and the traffic-servicing endpoint. * Update an endpoint's Traffic Policy without taking it offline. It is a common pattern to create public Cloud Endpoints that route traffic to internal Agent Endpoints via the [forward action](/docs/gateway/traffic-policy/actions/forward-internal/). The Traffic Policy of a Cloud Endpoint must terminate traffic via actions such as `deny`, `redirect`, `custom-response`, or `forward-internal`. ## Differences from Agent Endpoints See the [Endpoint Types](/docs/gateway/types/) page for a full comparison between Agent and Cloud Endpoints. * **Always-on** - Unlike Agent Endpoints, Cloud Endpoints are always-on and persistent. They exist permanently until they are explicitly deleted. * **Centralized Management** - Cloud Endpoints are created, managed and configured centrally via the API or Dashboard. You can update their Traffic Policy and other properties at any time via the API or Dashboard. Agent Endpoints, by contrast, can only be created and configured by the agent that initiated them. They are read-only in the API and Dashboard. * **URLs** - Cloud Endpoints must specify a fully qualified URL when created. You cannot ask for a URL to be randomly chosen for you. * **Traffic Policy** - Cloud Endpoints handle traffic they receive via their Traffic Policy. Unlike Agent Endpoints, they do not implicitly forward their traffic to an agent. You must specify a non-empty Traffic Policy when creating a Cloud Endpoint. * **API** - Cloud Endpoints API representation specifies `type: cloud` and its `tunnel_session` reference is `null`. ## Protocols, binding and pooling * **Protocols** - Cloud endpoints support all [Endpoint Protocols](/docs/gateway/endpoints/protocols) (`http`, `https`, `tcp` and `tls`). * **Bindings** - Cloud endpoints support all [Endpoint Bindings](/docs/gateway/endpoints/bindings) (`public`, `internal` and `kubernetes`). * **Pooling** - Cloud endpoints support [Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling/). They may also pool with themselves and with Agent Endpoints. ## Guides For examples of Cloud Endpoint usage, check out the following guides: * [Forwarding Traffic to and Load Balancing Internal Endpoints with Cloud Endpoints](/docs/gateway/endpoints/cloud-endpoints/forwarding-and-load-balancing/) * [Path-based Routing and Policy Decentralization with Cloud Endpoints](/docs/gateway/endpoints/cloud-endpoints/routing-and-policy-decentralization) ## API Cloud Endpoints can be managed programmatically via: * \[`/endpoints` API Resource]\(/api-reference/endpoints/list of the [API](/docs/api/) * [`CloudEndpoint` CRD](/docs/gateway/k8s/crds/) of the [Kubernetes Operator](/docs/gateway/k8s/) ## Cloud Endpoints pricing Cloud endpoints are available on the pay-as-you-go plan. Consult the [Endpoints Pricing](/docs/gateway/endpoints/#pricing) documentation for billing details. # Path-based Routing and Policy Decentralization with Cloud Endpoints Source: https://ngrok.com/docs/gateway/endpoints/cloud-endpoints/routing-and-policy-decentralization Learn how to use Cloud Endpoints to route traffic by path and apply different Traffic Policies to each of your endpoints. This guide provides an example on how to forward traffic to internal endpoints based on path using ngrok Cloud Endpoints. The Cloud Endpoint will have a Traffic Policy to route traffic to one of two internal endpoints depending on the path, but each internal endpoint will also have its own Traffic Policy to independently handle incoming traffic. ## Core concepts * [Cloud endpoints](/docs/gateway/endpoints/cloud-endpoints) are centrally managed endpoints in the cloud that can be used to route traffic to Agent Endpoints. * [Internal endpoints](/docs/gateway/endpoints/internal-endpoints) are endpoints that are not publicly accessible and are only reachable from within your network by Cloud Endpoints via the forward action. * **Path-Based Routing** involves directing incoming traffic to different destinations based on the path component of a URL ## Prerequisites To follow this guide, you will need a local computer with `ngrok` installed [by following the installation guides](https://download.ngrok.com). If you are going to be following along using **ngrok CLI**, you will need: * An [ngrok API key](https://dashboard.ngrok.com/api-keys) configured on your [ngrok agent](/docs/gateway/agent/api/). If you are going to be following along using **CURL**, you will need: * An [ngrok API key](https://dashboard.ngrok.com/api-keys) as an environment variable named `NGROK_API_KEY`. ## 1. Create an internal endpoint for the homepage Start by creating an internal endpoint for the homepage endpoint, which will handle all non-API traffic. ```bash theme={null} ngrok http 80 \ --url https://homepage.internal ``` After running, you should see the endpoint with an "online" status at your domain. It will route traffic to the app running at your local port 80, which you can setup to have any HTTP server running. ## 2. Create an internal endpoint for the API Next, create another internal endpoint, this one for all traffic to the `/api/` path. Use a Traffic Policy to simulate accessing an API endpoint. ```yaml policy.yml theme={null} on_http_request: - expressions: - "!(hasReqHeader('Api-Key'))" actions: - type: deny config: status_code: 404 - actions: - type: custom-response config: status_code: 200 body: "

Hello world

The quick brown fox jumps over the lazy dog.

" headers: content-type: text/html ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!(hasReqHeader('Api-Key'))" ], "actions": [ { "type": "deny", "config": { "status_code": 404 } } ] }, { "actions": [ { "type": "custom-response", "config": { "status_code": 200, "body": "

Hello world

The quick brown fox jumps over the lazy dog.

", "headers": { "content-type": "text/html" } } } ] } ] } ```
The above Traffic Policy will deny all requests that do not have an `API-Key` header, and return a 200 status code with a custom response for all other requests. Save the Traffic Policy as a file locally in your directory. Now, create an internal endpoint based on this Traffic Policy. ```bash theme={null} ngrok http 3001 \ --url https://api.internal \ --traffic-policy-file {PATH_TO_TRAFFIC_POLICY} ``` After running, you should see the endpoint with an "online" status for your endpoint. ## 3. Create a domain for the Cloud Endpoint Next, create the domain or TCP address that the Cloud Endpoint will reside on. For the purpose of this guide, create an ngrok HTTP domain. ```bash theme={null} ngrok api reserved-domains create \ --url ${NGROK_SUBDOMAIN}.ngrok.app ``` * Replace `NGROK_SUBDOMAIN` with the subdomain you'd like to use for this guide. After running, you should see the following: ```json skip-validation theme={null} 200 OK { "id":"rd_2MT5Bqt0UzU0mFQ0zr8m1UQWCfm", ... } ``` ```bash theme={null} curl \ -X POST https://api.ngrok.com/reserved_domains \ -H "Authorization: Bearer ${NGROK_API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d @- < You can reserve a subdomain via [ngrok dashboard](https://dashboard.ngrok.com/domains/new). After successfully reserving a domain, you should see it listed in the domains table. When you have completed this step, you can move on to the next step. ## 4. Create a Traffic Policy for your Cloud Endpoint Now, create a Traffic Policy for the Cloud Endpoint. All incoming traffic will be preprocessed by this Traffic Policy, before being forwarded to the internal endpoints which may or may not have their own independent Traffic Policies. This separation allows for granularity and full control over how traffic is handled. ```yaml policy.yml theme={null} on_http_request: - expressions: - req.url.path.startsWith('/api/') actions: - type: forward-internal config: url: https://api.internal binding: internal - actions: - type: forward-internal config: url: https://homepage.internal binding: internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.url.path.startsWith('/api/')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://api.internal", "binding": "internal" } } ] }, { "actions": [ { "type": "forward-internal", "config": { "url": "https://homepage.internal", "binding": "internal" } } ] } ] } ``` The Traffic Policy simply forwards traffic to one of the two internal endpoints based on the path. More complex traffic handling can be created at your discretion. ## 5. Create an ngrok Cloud Endpoint Now create an ngrok Cloud Endpoint that points to the newly created internal endpoint. Cloud Endpoints can be created via the API or ngrok dashboard. We use the URL from Step 2 as the `url` value in the Traffic Policy, and the Traffic Policy from Step 3 as the `traffic-policy` value. The binding is set to public so anyone can access our Cloud Endpoint. Due to the shell's difficulty in parsing a more complex string that includes both `'` and `"`, we will use Postman to send a POST request to the ngrok API instead of using the `ngrok api` command. 1. Open Postman and create a POST request to `https://api.ngrok.com/endpoints`. 2. In the Authorization tab, select "API Key". * Set Key as `Authorization` * Set Value as `Bearer NGROK_API_KEY` * Set Add to as `Header` 3. In the Headers tab, add a new header: * Create a header with the key `Content-Type` and value `application/json` * Create a header with the key `Ngrok-Version` and value `2` 4. In the Body tab, create a raw JSON: ```json theme={null} { "bindings": ["public"], "description": "sample Cloud Endpoint", "metadata": "{\"environment\": \"prod\"}", "type": "cloud", "traffic_policy": "{\"on_http_request\": [{\"expressions\": [\"req.url.path.startsWith('/api/')\"],\"actions\": [{\"type\": \"forward-internal\",\"config\": {\"url\": \"https://api.internal\",\"binding\": \"internal\"}}]},{\"actions\": [{\"type\": \"forward-internal\",\"config\": {\"url\": \"https://homepage.internal\",\"binding\": \"internal\"}}]}]}", "url": "https://{NGROK_SUBDOMAIN}.ngrok.app" } ``` * Replace `YOUR_NGROK_API_KEY` with your ngrok API key. * Replace `NGROK_SUBDOMAIN` with the value used in Step 3. For more details on using the API to create a Cloud Endpoint, see the \[API documentation for endpoints]\(/api-reference/endpoints/list. Coming soon! ## Conclusion You've successfully created a Cloud Endpoint that can be accessed from the URL you reserved: `${NGROK_SUBDOMAIN}.ngrok.app` On most paths, the Cloud Endpoint will refer to its Traffic Policy and forward traffic to the homepage internal endpoint, which exposes the app running on local port 80. For /api/ paths, the Cloud Endpoint will forward traffic to the API internal endpoint. It requires an `Api-Key` header to access, and denies for all other requests. You won't be able to access the API normally from the browser, but you can use CURL or Postman to test it. ```bash theme={null} curl --header "Api-Key: AnythingHere" https://${NGROK_SUBDOMAIN}.ngrok.app/api/ ``` # Endpoint Pooling Source: https://ngrok.com/docs/gateway/endpoints/endpoint-pooling Learn how ngrok enables you to load balance traffic to endpoints with endpoint pooling. Endpoint pooling makes load balancing dead simple. Load balancing helps you scale traffic across multiple replicas of your app to increase capacity, tolerate failures and geo-distribute load. When your create two endpoints with the same URL (and binding), those endpoints automatically form a "pool" and share incoming traffic. Endpoint pooling is vastly more flexible than comparable load balancing systems: * Endpoints join and leave pools automatically as they are created and destroyed, there is no need to register your upstreams ahead of time. * You can pool endpoints with different Traffic Policies. This means that not only can you blue-green and canary deploy your own apps, you can also do that with the Traffic Policies themselves. This helps you dramatically mitigate the risk of gateway changes that so often cause site-wide outages. ## When to use Endpoint Pooling Endpoint pooling is a good choice when you want to: * Load balance among multiple replicas of the same application, even running in different cloud providers, on-prem, etc. * Switch from Agent Endpoints to Cloud Endpoints with no downtime (or vice-versa) * Implement blue-green deployments to reduce risk and rollback times * Deploy canary version of your application * A/B test different version of your apps with blue-green deployments * Gradually roll out changes to an endpoint's Traffic Policy ## Quickstart ### Agent Endpoints Create two endpoints with the same URL forwarding to two different ports. They will automatically be pooled together. ``` ngrok http 8080 --url https://your-domain.ngrok.app --pooling-enabled=true ``` ``` ngrok http 9090 --url https://your-domain.ngrok.app --pooling-enabled=true ``` When you make requests to the endpoints' URL, you will see responses balanced among each endpoint in the pool. ``` curl https://your-domain.ngrok.app curl https://your-domain.ngrok.app ``` ### Cloud Endpoints Create two endpoints with the same URL and two different Traffic Policies. They will automatically be pooled together. First, create two Traffic Policy files, `policy-a.yml` and `policy-b.yml`. ```yaml title="policy-a.yml" mode=traffic-policy theme={null} on_http_request: - actions: - type: custom-response config: status_code: 200 body: Policy A ``` ```yaml title="policy-b.yml" mode=traffic-policy theme={null} on_http_request: - actions: - type: custom-response config: status_code: 200 body: Policy B ``` Then create two Cloud Endpoints, one with each policy: ```bash title="Command line" theme={null} ngrok api endpoints create \ --url https://your-domain.ngrok.app \ --traffic-policy "$(cat policy-a.yml)" \ --pooling-enabled=true ngrok api endpoints create \ --url https://your-domain.ngrok.app \ --traffic-policy "$(cat policy-b.yml)" \ --pooling-enabled=true ``` When you make requests to the endpoints' URL, you will see responses balanced among each endpoint in the pool. ```bash title="Command line" theme={null} curl https://your-domain.ngrok.app curl https://your-domain.ngrok.app ``` ## Lifecycle An Endpoint Pool is automatically created whenever you create two endpoints with: * The same URL * The same binding * Pooling enabled When only a single endpoint remains in a pool, it is no longer pooled with others, but the endpoint remains poolable and can accept additional entries. ## Enabling pooling Endpoint pooling is disabled by default. Endpoint pooling is enabled on a per-endpoint basis. You must enable pooling on each endpoint you want to pool by setting `pooling_enabled` to `true`. ## Pool size There is no limit on the number of endpoints that may be in a pool. ## Conflicts If you create a second endpoint with the same URL and binding as an existing endpoint, both the new endpoint and existing endpoint must have [pooling enabled](#enabling-pooling). If they do not, ngrok will return a conflict error when attempting to start the second endpoint. The URLs of all endpoints in a pool must share the same protocol. For example, you may not pool the endpoints `https://foo.internal:1234` and `tcp://foo.internal:1234` together because they have a different protocol. Conflicts can only occur during endpoint creation because both the URL and the pooling enabled property may not be updated after an endpoint has been created. ## Load balancing Traffic to pooled URLs is load balanced among the endpoints in the pool. Load balancing is applied in two places: * When pooled public and kubernetes endpoints load balance traffic received from external clients. * When pooled internal endpoints receive traffic that is forwarded to them via the `forward-internal` Traffic Policy action. ### Granularity ngrok intelligently chooses the load balancing layer automatically for you. Traffic to TCP and TLS endpoints is balanced on a per-connection basis (layer 4\). Traffic to HTTP and HTTPS endpoints balanced on a per-request basis (layer 7). If multiple endpoints in a pool have different `on_tcp_connect` phases in their Traffic Policy, load balancing is instead done on a per-connection basis (layer 4\). ### Heterogeneous endpoints Endpoints in a pool may have different Traffic Policies. This lets you run old and new Traffic Policies side-by-side during a transition. It also enables you to roll out new Traffic Policy changes without risking an all-or-nothing deployment. When traffic is balanced among endpoints with different Traffic Policies, ngrok first chooses an endpoint to balance to and then executes the Traffic Policy of the chosen endpoint. ### Default strategy ngrok's default balancing strategy attempts to strike a balance between performance and fairness: * First, it identifies all endpoints in the pool which are in the point of presence closest to where a connection is received. * Then, connections are balanced in a random distribution among those endpoints. For the purposes of the first step, [cloud endpoints](/docs/gateway/endpoints/cloud-endpoints) are considered to be online in all points of presence. For example, if an endpoint pool consists of the following endpoints: * Endpoint A: Agent Endpoint connected to `eu` * Endpoint B: Agent Endpoint connected `eu` * Endpoint C: Agent Endpoint connected `us` * Endpoint D: Cloud Endpoint Then: * A connection received in `eu` will be balanced among endpoints A, B, D * A connection received in `us` will be balanced among endpoints C, D * A connection received in `jp` will be balanced among endpoints D ## Custom strategies Custom balancing strategies are not yet generally available, but you can request Early Access in your [ngrok dashboard](https://dashboard.ngrok.com/early-access). ### Retries When a connection or request is balanced to an endpoint in a pool, if that connection or request fails, it is not retried by sending it to another endpoint in the pool. ## Wildcard endpoints Wildcard endpoints do not pool with non-wildcard URLs. For example, if you have two endpoints online, `https://foo.example.com` and `https://*.example.com`, they will not pool together and traffic to `https://foo.example.com` will not be load balanced. Consult the documentation for [wildcard endpoints](/docs/gateway/endpoints/http/#wildcard-endpoints) to understand the rules for matching and precedence. ## Protocol, binding, and type * Endpoint pooling supports all endpoint protocols * Endpoint pooling supports all endpoint bindings * Endpoint pooling supports all endpoint types (and you can mix types in an endpoint pool) ## API Because endpoint pools are created on-demand when two endpoints share the same URL and binding, there is no API resource for endpoint pools. Instead, each \[Endpoint API Resource]\(/api-reference/endpoints/list has a read-only `pools` field which lists the other endpoints in the same pool as the requested endpoint. ## Endpoint Pooling pricing Endpoint pooling is available on the Pay-As-You-Go plan. Each endpoint in a pool is billed separately. # HTTP/S Agent Endpoints Source: https://ngrok.com/docs/gateway/endpoints/http Learn how to create and configure HTTP and HTTPS endpoints with ngrok including domain configuration, TLS, and Traffic Policies. HTTP/S endpoints enable you to serve web services like REST APIs, web applications, websites, and WebSocket servers. Serving a web application is as simple as `ngrok http 80`. Once your endpoint is running, check out: * [Traffic Policy](/docs/gateway/traffic-policy/) - Add routing, authentication and traffic transformation * [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) - Real-time observability with request/response introspection * [Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling/) - Load balancing ## Quickstart Agent Endpoints are the easiest way to get started with ngrok. An [agent endpoint](/docs/gateway/endpoints/cloud-endpoints/) is started by a [Secure Tunnels](/docs/gateway/agent/) agent. The endpoint lives for the lifetime of the process and forwards traffic to a port or URL of your choosing. Create the endpoint `https://example.ngrok.app` and forward its traffic to a local port. ```bash theme={null} ngrok http 8080 --url https://example.ngrok.app ``` ```yaml theme={null} version: 3 endpoints: - name: example url: https://example.ngrok.app upstream: url: 8080 ``` ```bash theme={null} ssh -R example.ngrok.app:443:localhost:8080 v2@connect.ngrok-agent.com http ``` ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Forward(ctx, ngrok.WithUpstream("http://localhost:8080"), ngrok.WithURL("https://example.ngrok.app"), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, domain: "example.ngrok.app", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain) * [https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#domain](https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#domain) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, domain="example.ngrok.app") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/http\_listener\_builder.html#ngrok.HttpListenerBuilder.domain](https://ngrok.github.io/ngrok-python/http_listener_builder.html#ngrok.HttpListenerBuilder.domain) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .http_endpoint() .domain("example.ngrok.app") .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.domain](https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.domain) ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: ingressClassName: ngrok rules: - host: example.ngrok.app http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ## URL URLs are validated differently depending on their [binding](/docs/gateway/endpoints/bindings). Consult the following documentation for details on valid URLs for TCP endpoints: * [Public Endpoint URLs](/docs/gateway/endpoints/public-endpoints/#urls) * [Internal Endpoint URLs](/docs/gateway/endpoints/internal-endpoints/#urls) * [Kubernetes Endpoint URLs](/docs/gateway/endpoints/kubernetes-endpoints/#urls) ### Public #### HTTP * The hostname must be a domain with a valid [public suffix](https://publicsuffix.org/). * The port must be `80`. If you do not specify a port, the default `80` will be used for you. **Examples** * `http://example.ngrok.app` * `http://example.ngrok.app:80` * `http://example.party` * `http://example.ngrok.app:81` - invalid port: port number must be `80`, not `81` * `http://example.doesnotexist` - invalid hostname: `.doesnotexist` is not a public suffix domain * `http://example.internal` - invalid hostname: `.internal` is not a public suffix domain #### HTTPS * The hostname must be a domain with a valid [public suffix](https://publicsuffix.org/). * The port must be `443`. If you do not specify a port, the default `443` will be used for you. ### Internal ### Kubernetes #### Valid URLs * [https://example.ngrok.app](https://example.ngrok.app) * [https://example.ngrok.app:443](https://example.ngrok.app:443) * [https://example.party](https://example.party) #### Invalid URLs * [https://example.ngrok.app:8443](https://example.ngrok.app:8443) * [https://example.nosuchtld](https://example.nosuchtld) * [https://example.internal](https://example.internal) ### Validation When you create an Agent Endpoint, if you do not specify a complete URL, the following defaults are used to construct the endpoint URL. | URL Part | Default | | - | - | | Scheme | `https` | | Hostname | randomly selected | | Port | `443` if scheme is `https`
`80` if scheme is `http` | Consult the following table of examples of URL defaulting: | Value | Endpoint URL | | - | - | | `https://example.ngrok.app` | `https://example.ngrok.app` | | `http://example.ngrok.app` | `http://example.ngrok.app` | | `example.ngrok.app` | `https://example.ngrok.app` | | `app.example.com` | `https://app.example.com` | | `https://example.internal` | `https://example.internal` | | `https://example.internal:1234` | `https://example.internal:1234` | | `http://example.internal` | `http://example.internal` | | `foo.internal` | `https://foo.internal` | | `{empty}` | `https://1eb2-181-80-12-3.ngrok.app` (randomly selected) | If you would like to listen for both http and https traffic, create two endpoints. #### Domains When you create a public endpoint, it must match a [Domain](/docs/domains) on your account. Domains help you set up branded domains and manage TLS certificates. You may create wildcard endpoints as well. Endpoints with randomly assigned hostnames are an exception and won't match an existing Domain object. ### Bring your own domain If you want to bring your own domain, first [create a Domain record and set up a DNS CNAME record](/docs/gateway/domains/#branded-domains). Then create an endpoint on that domain by specifying a URL with a matching hostname. For example, to create an HTTPS endpoint on `https://app.example.com`, [create a Domain](https://dashboard.ngrok.com/domains) and follow the instructions to set up a CNAME record. Then use the following example to start an endpoint on your domain: ```bash theme={null} ngrok http 80 --url https://app.example.com ``` ```yaml theme={null} version: 3 endpoints: - name: example url: https://app.example.com upstream: url: 8080 ``` ```bash theme={null} ssh -R app.example.com:443:localhost:80 v2@connect.ngrok-agent.com http ``` ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Listen(ctx, ngrok.WithURL("https://app.example.com"), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, domain: "example.ngrok.app", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain) * [https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#domain](https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#domain) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, domain="example.ngrok.app") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/http\_listener\_builder.html#ngrok.HttpListenerBuilder.domain](https://ngrok.github.io/ngrok-python/http_listener_builder.html#ngrok.HttpListenerBuilder.domain) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .http_endpoint() .domain("example.ngrok.app") .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.domain](https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.domain) ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: ingressClassName: ngrok rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ### Wildcard endpoints You can create a wildcard endpoint which will receive traffic for all of the subdomains matching a wildcard pattern like `*.example.com`. To create a public wildcard endpoint, you must first reserve a [wildcard domain](/docs/gateway/domains/#wildcard-domains). For example, if you create the wildcard endpoint `https://*.example.com`, it will receive traffic for `https://foo.example.com` and `https://bar.example.com`. * Connections to URLs which match an online wildcard endpoint will be routed to it. For example, if you have created a wildcard endpoint `https://*.example.com`, connections to `https://foo.bar.baz.example.com` will route to it. * Connections are routed to the most specific online endpoint. For example, if the endpoints `https://*.example.com` and `https://app.example.com` are both online, a connection to `https://app.example.com` will not be routed to the wildcard endpoint. ```bash theme={null} ngrok http 80 --url "https://*.example.com" ``` ```yaml theme={null} version: 3 examples: - name: example url: https://*.example.com upstream: url: 80 ``` ```bash theme={null} ssh -R '*.example.com:443:localhost:80' v2@connect.ngrok-agent.com http ``` ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Listen(ctx, ngrok.WithURL("https://*.example.com"), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, domain: "*.example.com", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain) * [https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#domain](https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#domain) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, domain="*.example.com") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/http\_listener\_builder.html#ngrok.HttpListenerBuilder.domain](https://ngrok.github.io/ngrok-python/http_listener_builder.html#ngrok.HttpListenerBuilder.domain) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .http_endpoint() .domain("*.example.com") .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.domain](https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.domain) ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: ingressClassName: ngrok rules: - host: "*.example.com" http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` For information on how wildcard endpoints are billed, including endpoint hours and Traffic Policy charges when forwarding to internal endpoints, see the [wildcard endpoints pricing documentation](/docs/pricing-limits/#wildcard-endpoints). ### Randomly assigned hostnames If you run create a public endpoint without specifying a hostname, ngrok will automatically assign a random one for you by selecting a random subdomain of one of the [ngrok-managed Domains](/docs/domains#ngrok-managed-domains) to your endpoint. For example, the command `ngrok http 80` may create an endpoint like `https://1eb2-181-80-12-3.ngrok.app`. The following example create an HTTPS endpoint on a randomly assigned hostname that forwards to port 8080. ```bash theme={null} ngrok http 8080 ``` ```yaml theme={null} version: 3 endpoints: - name: example url: https:// upstream: url: 8080 ``` ```bash theme={null} ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http ``` ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Listen(ctx) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html](https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html) * [https://ngrok.github.io/ngrok-javascript/classes/Session.html#httpEndpoint](https://ngrok.github.io/ngrok-javascript/classes/Session.html#httpEndpoint) * [https://ngrok.github.io/ngrok-javascript/functions/connect.html](https://ngrok.github.io/ngrok-javascript/functions/connect.html) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True) print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/http\_listener\_builder.html](https://ngrok.github.io/ngrok-python/http_listener_builder.html) * [https://ngrok.github.io/ngrok-python/session.html#ngrok.Session.http\_endpoint](https://ngrok.github.io/ngrok-python/session.html#ngrok.Session.http_endpoint) * [https://ngrok.github.io/ngrok-python/module.html#ngrok.connect](https://ngrok.github.io/ngrok-python/module.html#ngrok.connect) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .http_endpoint() .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/session/struct.Session.html#method.http\_endpoint](https://docs.rs/ngrok/latest/ngrok/session/struct.Session.html#method.http_endpoint) ## Traffic Policy Attach [Traffic Policy](/docs/gateway/traffic-policy/) to endpoints to route, authenticate and transform the traffic through the endpoint. ### Authentication Public endpoints are accessible to the public internet unless you secure them with authentication. That's desirable if you're hosting a public website but most often you want to add authentication. You can secure your endpoints with [Traffic Policy](/docs/traffic-policy) with any of the following actions: * [Basic Auth](/docs/gateway/traffic-policy/actions/basic-auth) * [OAuth](/docs/gateway/traffic-policy/actions/oauth) * [IP Restriction](/docs/gateway/traffic-policy/actions/restrict-ips/) * [Webhook Verification](/docs/gateway/traffic-policy/actions/verify-webhook/) * [JWT](/docs/gateway/traffic-policy/actions/jwt-validation/) * [Mutual TLS](/docs/gateway/traffic-policy/actions/terminate-tls/) * [OpenID Connect](/docs/gateway/traffic-policy/actions/oidc/) * [SAML](/docs/gateway/traffic-policy/actions/saml/) ### Basic auth example Adds a username and password with the [Basic Auth](/docs/gateway/traffic-policy/actions/basic-auth) Traffic Policy action. ```bash theme={null} ngrok http 80 --traffic-policy-file traffic-policy.yml ``` ##### `traffic-policy.yml` ```yaml theme={null} on_http_request: - actions: - type: basic-auth config: credentials: - username1:password1 - username2:password2 ``` ```yaml theme={null} version: 3 endpoints: - name: example url: https://your-subdomain-here.ngrok-free.app upstream: url: 80 traffic_policy: on_http_request: - actions: - type: basic-auth config: credentials: - username1:password1 - username2:password2 ``` ```bash theme={null} ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http \ --basic-auth "username1:password1" \ --basic-auth "username2:password2" ``` ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) trafficPolicy := ` on_http_request: - actions: - type: basic-auth config: credentials: - username1:password1 - username2:password2 ` func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Listen(ctx, ngrok.WithTrafficPolicy(trafficPolicy), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, basic_auth: ["username1:password1", "username2:password2"], }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#basic\_auth](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#basic_auth) * [https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#basicAuth](https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#basicAuth) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, basic_auth=["username1:password1", "username2:password2"]) print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/http\_listener\_builder.html#ngrok.HttpListenerBuilder.basic\_auth](https://ngrok.github.io/ngrok-python/http_listener_builder.html#ngrok.HttpListenerBuilder.basic_auth) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .http_endpoint() .basic_auth("username1", "password1") .basic_auth("username2", "password2") .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.basic\_auth](https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.basic_auth) ```yaml theme={null} --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress annotations: k8s.ngrok.com/traffic-policy: example spec: ingressClassName: ngrok rules: - host: your-domain.ngrok.app http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 --- kind: NgrokTrafficPolicy apiVersion: ingress.k8s.ngrok.com/v1alpha1 metadata: name: example policy: on_http_request: - actions: - type: basic-auth config: credentials: - username1:password1 - username2:password2 ```
### Google OAuth example The following example enforces a browser-based OAuth redirect flow in front of your endpoint using Google as the identity provider by using the [OAuth](/docs/gateway/traffic-policy/actions/oauth) Traffic Policy action. ``` ngrok http 80 --traffic-policy-file traffic-policy.yml ``` ##### `traffic-policy.yml` ```yaml theme={null} on_http_request: - actions: - type: oauth config: provider: google ``` ```yaml theme={null} version: 3 endpoints: - name: example url: https://example.ngrok.app upstream: url: 80 traffic_policy: on_http_request: - actions: - type: oauth config: provider: google ``` When creating a reverse tunnel, you must provide a publicly accessible URL to your traffic policy file, as shown in this example. First, create the `oauth-policy.yml` file: ```yaml theme={null} on_http_request: - actions: - type: oauth config: provider: google ``` Store this file at a publicly accessible address online. For example, you could add it to a github repository. Finally, add your traffic policy to your reverse tunnel using the `--traffic-policy-url` flag. ```bash theme={null} ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http --traffic-policy-url=https://some-website/oauth-policy.yml ``` ```go theme={null} import ( "context" "fmt" "log" "net/http" "golang.ngrok.com/ngrok/v2" ) const trafficPolicy = ` on_http_request: - name: OAuth actions: - type: oauth config: auth_id: oauth provider: google - type: add-headers config: headers: authenticated-user: "${actions.ngrok.oauth.identity.email}" - name: bad email expressions: - actions.ngrok.oauth.identity.email != 'alan@example.com' actions: - type: custom-response config: body: "Access denied: ${actions.ngrok.oauth.identity.name}!" status_code: 403 ` func main() { ctx := context.Background() ln, err := ngrok.Listen(ctx, ngrok.WithTrafficPolicy(trafficPolicy), ) if err != nil { log.Fatal(err) } log.Println("Endpoint online:", ln.URL()) http.Serve(ln, http.HandlerFunc(handler)) } func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %s\n", r.Header.Get("authenticated-user")) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, oauth_provider: "google", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#oauth\_provider](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#oauth_provider) * [https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#oauth](https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#oauth) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, oauth_provider="google") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/http\_listener\_builder.html#ngrok.HttpListenerBuilder.oauth](https://ngrok.github.io/ngrok-python/http_listener_builder.html#ngrok.HttpListenerBuilder.oauth) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .http_endpoint() .oauth(OauthOptions::new("google")) .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.oauth](https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.oauth) * [https://docs.rs/ngrok/latest/ngrok/config/struct.OauthOptions.html](https://docs.rs/ngrok/latest/ngrok/config/struct.OauthOptions.html) ```yaml theme={null} --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress annotations: k8s.ngrok.com/traffic-policy: example spec: ingressClassName: ngrok rules: - host: your-domain.ngrok.app http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 --- kind: NgrokTrafficPolicy apiVersion: ingress.k8s.ngrok.com/v1alpha1 metadata: name: example policy: on_http_request: - actions: - type: oauth config: provider: google ``` ### Rewriting the `host` header Some application servers expect the `host` header to match a specific value when they receive requests and some use the `host` header to determine which of many sites to display. ngrok can rewrite the `host` header of incoming requests so that your application behaves correctly. When you rewrite the `host` header, ngrok also rewrites the `location` header of HTTP responses automatically to match the hostname of your Endpoint URL. The following example rewrites the `host` header to the value `localhost` using the [`add-headers`](/docs/gateway/traffic-policy/actions/add-headers) Traffic Policy action. Adding the `Host` header [is a special case](/docs/gateway/traffic-policy/actions/add-headers/#special-cases) that replaces the existing `Host` header instead of appending a second value. ```bash theme={null} ngrok http 8080 --traffic-policy-file traffic-policy.yml ``` ##### `traffic-policy.yml` ```yaml theme={null} on_http_request: - actions: - type: add-headers config: headers: host: localhost ``` ```yaml theme={null} version: 3 endpoints: - name: example url: https://example.ngrok.app upstream: url: 8080 traffic_policy: on_http_request: - actions: - type: add-headers config: headers: host: localhost ``` ```bash theme={null} ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http \ --request-header-add='host: localhost' ``` ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) trafficPolicy := ` on_http_request: - actions: - type: add-headers config: headers: host: localhost ` func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Listen(ctx, ngrok.WithTrafficPolicy(trafficPolicy), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, request_header_add: "host:localhost", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#request\_header\_add](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#request_header_add) * [https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#requestHeader](https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#requestHeader) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, request_header_add="host:localhost") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/http\_listener\_builder.html#ngrok.HttpListenerBuilder.request\_header](https://ngrok.github.io/ngrok-python/http_listener_builder.html#ngrok.HttpListenerBuilder.request_header) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .http_endpoint() .request_header("host", "localhost") .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.request\_header](https://docs.rs/ngrok/latest/ngrok/config/struct.HttpTunnelBuilder.html#method.request_header) ```yaml theme={null} --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress annotations: k8s.ngrok.com/traffic-policy: example spec: ingressClassName: ngrok rules: - host: your-domain.ngrok.app http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 --- kind: NgrokTrafficPolicy apiVersion: ingress.k8s.ngrok.com/v1alpha1 metadata: name: example policy: on_http_request: - actions: - type: add-headers config: headers: host: localhost ``` ## Agent forwarding The [ngrok agent](/docs/gateway/agent/) and [Agent SDKs](/docs/agent-sdks/) forward traffic that your endpoints receive to upstream services. You specify a URL or port number to instruct the ngrok agent where and how to forward traffic. ### HTTPS forwarding The scheme in your upstream URL is used to determine whether to forward HTTP or HTTPS traffic to the upstream service. If you do not specify a scheme, the default `http` scheme is chosen *unless* you forward to port `443`, in which case ngrok will use `https`. Consult the following table of examples. | Upstream URL | Normalized Value | | - | - | | `http://localhost:1234` | `http://localhost:1234` | | `https://localhost:1234` | `https://localhost:1234` | | `localhost:1234` | `http://localhost:1234` | | `1234` | `http://localhost:1234` | | `localhost:443` | `https://localhost:443` | | `443` | `https://localhost:443` | ngrok assumes that the network you run the agent on is private and it does not verify the TLS certificate presented by the upstream service. You may change this behavior with the [flags `--upstream-tls-verify` and `upstream-tls-verify-cas`](/docs/gateway/agent/cli/#ngrok-http). ```bash theme={null} ngrok http https://localhost:8443 ``` ```yaml theme={null} version: 3 endpoints: - name: example url: "https://example.ngrok.app" upstream: url: https://localhost:8443 ``` Forwarding to an upstream HTTPS service is not supported via SSH. ```go theme={null} import ( "context" "golang.ngrok.com/ngrok/v2" ) func ngrokForwarder(ctx context.Context) (ngrok.EndpointForwarder, error) { return ngrok.Forward(ctx, ngrok.WithUpstream("https://localhost:8443"), ) } ``` For HTTP/2 Use: `ngrok.WithUpstream("https://localhost:8443", ngrok.WithUpstreamProtocol("http2"))` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: "https://localhost:8443", authtoken_from_env: true, }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#addr](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#addr) * [https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#listenAndForward](https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#listenAndForward) ```python theme={null} import ngrok listener = ngrok.forward("https://localhost:8443", authtoken_from_env=True) print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/module.html#ngrok.connect](https://ngrok.github.io/ngrok-python/module.html#ngrok.connect) * [https://ngrok.github.io/ngrok-python/http\_listener\_builder.html#ngrok.HttpListenerBuilder.listen\_and\_forward](https://ngrok.github.io/ngrok-python/http_listener_builder.html#ngrok.HttpListenerBuilder.listen_and_forward) ```rust theme={null} use ngrok::prelude::*; use ngrok::tunnel; use ngrok::forwarder::Forwarder; use url::Url; async fn forward_ngrok() -> Result, Error> { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; sess .http_endpoint() .listen_and_forward(Url::parse("https://localhost:8443")?) .await .map_err(Into::into) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/0.14.0-pre.13/ngrok/config/struct.HttpTunnelBuilder.html#method.listen\_and\_forward](https://docs.rs/ngrok/0.14.0-pre.13/ngrok/config/struct.HttpTunnelBuilder.html#method.listen_and_forward) Add the `k8s.ngrok.com/app-protocols` label to the **Service** definition targeted by your ingress backend to instruct the Operator to use `https` when forwarding connections. ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: example-service annotations: k8s.ngrok.com/app-protocols: '{"example-https-port":"HTTPS"}' spec: ports: - name: example-https-port port: 443 protocol: TCP targetPort: 8443 selector: app-name: some-example-app-label --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: ingressClassName: ngrok rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 443 ``` ### Non-local forwarding Agents don't just forward to ports on your localhost. You can forward traffic to any address or URL reachable from the agent. For example, if you want to forward traffic to a HTTP server running on your network at `192.168.1.2:80`: ```bash theme={null} ngrok http 192.168.1.2:80 ``` ```yaml theme={null} version: 3 endpoints: - name: example url: http://example.ngrok.app upstream: url: http://192.168.1.2 ``` ```bash theme={null} ssh -R 0:192.168.1.2:80 v2@connect.ngrok-agent.com http ``` ```go theme={null} import ( "context" "golang.ngrok.com/ngrok/v2" ) func ngrokForwarder(ctx context.Context) (ngrok.EndpointForwarder, error) { return ngrok.Forward(ctx, ngrok.WithUpstream("http://192.168.1.2:80"), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: "192.168.1.2:80", authtoken_from_env: true, }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#addr](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#addr) * [https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#listenAndForward](https://ngrok.github.io/ngrok-javascript/classes/HttpListenerBuilder.html#listenAndForward) ```python theme={null} import ngrok listener = ngrok.forward("192.168.1.2:80", authtoken_from_env=True) print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/module.html#ngrok.connect](https://ngrok.github.io/ngrok-python/module.html#ngrok.connect) * [https://ngrok.github.io/ngrok-python/http\_listener\_builder.html#ngrok.HttpListenerBuilder.listen\_and\_forward](https://ngrok.github.io/ngrok-python/http_listener_builder.html#ngrok.HttpListenerBuilder.listen_and_forward) ```rust theme={null} use ngrok::prelude::*; use ngrok::tunnel; use ngrok::forwarder::Forwarder; use url::Url; async fn forward_ngrok() -> Result, Error> { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; sess .http_endpoint() .listen_and_forward(Url::parse("http://192.168.1.2:80")?) .await .map_err(Into::into) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/0.14.0-pre.13/ngrok/config/struct.HttpTunnelBuilder.html#method.listen\_and\_forward](https://docs.rs/ngrok/0.14.0-pre.13/ngrok/config/struct.HttpTunnelBuilder.html#method.listen_and_forward) The ngrok Kubernetes Operator always forwards its traffic. All of our other examples show the most common forwarding case: a `Service` object that defines a label selector of matching pods to forward traffic to. But you can also [forward to an explicit set of IP addresses](https://kubernetes.io/docs/concepts/services-networking/service/#services-without-selectors) on the same network using `Service` and `EndpointSlice` objects. ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: example-service spec: ports: - protocol: TCP port: 80 targetPort: 80 --- apiVersion: discovery.k8s.io/v1 kind: EndpointSlice metadata: name: example-service-1 labels: kubernetes.io/service-name: example-service addressType: IPv4 ports: - name: "http" appProtocol: http protocol: TCP port: 80 endpoints: - addresses: - "192.168.1.2" --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: ingressClassName: ngrok rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ### HTTP/2 forwarding When agents forward to upstream http/2 services, connections use HTTP/1.1 by default. You can configure the agent, SDKs, and Kubernetes Operator to instead use HTTP/2 when forwarding to your upstream service. | Forwarder | Option | Docs | | - | - | - | | Agent | `--upstream-protocol` | [Agent CLI flags](/docs/gateway/agent/cli/#ngrok-http) | | Agent SDKs | language-dependent | [Agent SDKs](/docs/agent-sdks) | | Kubernetes Operator | `appProtocol` on the `Tunnel` CRD | [Kubernetes Operator](/docs/k8s) | When http2 forwarding is enabled, all requests to your upstream service will be transmitted over HTTP/2 Cleartext since TLS was already terminated at the ngrok cloud service. [TLS-ALPN](https://httpwg.org/specs/rfc7540.html#TLS-ALPN) cannot be used at this time. ngrok relies on [HTTP/2 with Prior Knowledge](https://httpwg.org/specs/rfc7540.html#known-http) currently. ### Serving file directories The ngrok agent supports the `file://` scheme in a forwarding URL. When you use the `file://` scheme, the ngrok agent serves local file system directories by using its own built-in file server, no separate server needed. It works just like `python3 -m http.server` but it is built directly into the ngrok agent. Paths in `file://` URLs must be specified as absolute paths. #### Serve files in `/var/log` ```bash theme={null} ngrok http "file:///var/log" ``` ```yaml theme={null} version: 3 endpoints: - name: example url: "https://example.ngrok.app" upstream: url: "file:///var/log" ``` Serving directory files is not supported via SSH. ```go theme={null} import ( "context" "net" "net/http" "golang.ngrok.com/ngrok/v2" ) func serveFiles(ctx context.Context) error { l, _ := ngrok.Listen(ctx) http.Serve(l, http.FileServer(http.Dir("/var/log"))) } ``` Serving directory files is not supported in the Javascript SDK. Serving directory files is not supported in the Python SDK. Serving directory files is not supported in the Rust SDK. Serving directory files is not supported in the ngrok Kubernetes Operator.
#### Serve files on Windows ```bash theme={null} ngrok http "file://C:\Users\alan\Directory Name" ``` ```yaml theme={null} version: 3 endpoints: - name: example url: "https://example.ngrok.app" upstream: url: "file://C:\\Users\\alan\\Directory Name" ``` Serving directory files is not supported via SSH. ```go theme={null} import ( "context" "net" "net/http" "golang.ngrok.com/ngrok/v2" ) func serveFiles(ctx context.Context) error { l, _ := ngrok.Listen(ctx) http.Serve(l, http.FileServer(http.Dir("C:\\Users\\alan\\Directory Name"))) } ``` Serving directory files is not supported in the Javascript SDK. Serving directory files is not supported in the Python SDK. Serving directory files is not supported in the Rust SDK. Serving directory files is not supported in the ngrok Kubernetes Operator.
#### Serve files in your current working directory ```bash theme={null} ngrok http file://`pwd` ``` Serving the current working directory is not supported via the agent configuration file. Serving directory files is not supported via SSH. ```go theme={null} import ( "context" "net" "net/http" "golang.ngrok.com/ngrok/v2" ) func serveFiles(ctx context.Context) error { l, _ := ngrok.Listen(ctx) http.Serve(l, http.FileServer(http.Dir("."))) } ``` Serving directory files is not supported in the Javascript SDK. Serving directory files is not supported in the Python SDK. Serving directory files is not supported in the Rust SDK. Serving directory files is not supported in the ngrok Kubernetes Operator. ## Traffic observability ### Traffic Inspector [Traffic Inspector](/docs/obs/traffic-inspection/) gives you a real-time view in the ngrok dashboard of the HTTP traffic flowing through your HTTP/S endpoints. You can choose whether Traffic Inspector captures only request metadata or full request and response bodies. ### Log export logs You can export logs of traffic to HTTP/S endpoints with [ngrok's events system](/docs/obs/). The following events are published for log exporting: | Log | When | | - | - | | [http\_request\_complete.v0](/docs/obs/events/reference/#http-request-complete) | Published when an HTTP request to an HTTP/S endpoints completes. | | [tcp\_connection\_closed.v0](/docs/obs/events/reference/#tcp-connection-closed) | Published when a TCP connection to an HTTP/S endpoints completes. | ## Advanced HTTP/S endpoints are standards-compliant HTTP reverse proxies. ### Versions * HTTP/S endpoints support HTTP/1.1. * HTTPS endpoints support HTTP/1.1 and HTTP/2. * HTTP/1.0, HTTP/3 and QUIC are **not** supported. ### HTTP/2 HTTPS endpoints will automatically use HTTP/2 for all connections if the client supports it. Client support is determined via standard ALPN negotiation. HTTP/2 is used between the client and your endpoint even even if your upstream service does not support HTTP/2. The section on [HTTP/2 agent forwarding](#http2-forwarding) has details on how to configure the use of HTTP/2 when sending traffic to an upstream service. ### Websockets Websocket connections are supported out-of-the-box. No configuration is required. ### Hop by hop headers ngrok does not forward any [hop-by-hop headers](https://datatracker.ietf.org/doc/html/rfc2616#section-13.5.1) to the upstream service. As an exception, `Connection: upgrade` headers are forwarded to support [websockets](#websockets). For information on headers added automatically by ngrok, see [Upstream Headers](#upstream-headers). ### Persistent connections When a connection is made to HTTP/S ngrok endpoints with HTTP/1.1, ngrok may choose to use persistent connections (such as HTTP keep-alive) to improve the performance of future requests from the same client if the client supports it. This behavior is not guaranteed and it is not configurable. See [RFC 7230](https://datatracker.ietf.org/doc/html/rfc7230#section-6.3) for additional details. ### Well known URIs #### `/.well-known/acme-challenge` ngrok takes over handling of this path of any HTTP endpoint matching a [Domain](/docs/domains) with automated certificate management enabled. You may disable this behavior by uploading your own certificate on the matching Domain. ## TLS ngrok automatically handles TLS (SSL) certificate management and termination for you. There is nothing to set up, configure, or manage. TLS connections to `https` endpoints are terminated at ngrok's cloud service. If you wish to terminate TLS traffic at the ngrok agent or in your upstream application, use a [TLS Endpoint](/docs/gateway/endpoints/tls) instead. Consult the following documentation for additional details on how ngrok handles TLS termination and certificiate management: * [TLS Certificates](/docs/gateway/domains/tls-certificates) * [TLS Termination](/docs/gateway/domains/tls-termination) ## Upstream headers ngrok adds headers to each HTTP request with information about the original client IP, request scheme and request `host` header value. | Header | Description | | - | - | | `x-forwarded-for` | The IP address of the client who initiated the request. If this header exists on the original request, ngrok will append a new value. | | `x-forwarded-proto` | The scheme of the original request, either `http` or `https`. If this header exists on the original request, ngrok will append a new value. | | `x-forwarded-host` | The header from the client's request if it existed, otherwise is set to the request's `Host` header value. | Because ngrok appends values to `x-forwarded-for` and `x-forwarded-proto`, be sure to use the last value of the header in your application code to read the values injected by ngrok. You may remove these headers with the [Remove Headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action. ## Limits & timeouts [Contact Support](mailto:support@ngrok.com) if you need to configure limits and timeouts on connections to HTTP endpoints. ### Connection | Limit | Name | Notes | | - | - | - | | 5 minutes | Client Idle Timeout | Time since data was last transmitted by the upstream service | | 5 minutes | Server Idle Timeout | Time since data was last transmitted by the upstream service | | No limit | Data transmitted | Data transmitted by the client or upstream service | ### TLS | Limit | Name | Notes | | - | - | - | | 60 seconds | TLS Handshake Duration | Time between ClientHello received and handshake completion | | 64 KB | Handshake Message Size | Max size of non-certificate handshake messages | | 256 KB | Certificate Message Size | Max size of certificate handshake messages | | 16 KB | Record Payload Size | | ### HTTP | Limit | Name | Notes | | - | - | - | | No timeout | Round Trip Timeout | Time for the entire HTTP request and response | ### HTTP request | Limit | Name | Notes | | - | - | - | | 1 MB | Request Header Size | Includes method, URI, and headers | | 1 MB | Request URI Length | Limited by the size of the request header | | No timeout | Request Timeout | Time to read the entire HTTP request from the client | | No timeout | Request Header Timeout | Time to read the HTTP request header from the client | | No limit | Request Body Size | | ### HTTP response | Limit | Name | Notes | | - | - | - | | 1 MB | Response Header Size | Includes method, URI, and headers | | No timeout | Response Timeout | Time to read the entire HTTP response from the server | | No timeout | Response Header Timeout | Time to read the HTTP response header from the server | | No limit | Response Body Size | | ## Errors If ngrok fails to handle an HTTP request it will set the `ngrok-error-code` header in the HTTP response with a [unique ngrok Error Code](/docs/errors/) describing the failure. ngrok guarantees that the upstream service may never set the `ngrok-error-code` HTTP response header so you know reliably that it was set by ngrok. ngrok may return an error under the following conditions: * Your upstream service timed out or rejected the connection * Your upstream service returned a response that was not valid HTTP * A [Traffic Policy](/docs/traffic-policy) action rejected the request. * [Traffic Policy](/docs/traffic-policy) execution encountered a runtime error. * ngrok encountered an internal error ## API HTTP/S Endpoints can be created programmatically. Consult the documentation on \[Endpoint APIs]\(/api-reference/endpoints/list. ## HTTP pricing HTTP/S endpoints are available on all plans. Consult the [Endpoints Pricing](/docs/gateway/endpoints/#pricing) documentation for billing details. See [Domains pricing](/docs/gateway/domains/#pricing) for details on pricing for custom domains, wildcard endpoints and more. # Endpoints Overview Source: https://ngrok.com/docs/gateway/endpoints/index Create and manage ngrok Endpoints to deliver and manage traffic to your services. An **ngrok Endpoint** is a URL that enables network traffic to reach your services. You can think of endpoints as a gateway to anything you want to enable access for—whether that's a local development server on your laptop, a production Kubernetes cluster, a database behind a corporate firewall, or a cloud API. Endpoints bridge the gap between your resources and the traffic that needs to reach them. ## Why endpoints matter Endpoints power ngrok's Gateway, letting you: * **Go live instantly**: Enable access to local prototypes, internal tools or [Kubernetes](/docs/gateway/endpoints/kubernetes-endpoints/) services in seconds. * **Talk to anything**: APIs, Kubernetes clusters, game servers, third-party tools, IoT devices, legacy apps—endpoints support them all. * **Secure access**: Effortlessly lock down access with encryption, authentication, or IP restrictions to whatever you need without code changes. * **Observability:** Instantly gain insights into how traffic is flowing through endpoints with Traffic Inspector and Log Exporting. * **Simplify your stack**: Reduce infrastructure sprawl by chaining endpoints and embedding logic directly into traffic flow—rate limiting, mock responses, or security policies, executed at the edge with no code required. ## How endpoints work Endpoints enable traffic flow and management through three core features: * **URL:** The entry point for traffic to reach your services. This could be any URL, such as `https://your-app.ngrok.io` or `tcp://your-api.internal`. * **[Binding](#control-access-with-bindings):** The source or origin of incoming traffic. * **[Traffic Policy](/docs/gateway/traffic-policy/):** Configuration to manage, manipulate and secure traffic. ### Control access with bindings An endpoint's [binding](/docs/gateway/endpoints/bindings) defines *where traffic originates*: * **Public:** Accepts traffic from the open internet (for example, `https://your-app.ngrok.io`). * **Internal:** Restricted to accept traffic only through the [`forward-internal` Traffic Policy action](/docs/gateway/traffic-policy/actions/forward-internal/) (for example, `http://your-api.internal`). * **[Kubernetes](/docs/gateway/k8s/):** Restricted to accept traffic only from Kubernetes clusters using the ngrok Kubernetes Operator. ### Set protocol via URL scheme You can specify your endpoint's URL protocol to indicate the type of traffic it should handle. This allows you to configure your endpoint to match your services and application type: * `HTTP/HTTPS`: Best for Webhooks, APIs, apps * `TCP`: Best for raw streams (SSH, gaming, IoT) * `TLS`: Best for custom encrypted workloads ### Manage, manipulate and secure traffic through policies Add business logic and route traffic during the request lifecycle with [Traffic Policy](/docs/gateway/traffic-policy/): * **Route traffic by anything:** Paths, headers or any other available variable. * **Manipulate traffic through [actions](/docs/gateway/traffic-policy/actions/):** Redirect urls, inject headers or send custom responses. * **Secure:** Add [OAuth](/docs/gateway/traffic-policy/actions/oauth/), [IP restrictions](/docs/gateway/traffic-policy/actions/restrict-ips) or [mutual TLS](/docs/gateway/traffic-policy/actions/terminate-tls/#enabling-mutual-tls). ### Deliver traffic anywhere You can host endpoints on your local machine with the [ngrok Agent](/docs/gateway/agent/) or in the cloud with [ngrok Cloud Endpoints](/docs/gateway/endpoints/cloud-endpoints/). * **[Cloud Endpoints](/docs/gateway/endpoints/cloud-endpoints):** * Persistent and globally available. * Start with the Dashboard or API. * Great for centralized configuration, multi-service routing and service failover. * **[Agent Endpoints](/docs/gateway/endpoints/agent-endpoints):** * Ephemeral and environment agnostic. * Start with the CLI, Language SDKs or through Kubernetes. * Great for ephemeral workloads, local development, scaling services and device management. See [Cloud Endpoints vs Agent Endpoints](/docs/gateway/types#cloud-endpoints-vs-agent-endpoints) for a side-by-side comparison. ## Get started Enable access to a local web app on port `8080` with the [ngrok CLI](/docs/gateway/agent/cli): ```bash theme={null} ngrok http 8080 ``` This command will start an Agent Endpoint that forwards traffic through the ngrok CLI agent to your locally running application on port 8080. To learn more, follow ngrok's [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/). ## Load balancing with Endpoint Pooling [Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling) allows you to create multiple endpoints with the same URL. Traffic sent to a URL with an Endpoint Pool is [load-balanced](/docs/gateway/domains/global-load-balancer/) among the Endpoints in the pool. See the [Endpoint Pooling quickstart](/docs/gateway/endpoints/endpoint-pooling/#quickstart) to learn more. ## Managing endpoints with ngrok's APIs [Cloud Endpoints](/docs/gateway/endpoints/cloud-endpoints/) are programmatically managed via: * \[`/endpoints` API Resource]\(/api-reference/endpoints/list of the [API](/docs/api/) * [`CloudEndpoint` CRD](/docs/gateway/k8s/crds/) of the [Kubernetes Operator](/docs/gateway/k8s/) [Agent Endpoints](/docs/gateway/endpoints/agent-endpoints), by contrast, are programtically managed via: * [Agent SDKs](/docs/agent-sdks/) * [Agent API](/docs/gateway/agent/api/) * [Kubernetes Operator](/docs/gateway/k8s/installation/helm) Agent Endpoints are also accessible in a read-only capacity via: * [`/endpoints` API Resource](/docs/api-reference/endpoints/list) of the [API](/docs/api) ## Endpoints pricing Free and Hobbyist plans allow you to use your included credit to start endpoints. On the Pay-as-you-go plan, there is no limit on the number of endpoints you can create. An endpoint that transmits data in a clock hour is counted as an active endpoint for that hour and charged an endpoint hour. See [Pricing](https://ngrok.com/pricing) for more information. ## Learn more * **[Cloud Endpoints](/docs/gateway/endpoints/cloud-endpoints):** Create always-on endpoints to deliver and manage traffic to custom responses, internal endpoints or third-party services. * Great for centralized configuration and routing across multiple services and handling failover when your upstream applications and services are offline. * **[Agent Endpoints](/docs/gateway/endpoints/agent-endpoints):** Create an endpoint that only exists as long the agent is running to deliver and manage traffic to your local applications, files, services or third-party services. * Great for load balancing and managing individual APIs, services, applications, devices and forwarding traffic to third-party destinations. # Internal Agent Endpoints Source: https://ngrok.com/docs/gateway/endpoints/internal-endpoints Learn about staging internal services and applications using internal endpoints. **Internal Endpoints** are private endpoints that only receive traffic when forwarded through the [`forward-internal` Traffic Policy action](/docs/gateway/traffic-policy/actions/forward-internal/). This allows you to route traffic to an application through ngrok without making it publicly addressable. Internal endpoint URLs must: * End with the `.internal` domain extension * Use the `internal` [binding](/docs/gateway/endpoints/bindings/) Internal domains are [namespaced](#namespacing) to the accounts that create them. Even if another user has the same internal endpoint URL as you, your traffic will never flow to their endpoint. A common practice is to use a [public](/docs/gateway/endpoints/public-endpoints) [Cloud Endpoints](/docs/gateway/endpoints/cloud-endpoints/) to manage a centralized Traffic Policy, then forward traffic to one or more internal [endpoints](/docs/gateway/endpoints/). See [the guide on using Cloud Endpoints with internal endpoints](/docs/gateway/endpoints/cloud-endpoints/forwarding-and-load-balancing) to learn more. ## Quickstart Create an internal Agent Endpoint by specifying a binding of `internal` when you create an endpoint. ```bash theme={null} ngrok http 8080 --url "https://example.internal" --binding internal ``` ##### `traffic-policy.yml` ```yaml theme={null} on_http_request: - actions: - type: custom-response config: status_code: 200 headers: content-type: text/plain body: "Hello world!" ``` ### Forwarding Internal endpoints aren’t directly accessible. To send traffic to an Internal Endpoint, you must create an endpoint that uses the Traffic Policy to forward traffic using the [`forward-internal` action](/docs/gateway/traffic-policy/actions/forward-internal/). For example, to route public traffic to your internal endpoint, use the following command and Traffic Policy to create a Public Cloud Endpoint that forwards requests to your Internal Endpoint: ```bash theme={null} ngrok api create \ --type cloud \ --url "https://your-name.ngrok.app" \ --traffic-policy "$(cat traffic-policy.yml)" ``` ##### `traffic-policy.yml` ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://example.internal ``` Now, when you make a request to `https://your-name.ngrok.app`, ngrok applies the specified Traffic Policy. This policy instructs ngrok to use the forward-internal action, routing the incoming HTTP request directly to your internal endpoint at `https://example.internal`. This configuration lets you manage how traffic reaches your service without requiring it to be directly accessible on the public internet. ## Inferred binding ngrok automatically infers an `internal` binding for any endpoint created with a `.internal` hostname, so you can omit the `binding` property in most cases. For example, the following command creates an endpoint with an internal binding: ```bash theme={null} ngrok http 8080 --url "https://example.internal" ``` ## URLs * **Scheme** - Internal endpoints support all [Endpoint Protocols](/docs/gateway/endpoints/protocols) (`http`, `https`, `tcp` and `tls`). * **Hostname** - Hostnames **must** end with `.internal` and may contain up to 5 subdomains. You must specify a URL with a hostname, randomly assigned hostnames are not supported. You may specify wildcard hostnames. * **Port** - All port numbers \[1-65535] are valid. For `tcp` endpoints, you **must** specify a port. ## Namespacing Internal endpoints are namespaced on a per-account basis. This means that, two different accounts may have internal endpoints with the same URL (for example, `https://api.internal`) without conflict or interference in [endpoint pooling](/docs/gateway/endpoints/endpoint-pooling). As a result, there is no need to reserve an internal domain. #### Examples * `https://example.internal` * `https://example.foo.bar.internal` * `https://example.internal:12345` * `tls://example.internal` * `tls://example.internal:12345` * `tcp://example.internal:12345` ## Type and pooling * Internal endpoints support both [Endpoint Types](/docs/gateway/types): `agent` and `cloud`. * Internal endpoints support [Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling). ## HTTP/S `on_tcp_connect` phase For internal `http` and `https` endpoints, the `on_tcp_connect` [Traffic Policy](/docs/gateway/traffic-policy/) phase is not applicable and never triggers. This is because when you use the [`forward-internal` action](/docs/gateway/traffic-policy/actions/forward-internal/) to forward traffic to an `http` or `https` endpoint, no new TCP connection is established. For additional details, see the [`forward-internal` documentation](/docs/gateway/traffic-policy/actions/forward-internal/). ## API Internal endpoints can be managed programmatically. To get started, consult the \[Endpoints API documentation]\(/api-reference/endpoints/list. ## Internal endpoints pricing Internal endpoints are available on the Pay-as-you-go plan. Consult the [Endpoints Pricing](/docs/gateway/endpoints/#pricing) documentation for billing details. # Kubernetes Agent Endpoints Source: https://ngrok.com/docs/gateway/endpoints/kubernetes-endpoints Learn about Kubernetes endpoints that provide secure, private connectivity to ngrok endpoints within Kubernetes clusters. Kubernetes endpoints are secure, private endpoints that are only addressable inside of Kubernetes clusters where you install the [Kubernetes operator](/docs/gateway/k8s/). They enable you to connect to ngrok endpoints without making them publicly addressable. Kubernetes endpoints have a [binding](/docs/gateway/endpoints/bindings) of `kubernetes`. ## Quickstart Create a Kubernetes endpoint by specifying binding of `kubernetes` when you create an endpoint. ### 1. Install the ngrok Kubernetes Operator ```bash theme={null} helm install ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --create-namespace \ --set description="example Operator" \ --set bindings.enabled=true \ --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN ``` ### 2. Create a new namespace ```bash theme={null} kubectl create ns prod ``` ### 3. Create an ngrok endpoint Run the following command in the same ngrok account to create the kubernetes bound endpoint. After the command completes, the ngrok Operator will [create `Service` objects](#service-creation) in the kubernetes cluster matching the endpoint's URL. ```bash theme={null} ngrok http 80 --url http://customer-2.prod --binding kubernetes ``` ### 4. Connect to the endpoint Other pods in the Kubernetes cluster where the ngrok Operator is running can connect to the bound endpoint. ```bash theme={null} $ kubectl run -i --tty --rm debug --restart=Never --image=appropriate/curl -- /bin/sh # curl http://customer-2.prod ``` Now your application is connected via a private Kubernetes endpoint. ## URLs Kubernetes endpoint URL hostnames must be in the following format: * `[http|tcp]://name.namespace[:port]` The following restrictions are enforced: * **Scheme** - Must be `http` or `tcp`. `https` and `tls` are not supported. * **Hostname** - Hostnames must always be two parts separated by a single dot, for example, `foo.bar`. Wildcard hostnames are not allowed. * **Port** - All port numbers \[1-65535] are valid. Port must be specified for `tcp` endpoints. * **Namespacing** - Kubernetes endpoints are namespaced on a per-account basis. Two accounts may have kubernetes endpoints with the same URL (for example, [http://api.internal](http://api.internal)). Those endpoints will not conflict or [pool](/docs/gateway/endpoints/endpoint-pooling). ### URL examples * `http://app.example` * `http://app.example:12345` * `tcp://app.example:443` * `tcp://app.example:12345` * `https://app.example` - invalid scheme `https` * `tls://app.example:12345` - invalid scheme `tls` * `http://app.foo.bar` - invalid hostname, must have only two parts * `tcp://app.example` - tcp endpoint must specify port number ## Types and pooling * Kubernetes endpoints support all [Endpoint Types](/docs/gateway/types) (`agent` and `cloud`). * Kubernetes endpoints support [Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling). ## Service creation After a kubernetes-bound endpoint is created, the ngrok cloud service notifies Kubernetes Operators that a new kubernetes-bound endpoint exists. Kubernetes Operators create `v1.Service` objects in their Kubernetes clusters which forward traffic they receive to the operators' pods. ### Cluster IP service A Cluster IP service is created in the Operator's namespace. ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: # the endpoint's ID namespace: ngrok-operator spec: selector: app.kubernetes.io/name: ngrok-operator-forwarder ports: - protocol: TCP port: 80 # the endpoint url's port targetPort: # assigned by Operator to target the ngrok-operator-forwarder container ``` ### External name service An External Name service is created in the namespace targeted by the second part of the URL's hostname. ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: customer-2 # first part of the URL's hostname namespace: prod ## second part of the URL's hostname spec: # Point service at the Operator Forwarder type: ExternalName externalName: .ngrok-operator.svc.cluster.local ``` ## How to update endpoint selectors If you don't want all kubernetes endpoints in your account to appear inside of a cluster, you may specify an Endpoint Selector which filters which Kubernetes endpoints are projected into the cluster it runs in. Endpoint Selectors are a CEL expression which is evaluated against each Kubernetes Endpoint in your account. The Operator will only projects endpoints that the selector returns `true` for. See [the docs on enabling bindings](/docs/gateway/k8s/guides/bindings/#enable-bindings-for-the-operator) to learn more. ## How to enable or disable Kubernetes endpoints on an existing installation By default, if you don't change the default endpoint selector, all endpoints with a Kubernetes binding will be bound to the cluster the Operator is installed on. Changing the binding allows you to restrict which endpoints that Operator will handle. See [the docs on enabling bindings](/docs/gateway/k8s/guides/bindings/#enable-bindings-for-the-operator) to learn more. ## Coming soon This feature is in developer preview—more documentation is coming soon. ## API Kubernetes endpoints can be created programmatically. Consult the documentation on \[Endpoint APIs]\(/api-reference/endpoints/list. ## Kubernetes endpoints pricing Kubernetes endpoints are available on the Pay-as-you-go plan. Consult the [Endpoints Pricing](/docs/gateway/endpoints/#pricing) documentation for billing details. # Load Balancing Between Multiple Clouds Source: https://ngrok.com/docs/gateway/endpoints/load-balancing-multiple-clouds Learn how to load balance traffic between multiple clouds with ngrok. ngrok's [Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling) allows you to automatically load balance between replicas of your service across multiple clouds by creating two endpoints with the same URL. This guide will show you how to set up endpoint pooling with a few different methods. This guide will reference reserved domains frequently. You can reserve a free domain in the [dashboard](https://dashboard.ngrok.com/domains) or use a custom domain you own. ## Why use Endpoint Pooling? ngrok's Endpoint Pooling: * Works with the [agent CLI](/docs/agent), [SDKs](/docs/agent-sdks/) and the [Kubernetes Operator](/docs/k8s). You can even use all these tools in a single pool. * Lets you migrate from one deployment strategy to another without hard cut-overs, like moving from using the agent for ingress to embedding ngrok directly in your app/API with an SDK. * Works with [Traffic Policy](/docs/traffic-policy), enabling you to manage traffic identically across all your replicas from a single "front door," or enforce different policies for each replica. ## Using the CLI Start with a single [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/) on an ngrok URL, or a [reserved domain](https://dashboard.ngrok.com/domains), with the `--pooling-enabled` flag on. ```bash mode="cli" theme={null} ngrok http 8000 --url your-reserved-domain --pooling-enabled=true ``` Repeat the same steps on another cloud where you've deployed a replica of your service. This will start a second internal Agent Endpoint on the same URL. ```bash mode="cli" theme={null} ngrok http 8000 --url your-reserved-domain --pooling-enabled=true ``` ### Test your pool Send a few requests to your domain to see responses balanced between those two endpoints, even though they're running in multiple clouds. You can verify pooling works in a few ways: 1. Check each agent's UI in the terminal that launched the process. You should see an increase in the number of recent requests. 2. In [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector), select a request, then select the **Response** tab. Under **Ngrok-Agent-Ips** you should see that the IP for the responding agent changes from request to request with a similar frequency. ## Using SDKs and other tools Pooling isn't limited to how you start a given service or its replicas. If you've already started up one Agent Endpoint with the CLI, you can start up another with one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s) to load-balance between them. The following is a simple example of enabling pooling with one of the SDKs. ```go title="example.go" theme={null} package main import ( "context" "fmt" "log" "os" "github.com/joho/godotenv" "golang.ngrok.com/ngrok/v2" ) func main() { if err := godotenv.Load(".env"); err != nil { log.Fatal("Error loading .env file", err) } if err := run(context.Background()); err != nil { log.Fatal(err) } } const address = "http://localhost:8000" func run(ctx context.Context) error { agent, err := ngrok.NewAgent(ngrok.WithAuthtoken(os.Getenv("NGROK_AUTHTOKEN"))) if err != nil { return err } ln, err := agent.Forward(ctx, ngrok.WithUpstream(address), ngrok.WithURL("your-reserved-domain"), ngrok.WithPoolingEnabled(true), ) if err != nil { fmt.Println("Error", err) return err } fmt.Println("Endpoint online: forwarding from", ln.URL(), "to", address) // Explicitly stop forwarding; otherwise it runs indefinitely <-ln.Done() return nil } ``` ```javascript title="example.js" theme={null} require('dotenv').config(); const ngrok = require('@ngrok/ngrok'); (async function() { const session = await new ngrok.SessionBuilder() .authtokenFromEnv() .connect(); // Endpoint pooling is currently only supported through the builder API, not ngrok.forward(). const listener = await session.httpEndpoint() .domain("your-reserved-domain") .poolingEnabled(true) .listenAndForward("http://localhost:8000"); // Output ngrok URL to console console.log(`Ingress established at ${listener.url()}`); })(); // Keep the process alive process.stdin.resume(); ``` ```rust title="src/main.rs" theme={null} #![deny(warnings)] use ngrok::config::ForwarderBuilder; use url::Url; #[tokio::main] async fn main() -> Result<(), Box> { // Set up ngrok tunnel let session = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; // Forward HTTP traffic from ngrok to the local server let _listener = session .http_endpoint() .domain("your-reserved-domain") .pooling_enabled(true) .listen_and_forward(Url::parse("http://localhost:8000").unwrap()) .await?; println!("ngrok tunnel established at {}", domain); // Wait indefinitely tokio::signal::ctrl_c().await?; Ok(()) } ``` ```python title="example.py" theme={null} import asyncio from dotenv import load_dotenv import ngrok load_dotenv() async def main(): session = await ngrok.SessionBuilder().authtoken_from_env().connect() # Endpoint pooling is currently only supported through the builder API, not ngrok.forward(). listener = await ( session.http_endpoint() .domain("your-reserved-domain") .pooling_enabled(True) .listen_and_forward("http://localhost:8000") ) # Output ngrok URL to console print(f"Ingress established at {listener.url()}") # Keep the listener alive while True: await asyncio.sleep(1) asyncio.run(main()) ``` Run your app. If you already have a pool of endpoints created by agent CLIs, make a few requests to see responses from your app using an SDK. ## Using Cloud Endpoints You can use Endpoint Pooling with Cloud Endpoints for custom traffic management. Pooling works with [internal Agent Endpoints](/docs/gateway/endpoints/internal-endpoints/), which can only receive traffic from other endpoints associated with your ngrok account. A [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/) is a persistent public endpoint where you can manage a Traffic Policy for any upstream endpoints it forwards traffic to. Forwarding traffic from a Cloud Endpoint to a pool of internal endpoints gives you a single "front door" for your multicloud load-balanced services. ### 1. Set up your internal Agent Endpoints First, set up your internal Agent Endpoints. To pool traffic with a Cloud Endpoint, you must: 1. Stop any agents you've already created associated with the URL you want to use. 2. Restart those agents as internal endpoints by giving them any URL with a TLD of `.internal`. ```bash title="Create an internal Agent Endpoint" theme={null} ngrok http 8080 --url "https://example.internal" ``` ### 2. Set up your Cloud Endpoint Next, set up your Cloud Endpoint: 1. Navigate to the [**Endpoint** section](https://dashboard.ngrok.com/endpoints) of your ngrok dashboard and select **New**, then **Cloud Endpoint**. 2. Leave the binding as **Public** and enter `your-reserved-domain` for the domain. 3. Select **Create Cloud Endpoint**. ### 3. Add a Traffic Policy Now you can add a Traffic Policy. While viewing your Cloud Endpoint in the dashboard, add the following Traffic Policy. It uses the `forward-internal` action to route all requests to your pool of internal Agent Endpoints. ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://example.internal ``` Be sure to select the **Save** button to apply the policy. Now when you make a request to your URL, your traffic is first routed through the Cloud Endpoint before being shared among your pooled internal Agent Endpoints. Custom load balancing strategies are not yet generally available, but you can request Early Access in your [ngrok dashboard](https://dashboard.ngrok.com/early-access). With custom load balancing strategies, you'll be able to decide exactly what happens to load-balanced traffic in certain scenarios, like: * Balance randomly among endpoints in a single cloud provider, then fall back to a secondary cloud if they all become unavailable. * Prioritize endpoints with the most memory regardless of which cloud they were deployed from. * Route traffic to specific cloud providers depending on where the request originated from. ## What's next? If you opted for a Cloud Endpoint that routes traffic to a pool of internal Agent Endpoints, you can now filter, manage, and orchestrate traffic from that single endpoint using Traffic Policy. Here are a few of the most popular actions: * [`oauth`](/docs/gateway/traffic-policy/actions/oauth) * [`url-redirect`](/docs/gateway/traffic-policy/actions/redirect) * [`set-vars`](/docs/gateway/traffic-policy/actions/set-vars) * [`add-headers`](/docs/gateway/traffic-policy/actions/add-headers) Kubernetes deployments are not covered in this guide, but a similar quickstart guide is available for [load-balancing K8s services](/docs/gateway/k8s/load-balancing/load-balancing-kubernetes). If you're interested in a more production-ready deployment, take a look at the [multicloud API gateway setup tutorial](/docs/gateway/api-gateway/multicloud). # Agent Endpoint Protocols Source: https://ngrok.com/docs/gateway/endpoints/protocols Learn about the four endpoint protocols supported by ngrok: HTTP/S, TCP, and TLS, and how they determine traffic processing. Endpoints have one of four supported **protocols**: * [HTTP/S Endpoints](/docs/gateway/endpoints/http) - `http`, `https` * [TCP Endpoints](/docs/gateway/endpoints/tcp) - `tcp` * [TLS Endpoints](/docs/gateway/endpoints/tls) - `tls` An Endpoint's protocol is specified in the scheme of its URL. For example: * `https://app.example.com` uses the `https` protocol * `tcp://db.internal:3306` uses the `tcp` protocol An Endpoint's protocol determines: 1. How it processes connections that it receives 2. The phases, variables, and actions that may be defined on its [Traffic Policy](/docs/traffic-policy). 3. What hostnames and ports may be specified in its URL Learn more about in the [Endpoints documentation](/docs/endpoints). # Public Agent Endpoints Source: https://ngrok.com/docs/gateway/endpoints/public-endpoints Learn about enabling internet traffic to your services with public endpoints. Public endpoints enable your services to receive geo-balanced internet traffic using [ngrok's global points of presence](/docs/gateway/edges/). Public endpoints enable you to receive traffic from the internet via ngrok’s [global points of presence](/docs/gateway/edges/). This is the most common type of endpoint and the easiest way to start. Public endpoint URLs must: * Use either a [Domain](/docs/gateway/domains/) or [TCP Address](/docs/gateway/domains/tcp-addresses/) * Use the `public` [binding](/docs/gateway/endpoints/bindings/) ## Get started You can create a public endpoint using: * [The dashboard](https://dashboard.ngrok.com/endpoints) * [The API](/docs/api-reference/endpoints/create). Follow the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/) to set up an ngrok account and create your first public endpoint. ## Authentication Public endpoints are useful out of the box for content you don’t want to restrict access to, such as blogs or marketing sites; but they can also be used for services that require strict access control such as dashboards or private APIs. To keep such services both public and secure, you can add a Traffic Policy to your endpoint for authentication and authorization. Traffic Policy actions enable you to do the following with your endpoints: * [JWT Validation](/docs/gateway/traffic-policy/actions/jwt-validation/) * [OAuth](/docs/gateway/traffic-policy/actions/oauth/) * [OIDC](/docs/gateway/traffic-policy/actions/oidc/) * [IP restrictions](/docs/gateway/traffic-policy/actions/restrict-ips/) * [Mutual TLS](/docs/gateway/traffic-policy/actions/terminate-tls/#example-traffic-policy-document) ## URLs When you create a public endpoint, the URL you create it with is validated differently based on the protocol of the URL. ### `http` * The hostname must match a [Domain](/docs/gateway/domains/) in your account. * The hostname must be a domain with a valid [public suffix](https://publicsuffix.org/). * The port must be `80`. If you do not specify a port, the default `80` will be used for you. **Examples** * `http://example.ngrok.app` * `http://example.ngrok.app:80` * `http://example.party` * `http://example.ngrok.app:81` - invalid port, must be `80`, not `81` * `http://example.doesnotexist` - invalid hostname: `.doesnotexist` is not a public suffix domain * `http://example.internal` - invalid hostname: `.internal` is not a public suffix domain ### `https`, `tls` * The hostname must match a [Domain](/docs/gateway/domains/) in your account. * The hostname must be a domain with a valid [public suffix](https://publicsuffix.org/). * The port must be `443`. If you do not specify a port, the default `443` will be used for you. **Examples** * `https://example.ngrok.app` * `https://example.ngrok.app:443` * `https://example.party` * `https://example.ngrok.app:8443` - invalid port, must be `443` * `https://example.nosuchtld` - invalid hostname, `.nosuchtld` is not a public suffix domain * `https://example.internal` - invalid hostname, public endpoints cannot use `.internal` ### `tcp` * You must specify a port number in TCP URLs. * The hostname and port must match the address of a [TCP Address](/docs/gateway/domains/tcp-addresses) in your account. **Examples** * `tcp://1.tcp.ngrok.io:12345` * `tcp://1.tcp.eu.ngrok.io:12345` * `tcp://1.tcp.ngrok.io` - invalid, does not specify a port number * `tcp://example.ngrok.app:12345` - invalid, does not match a TCP Address * `tcp://your-domain.com:12345` - invalid, does not match a TCP Address ## API Public endpoints can be created programmatically. Consult the documentation on \[Endpoint APIs]\(/api-reference/endpoints/list. ## Public endpoints pricing Public endpoints are available on all plans. Consult the [Endpoints Pricing](/docs/gateway/endpoints/#pricing) documentation for billing details. # TCP Agent Endpoints Source: https://ngrok.com/docs/gateway/endpoints/tcp Learn how to create and configure TCP endpoints with ngrok for routing raw TCP traffic to your applications and services. TCP endpoints enable you to deliver any network service with a TCP-based protocol. They are commonly used to create connectivity for: * Remote access protocols like SSH, VNC and RDP * Databases like MySQL, Postgres, MSSQL and SQLite * IoT protocols like MQTT * Gaming servers like Minecraft If you are accepting TLS traffic, you may prefer to create a [TLS Endpoint](/docs/gateway/endpoints/tls/). TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. ## Quickstart Agent Endpoints are the easiest way to get started with ngrok. An [agent endpoint](/docs/gateway/endpoints/cloud-endpoints/) is started by a [Secure Tunnels](/docs/gateway/agent/) agent. The endpoint lives for the lifetime of the process and forwards traffic to a port or URL of your choosing. This example creates a TCP endpoint on a randomly assigned URL - for example, `tcp://1.tcp.ngrok.io:12345` and forwards its traffic to a local port. ```bash theme={null} ngrok tcp 22 ``` ```bash theme={null} ssh -R 0:localhost:22 v2@connect.ngrok-agent.com tcp ``` See [the guide on using ngrok with SSH](/docs/using-ngrok-with/ssh) to use simple SSH instead of SSH -R. ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Listen(ctx, ngrok.WithURL("tcp://"), ) } ``` * [https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithURL](https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithURL) ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, proto: "tcp", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs * [https://ngrok.github.io/ngrok-javascript/classes/TcpListenerBuilder.html](https://ngrok.github.io/ngrok-javascript/classes/TcpListenerBuilder.html) * [https://ngrok.github.io/ngrok-javascript/classes/Session.html#tcpEndpoint](https://ngrok.github.io/ngrok-javascript/classes/Session.html#tcpEndpoint) * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#proto](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#proto) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, proto="tcp") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/module.html#ngrok.connect](https://ngrok.github.io/ngrok-python/module.html#ngrok.connect) * [https://ngrok.github.io/ngrok-python/tcp\_listener\_builder.html#ngrok.TcpListenerBuilder.listen\_and\_forward](https://ngrok.github.io/ngrok-python/tcp_listener_builder.html#ngrok.TcpListenerBuilder.listen_and_forward) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .tcp_endpoint() .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs * [https://docs.rs/ngrok/latest/ngrok/session/struct.Session.html#method.tcp\_endpoint](https://docs.rs/ngrok/latest/ngrok/session/struct.Session.html#method.tcp_endpoint) * [https://docs.rs/ngrok/latest/ngrok/config/struct.TcpTunnelBuilder.html](https://docs.rs/ngrok/latest/ngrok/config/struct.TcpTunnelBuilder.html) TCP Endpoints are not supported via the ngrok Kubernetes Operator ## URLs URLs are validated differently depending on their [binding](/docs/gateway/endpoints/bindings). Consult the following documentation for details on valid URLs for TCP endpoints: * [Public Endpoint URLs](/docs/gateway/endpoints/public-endpoints/#tcp) * [Internal Endpoint URLs](/docs/gateway/endpoints/internal-endpoints/#urls) * [Kubernetes Endpoint URLs](/docs/gateway/endpoints/kubernetes-endpoints/#urls) There is no standard scheme for TCP URLs so ngrok renders them as `tcp://`. ### Static URLs If you would like a public TCP endpoint to have a static URL, you must first create a [TCP Address](/docs/gateway/domains/tcp-addresses). When you create a TCP address, a random hostname and port will be assigned to you, for example, `1.tcp.ngrok.io:12345`. A TCP address is only needed to make a public TCP endpoint have a static URL. They are not needed for TCP endpoints on other bindings, like `internal` or `kubernetes`. After you have created a TCP Address, specify the address (for example, `1.tcp.eu.ngrok.io:12345`) in the URL when you create the endpoint. ```bash theme={null} ngrok tcp 3389 --url tcp://1.tcp.eu.ngrok.io:12345 ``` ```bash theme={null} ssh -R 1.tcp.eu.ngrok.io:12345:localhost:3389 connect.eu.ngrok-agent.com tcp ``` ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Listen(ctx, ngrok.WithURL("tcp://1.tcp.ngrok.io:12345"), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, proto: "tcp", remote_addr: "1.tcp.eu.ngrok.io:12345", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#remote\_addr](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#remote_addr) * [https://ngrok.github.io/ngrok-javascript/classes/TcpListenerBuilder.html#remoteAddr](https://ngrok.github.io/ngrok-javascript/classes/TcpListenerBuilder.html#remoteAddr) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, proto="tcp", remote_addr="1.tcp.eu.ngrok.io:12345") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/tcp\_listener\_builder.html#ngrok.TcpListenerBuilder.remote\_addr](https://ngrok.github.io/ngrok-python/tcp_listener_builder.html#ngrok.TcpListenerBuilder.remote_addr) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .tcp_endpoint() .remote_addr("1.tcp.eu.ngrok.io:12345") .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.TcpTunnelBuilder.html#method.remote\_addr](https://docs.rs/ngrok/latest/ngrok/config/struct.TcpTunnelBuilder.html#method.remote_addr) TCP Endpoints are not supported via the ngrok Kubernetes Operator ### Custom domains Public TCP endpoints are assigned randomly on an ngrok-controlled hostname with a randomly assigned port. You may not choose the hostname and you may not select the port. You may, however, simulate a customized hostname by creating a CNAME record to the hostname of your assigned TCP address. If you do so, be aware that all ports on that hostname, even those provisioned to other accounts will then be available on your domain. For example if your TCP address is `5.tcp.ngrok.io:12345`, you could create the following CNAME record: ``` CNAME tcp.mydomain.com -> 5.tcp.ngrok.io ``` And then you can access that TCP endpoint with ``` telnet tcp.mydomain.com 12345 ``` ## Traffic Policy Attach [Traffic Policy](/docs/gateway/traffic-policy/) to endpoints to route, authenticate and transform the traffic through the endpoint. ### Authentication When you create public TCP endpoints, you often want to secure them with authentication. You can secure your TCP endpoints with the following [Traffic Policy](/docs/gateway/traffic-policy/) actions. There is a limited set of actions available to authenticate TCP traffic because the TCP protocol is low-level. * [IP Restriction](/docs/gateway/traffic-policy/actions/restrict-ips/) * [Mutual TLS](/docs/gateway/traffic-policy/actions/terminate-tls/) ## Agent forwarding The [ngrok agent](/docs/gateway/agent/) and [Agent SDKs](/docs/agent-sdks/) forward traffic that your endpoints receive to upstream services. You specify a URL or port number to instruct the ngrok agent where and how to forward traffic. ### Forward to non-local service Agents don't just forward to ports on your localhost. You can forward traffic to any address or URL reachable from the agent. For example, if you want to forward traffic to a Postgres server running on your network at `192.168.1.2:5432`: ```bash theme={null} ngrok tcp 192.168.1.2:5432 ``` ```bash theme={null} ssh -R 0:192.168.1.2:5432 v2@connect.ngrok-agent.com tcp ``` ```go theme={null} import ( "context" "golang.ngrok.com/ngrok/v2" ) func ngrokForwarder(ctx context.Context) (ngrok.EndpointForwarder, error) { return ngrok.Forward(ctx, ngrok.WithUpstream("tcp://192.168.1.2:3306"), ngrok.WithURL("tcp://"), ) } ``` * [https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithUpstream](https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithUpstream) ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: "192.168.1.2:5432", authtoken_from_env: true, proto: "tcp", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#addr](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#addr) * [https://ngrok.github.io/ngrok-javascript/classes/TcpListenerBuilder.html#listenAndForward](https://ngrok.github.io/ngrok-javascript/classes/TcpListenerBuilder.html#listenAndForward) ```python theme={null} import ngrok listener = ngrok.forward("192.168.1.2:5432", authtoken_from_env=True, proto="tcp") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/module.html#ngrok.connect](https://ngrok.github.io/ngrok-python/module.html#ngrok.connect) * [https://ngrok.github.io/ngrok-python/tcp\_listener\_builder.html#ngrok.TcpListenerBuilder.listen\_and\_forward](https://ngrok.github.io/ngrok-python/tcp_listener_builder.html#ngrok.TcpListenerBuilder.listen_and_forward) ```rust theme={null} use ngrok::prelude::*; use ngrok::tunnel; use ngrok::forwarder::Forwarder; use url::Url; async fn forward_ngrok() -> Result, Error> { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; sess .tcp_endpoint() .listen_and_forward(Url::parse("tcp://127.0.0.1:8090")?) .await .map_err(Into::into) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/0.14.0-pre.13/ngrok/config/struct.TcpTunnelBuilder.html#method.listen\_and\_forward](https://docs.rs/ngrok/0.14.0-pre.13/ngrok/config/struct.TcpTunnelBuilder.html#method.listen_and_forward) TCP Endpoints are not supported via the ngrok Kubernetes Operator ### PROXY Protocol When you forward traffic to an upstream TCP service, because traffic is coming from the ngrok agent, it won't know the client's original IP address. You can add the [PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) header on connections to your upstream service to send connection information like the original client IP address to your upstream service. You will need to configure your upstream service to handle the PROXY protocol header. ```bash theme={null} ngrok tcp 22 --upstream-proxy-protocol=2 ``` ```yaml theme={null} version: 3 endpoints: - name: example url: tcp://1.tcp.ngrok.io:12345 upstream: url: 22 proxy_protocol: 2 ``` PROXY proto is not support via SSH. ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Forward(ctx, ngrok.WithUpstream("tcp://localhost:8080", ngrok.WithUpstreamProxyProto(ngrok.ProxyProtoV2), ), ngrok.WithURL("tcp://"), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, proto: "tcp", proxy_proto: "2", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#proxy\_proto](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#proxy_proto) * [https://ngrok.github.io/ngrok-javascript/classes/TcpListenerBuilder.html#proxyProto](https://ngrok.github.io/ngrok-javascript/classes/TcpListenerBuilder.html#proxyProto) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, proto="tcp", proxy_proto="2") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/tcp\_listener\_builder.html#ngrok.TcpListenerBuilder.proxy\_proto](https://ngrok.github.io/ngrok-python/tcp_listener_builder.html#ngrok.TcpListenerBuilder.proxy_proto) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .tcp_endpoint() .proxy_proto(ProxyProto::V2) .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.TcpTunnelBuilder.html#method.proxy\_proto](https://docs.rs/ngrok/latest/ngrok/config/struct.TcpTunnelBuilder.html#method.proxy_proto) TCP Endpoints are not supported via the ngrok Kubernetes Operator ## Observability ### Traffic Inspector [Traffic Inspector](/docs/obs/traffic-inspection) does not support TCP endpoints. ### Log exports You can export logs of traffic to TCP endpoints with [ngrok's events system](/docs/obs/events/). The following events are published for log exporting: | Log | When | | - | - | | [tcp\_connection\_closed.v0](/docs/obs/events/reference/#tcp-connection-closed) | Published when a TCP connection to a TCP endpoint completes. | ## Limits & timeouts [Contact Support](mailto:support@ngrok.com) if you need to configure limits and timeouts on connections to TCP endpoints. | Limit | Name | Notes | | - | - | - | | 5 minutes | Client Idle Timeout | Time since data was last transmitted by the upstream service | | 5 minutes | Server Idle Timeout | Time since data was last transmitted by the upstream service | | No limit | Data transmitted | Data transmitted by the client or upstream service | ## Errors If an error is encountered while handling connections to a TCP endpoint for any reason (for example, Traffic Policy action error, internal server error), the connection will be closed. Because of the low-level nature of the TCP protocol, there is no mechanism used to transmit information about what error code was encountered. Use the [observability](#observability) features to understand connection handling errors. ## API TCP Endpoints can be created programmatically. Consult the documentation on [Endpoint APIs](/docs/api-reference/endpoints/list). ## TCP endpoint pricing TCP endpoints are available on all plans. Consult the [Endpoints Pricing](/docs/gateway/endpoints/#pricing) documentation for billing details. TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. See [TCP Addresses pricing](/docs/gateway/domains/tcp-addresses/#pricing) for details on pricing for fixed TCP Addresses. # TLS Agent Endpoints Source: https://ngrok.com/docs/gateway/endpoints/tls Learn how to create and configure TLS endpoints with ngrok for handling encrypted TLS traffic using SNI-based routing. TLS endpoints enable you to deliver any network service that runs over a TLS-based protocol. TLS endpoints make no assumptions about the wrapped protocol being transported. TLS endpoints inspect the [Server Name Indication (SNI)](#sni) data on incoming TLS connections to route connections to the appropriate endpoint. Because the TLS protocol describes no application-level semantics, ngrok can only offer a [limited set of Traffic Policy actions](/docs/gateway/traffic-policy/actions/) to handle TLS traffic. If you are delivering an HTTPS application, prefer to create an [HTTP Endpoint](/docs/gateway/endpoints/http/). ## Quickstart ```bash theme={null} ngrok tls 80 \ --url tls://your-name.ngrok.app \ --traffic-policy-file traffic-policy.yml ``` ##### `traffic-policy.yml` ```yaml theme={null} on_tcp_connect: - actions: - type: terminate-tls ``` ```yaml theme={null} version: 3 endpoints: - name: example url: tls://your-name.ngrok.app upstream: url: 80 traffic_policy: on_tcp_connect: - actions: - type: terminate-tls ``` SSH does not support termination at the edge ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) trafficPolicy := ` on_tls_request: - actions: - type: terminate-tls ` func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Listen(ctx, ngrok.WithURL("tls://your-name.ngrok.app"), ngrok.WithTrafficPolicy(trafficPolicy), ) } ``` * [https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithTrafficPolicy](https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithTrafficPolicy) ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); const fs = require("fs"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, proto: "tls", domain: "app.example.com", crt: fs.readFileSync("/path/to/app-example-com-crt.pem", "utf8"), key: fs.readFileSync("/path/to/app-example-com-key.pem", "utf8"), }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain) * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#crt](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#crt) * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#key](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#key) * [https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#domain](https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#domain) * [https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#termination](https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#termination) ```python theme={null} import ngrok def load_file(name): with open(name, "r") as crt: return bytearray(crt.read().encode()) listener = ngrok.forward("localhost:8080", authtoken_from_env=True, proto="tls", domain="app.example.com", crt=load_file("/path/to/app-example-com-crt.pem"), key=load_file("/path/to/app-example-com-key.pem")) print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/tls\_listener\_builder.html#ngrok.TlsListenerBuilder.domain](https://ngrok.github.io/ngrok-python/tls_listener_builder.html#ngrok.TlsListenerBuilder.domain) * [https://ngrok.github.io/ngrok-python/tls\_listener\_builder.html#ngrok.TlsListenerBuilder.termination](https://ngrok.github.io/ngrok-python/tls_listener_builder.html#ngrok.TlsListenerBuilder.termination) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) The Rust SDK does not support TLS termination at the ngrok edge TLS endpoints are not supported by the ngrok Kubernetes Operator ## URLs URLs are validated differently depending on their [binding](/docs/gateway/endpoints/bindings). Consult the following documentation for details on valid URLs for TLS endpoints: * [Public Endpoint URLs](/docs/gateway/endpoints/public-endpoints/#https-tls) * [Internal Endpoint URLs](/docs/gateway/endpoints/internal-endpoints/#urls) * [Kubernetes Endpoint URLs](/docs/gateway/endpoints/kubernetes-endpoints/#urls) There is no standard scheme for TLS URLs so ngrok renders them as `tls://`. ## Bindings TLS endpoints support `public` and `internal` bindings. `kubernetes` binding is not supported. ## Traffic Policy Attach [Traffic Policy](/docs/gateway/traffic-policy/) to endpoints to route, authenticate and transform the traffic through your TLS endpoints. ### Authentication When you create public TLS endpoints, you often want to secure them with authentication. You can secure your TLS endpoints with the following [Traffic Policy](/docs/gateway/traffic-policy/) actions. There is a limited set of actions available to authenticate TLS traffic because the TLS protocol is low-level. * [IP Restriction](/docs/gateway/traffic-policy/actions/restrict-ips/) * [Mutual TLS](/docs/gateway/traffic-policy/actions/terminate-tls/) ## TLS ### Termination TLS Endpoints provide you with the flexibility to define where TLS termination occurs. You may configure your endpoint to terminate TLS at the ngrok cloud service or you can achieve [end-to-end encryption](/docs/gateway/domains/tls-termination/#end-to-end-encryption) by terminating at the agent or your upstream service. When you use end-to-end encryption, the ngrok cloud service can not see payload that transit through your endpoints. Consult the documentation on [TLS Termination Locations](/docs/gateway/domains/tls-termination/#termination-location) for additional details. #### Cloud service ```bash theme={null} ngrok tls 80 \ --url tls://your-name.ngrok.app \ --traffic-policy-file traffic-policy.yml ``` ##### `traffic-policy.yml` ```yaml theme={null} on_tcp_connect: - actions: - type: terminate-tls ``` ```yaml theme={null} version: 3 endpoints: - name: example url: tls://your-name.ngrok.app upstream: url: 80 traffic_policy: on_tcp_connect: - actions: - type: terminate-tls ``` SSH does not support termination at the edge ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) trafficPolicy := ` on_tls_request: - actions: - type: terminate-tls ` func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Listen(ctx, ngrok.WithURL("tls://your-name.ngrok.app"), ngrok.WithTrafficPolicy(trafficPolicy), ) } ``` * [https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithTrafficPolicy](https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithTrafficPolicy) ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); const fs = require("fs"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, proto: "tls", domain: "app.example.com", crt: fs.readFileSync("/path/to/app-example-com-crt.pem", "utf8"), key: fs.readFileSync("/path/to/app-example-com-key.pem", "utf8"), }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain) * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#crt](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#crt) * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#key](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#key) * [https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#domain](https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#domain) * [https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#termination](https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#termination) ```python theme={null} import ngrok def load_file(name): with open(name, "r") as crt: return bytearray(crt.read().encode()) listener = ngrok.forward("localhost:8080", authtoken_from_env=True, proto="tls", domain="app.example.com", crt=load_file("/path/to/app-example-com-crt.pem"), key=load_file("/path/to/app-example-com-key.pem")) print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/tls\_listener\_builder.html#ngrok.TlsListenerBuilder.domain](https://ngrok.github.io/ngrok-python/tls_listener_builder.html#ngrok.TlsListenerBuilder.domain) * [https://ngrok.github.io/ngrok-python/tls\_listener\_builder.html#ngrok.TlsListenerBuilder.termination](https://ngrok.github.io/ngrok-python/tls_listener_builder.html#ngrok.TlsListenerBuilder.termination) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) The Rust SDK does not support TLS termination at the ngrok edge TLS endpoints are not supported by the ngrok Kubernetes Operator
#### Terminate at Agent See [TLS Termination at the Agent for End-to-End Encryption](/docs/gateway/domains/tls-termination#end-to-end-encryption) for additional details. ```bash theme={null} ngrok tls 80 \ --terminate-at agent \ --url tls://app.example.com \ --crt /path/to/app-example-com-crt.pem \ --key /path/to/app-example-com-key.pem ``` ```yaml theme={null} version: 3 endpoints: - name: example url: tls://app.example.com" upstream: url: 80 agent_tls_termination: server_certificate: "/path/to/app-example-com-crt.pem" server_private_key: "/path/to/app-example-com-crt.key" ``` SSH does not support termination at the agent ```go theme={null} import ( "context" "crypto/tls" "net" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { cert, err := tls.LoadX509KeyPair("/path/to/cert.pem", "/path/to/key.pem") if err != nil { return nil, err } tlsConfig := &tls.Config{ Certificates: []tls.Certificate{cert}, } return ngrok.Listen(ctx, ngrok.WithURL("tls://"), ngrok.WithAgentTLSTermination(tlsConfig), ) } ``` The Javascript SDK does not support TLS termination at the agent. The Python SDK does not support TLS termination at the agent. The Rust SDK does not support TLS termination at the agent. TLS endpoints are not supported by the ngrok Kubernetes Operator
#### Terminate at Upstream ```bash theme={null} ngrok tls 443 \ --terminate-at upstream \ --url tls://app.example.com ``` ```yaml theme={null} version: 3 endpoints: - name: example url: tls://app.example.com upstream: url: localhost:443 ``` ```bash theme={null} ssh -R app.example.com:443:localhost:443 v2@connect.ngrok-agent.com tls ``` ```go theme={null} import ( "context" "golang.ngrok.com/ngrok/v2" ) func ngrokForwarder(ctx context.Context) (ngrok.EndpointForwarder, error) { return ngrok.Forward(ctx, ngrok.WithUpstream("tls://localhost:443"), ngrok.WithURL("tls://app.example.com"), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, proto: "tls", domain: "app.example.com", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#domain) * [https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#domain](https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#domain) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, proto="tls", domain="app.example.com") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/tls\_listener\_builder.html#ngrok.TlsListenerBuilder.domain](https://ngrok.github.io/ngrok-python/tls_listener_builder.html#ngrok.TlsListenerBuilder.domain) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .tls_endpoint() .domain("app.example.com") .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.TlsTunnelBuilder.html#method.domain](https://docs.rs/ngrok/latest/ngrok/config/struct.TlsTunnelBuilder.html#method.domain) TLS endpoints are not supported by the ngrok Kubernetes Operator ### Certificates It is very common to encounter certificate errors when working with TLS endpoints. When terminating TLS at ngrok's cloud service, ngrok will automatically select, provision, and manage certs for you. When performing [end-to-end encryption](/docs/gateway/domains/tls-termination/#end-to-end-encryption) by terminating at the agent or upstream service, you become responsible for provisioning, managing, and distributing certificates. Consult the documentation on [TLS Certificates](/docs/gateway/domains/tls-certificates/) for details about certificate selection, provisioning and management. ## Agent forwarding ### Re-encrypt to upstream If you terminate TLS at the ngrok cloud service or ngrok agent, you may want to re-encrypt the connection from the agent to your upstream service. The ngrok agent supports this behavior by using the non-standard `tls://` scheme syntax. ```bash theme={null} ngrok tls tls://localhost:443 --terminate-at=edge ``` ```yaml theme={null} version: 3 endpoints: - name: example url: tls://example.ngrok.app upstream: url: tls://localhost:443 traffic_policy: on_tcp_connect: - actions: - type: terminate-tls ``` Re-encrypting the connection to your upstream service with TLS is not supported via SSH. ```go theme={null} import ( "context" "crypto/tls" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (ngrok.EndpointForwarder, error) { // TLS config is optional - can be nil for default settings tlsConfig := &tls.Config{ // Add your upstream TLS configuration here } return ngrok.Forward(ctx, ngrok.WithUpstream("tls://localhost:8443", ngrok.WithUpstreamTLSClientConfig(tlsConfig), ), ) } ``` ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: "tls://localhost:443", authtoken_from_env: true, proto: "tls", crt: "", key: "", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` An empty certificate and key will default to the ngrok edge's automatically provisioned keypair. The upstream certificate of `localhost:443` will be validated by a filepath specified in the `SSL_CERT_FILE` environment variable (for example, `SSL_CERT_FILE=/path/to/ca.crt`), or falling back to the host OS installed trusted certificate authorities. Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html](https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html) * [https://ngrok.github.io/ngrok-javascript/classes/Session.html#tlsEndpoint](https://ngrok.github.io/ngrok-javascript/classes/Session.html#tlsEndpoint) * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#addr](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#addr) ```python theme={null} import ngrok listener = ngrok.forward("tls://localhost:443", authtoken_from_env=True, proto="tls" crt=bytearray(), key=bytearray()) print(f"Ingress established at: {listener.url()}"); ``` An empty certificate and key will default to the ngrok edge's automatically provisioned keypair. The upstream certificate of `localhost:443` will be validated by a filepath specified in the `SSL_CERT_FILE` environment variable (for example, `SSL_CERT_FILE=/path/to/ca.crt`), or falling back to the host OS installed trusted certificate authorities. Python SDK Docs: * [https://ngrok.github.io/ngrok-python/module.html#ngrok.connect](https://ngrok.github.io/ngrok-python/module.html#ngrok.connect) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) Re-encrypting the connection to your upstream service with TLS is not yet supported in the Rust SDK. TLS endpoints are not supported by the ngrok Kubernetes Operator ### PROXY Protocol Add a [PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) header on connection to your upstream service. This sends connection information like the original client IP address to your upstream service. ```bash theme={null} ngrok tls 443 --upstream-proxy-protocol=2 ``` ```yaml theme={null} version: 3 endpoints: - name: example url: tls://example.ngrok.app upstream: url: 443 proxy_protocol: 2 ``` PROXY proto is not support via SSH. ```go theme={null} import ( "context" "net" "golang.ngrok.com/ngrok/v2" ) func ngrokListener(ctx context.Context) (net.Listener, error) { return ngrok.Forward(ctx, ngrok.WithUpstream("tls://localhost:8443", ngrok.WithUpstreamProxyProto(ngrok.ProxyProtoV2), ), ngrok.WithURL("tls://"), ) } ``` Go Package Docs: * [https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithUpstreamProxyProto](https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithUpstreamProxyProto) ```jsx theme={null} const ngrok = require("@ngrok/ngrok"); (async function () { const listener = await ngrok.forward({ addr: 8080, authtoken_from_env: true, proto: "tls", proxy_proto: "2", }); console.log(`Ingress established at: ${listener.url()}`); })(); ``` Javascript SDK Docs: * [https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#proxy\_proto](https://ngrok.github.io/ngrok-javascript/interfaces/Config.html#proxy_proto) * [https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#proxyProto](https://ngrok.github.io/ngrok-javascript/classes/TlsListenerBuilder.html#proxyProto) ```python theme={null} import ngrok listener = ngrok.forward("localhost:8080", authtoken_from_env=True, proto="tls", proxy_proto="2") print(f"Ingress established at: {listener.url()}"); ``` Python SDK Docs: * [https://ngrok.github.io/ngrok-python/tls\_listener\_builder.html#ngrok.TlsListenerBuilder.proxy\_proto](https://ngrok.github.io/ngrok-python/tls_listener_builder.html#ngrok.TlsListenerBuilder.proxy_proto) * [https://ngrok.github.io/ngrok-python/index.html#full-configuration](https://ngrok.github.io/ngrok-python/index.html#full-configuration) ```rust theme={null} use ngrok::prelude::*; async fn listen_ngrok() -> anyhow::Result { let sess = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let tun = sess .tls_endpoint() .proxy_proto(ProxyProto::V2) .listen() .await?; println!("Listening on URL: {:?}", tun.url()); Ok(tun) } ``` Rust Crate Docs: * [https://docs.rs/ngrok/latest/ngrok/config/struct.TlsTunnelBuilder.html#method.proxy\_proto](https://docs.rs/ngrok/latest/ngrok/config/struct.TlsTunnelBuilder.html#method.proxy_proto) TLS Endpoints are not supported via the ngrok Kubernetes Operator ## Compatible clients ### SNI TLS endpoints only work with modern TLS clients that populate the SNI extension. See the documentation on [TLS Termination](/docs/gateway/domains/tls-termination/#sni) for additional details on compatible clients. ### STARTTLS Protocols that begin in plain text and upgrade to TLS via a mechanism like STARTTLS in SMTP, IMAP, etc are not supported. If you need to support connections which upgrade to TLS, use a [TCP Endpoint](/docs/gateway/endpoints/tcp/). ## Observability ### Traffic Inspector [Traffic Inspector](/docs/obs/traffic-inspection) does not support TLS endpoints. ### Log exports You can export logs of traffic to TLS endpoints with [ngrok's events system](/docs/obs/). The following events are published for log exporting: | Log | When | | - | - | | [tcp\_connection\_closed.v0](/docs/obs/events/reference/#tcp-connection-closed) | Published when a TCP connection to a TCP endpoint completes. | ## Limits & timeouts [Contact Support](mailto:support@ngrok.com) if you need to configure limits and timeouts on connections to TLS endpoints. ### Connections | Limit | Name | Notes | | - | - | - | | 3 seconds | ClientHello Timeout | Time between connection establishment and ClientHello received | | 5 minutes | Client Idle Timeout | Time since data was last transmitted by the upstream service | | 5 minutes | Server Idle Timeout | Time since data was last transmitted by the upstream service | | No limit | Data transmitted | Data transmitted by the client or upstream service | ### TLS | Limit | Name | Notes | | - | - | - | | 60 seconds | TLS Handshake Duration | Time between ClientHello received and handshake completion | | 64 KB | Handshake Message Size | Max size of non-certificate handshake messages | | 256 KB | Certificate Message Size | Max size of certificate handshake messages | | 16 KB | Record Payload Size | | ## Errors If a TLS handshake fails, an appropriate TLS abort code will be sent to the client. In all other cases, if an error is encountered while handling TLS connections to your endpoints (for example, no available backends or internal server error), the connection will be closed. The TLS protocol and its implementations are not sufficiently flexible enough to deliver additional rich error information when failures are encountered. Use the [observability](#observability) features to understand connection handling errors. ## API TLS Endpoints can be created programmatically. Consult the documentation on \[Endpoint APIs]\(/api-reference/endpoints/list. ## TLS pricing TLS endpoints are available on Hobbyist and Pay-as-you-go plans. Consult the [Endpoints Pricing](/docs/gateway/endpoints/#pricing) documentation for billing details. See [Domains pricing](/docs/gateway/domains/#pricing) for details on pricing for custom domains, wildcard endpoints and more. Zero-knowledge TLS is available on the Pay-as-you-go plan. # Gateway Examples Source: https://ngrok.com/docs/gateway/examples Browse examples and patterns for using ngrok's Gateway including API gateway patterns, multicloud failover, and integrations. Whether you need an API gateway, multicloud failover, or want to integrate ngrok with your stack, these examples help you compose endpoints and Traffic Policy to orchestrate traffic for common jobs to be done and problems to be solve. Wire it up, secure it, and *ship it already*. ### Fundamentals The front door pattern is a great way to improve the user experience of your services. Multiplex is a great way to improve the user experience of your services. Custom error pages are a great way to improve the user experience of your services. ### Gateway shapes Get the polish of managed platforms like Heroku or Vercel while maintaining full control over your infrastructure with load balancing, security, and observability. Transform your local development environment into a production-ready testbed with real authentication, traffic transformation, and public accessibility for AI-assisted development. Coordinate ingress traffic for microservices without a full service mesh, using ngrok to handle north-south traffic patterns with authentication and routing. Centralize webhook validation and routing from third-party providers like Stripe, Twilio, and Slack to secure your integrations and eliminate repetitive implementation. Securely expose databases to external clients with strict authentication, rate limiting, and query transformation to prevent runaway costs and data leaks. Expose deploy previews and CI test environments on secure, short-lived URLs with dynamic routing and authentication. ### Other Blue-green deployments make your release processes safer and less prone to errors. Canary deployments are a trusted method for rolling out new features or major versions of your services. IP restrictions and basic auth are a great way to improve the security of your services. Lock admin dashboards are a great way to improve the security of your services. Maintenance mode is a great way to improve the user experience of your services. Minecraft is a great way to improve the user experience of your services. N8N is a great way to improve the user experience of your services. Offload analytics are a great way to improve the user experience of your services. Ollama is a great way to improve the user experience of your services. Pre-tier requests are a great way to improve the user experience of your services. Rewrite headers and redirects are a great way to improve the user experience of your services. Route API app traffic by user agent is a great way to improve the user experience of your services. Route by geography is a great way to improve the user experience of your services. Route by OIDC is a great way to improve the user experience of your services. Secure developer environments is a great way to improve the user experience of your services. Validate requests identity is a great way to improve the user experience of your services. Wordpress is a great way to improve the user experience of your services. # Agent-Assisted Gateway Source: https://ngrok.com/docs/gateway/examples/agent-assisted-gateway Transform your local development environment into a production-ready testbed with real authentication, traffic transformation, and public accessibility for AI-assisted development. An agent-assisted gateway bridges your localhost development environment with the public internet, enabling AI coding tools, webhook testing, and real authentication flows without waiting for production deployment. Instead of port forwarding and localhost limitations, you get a production-like environment for testing traffic patterns, auth flows, and integrations. With this setup, you can: * Expose local services securely to AI coding assistants and external tools * Test real OAuth flows, webhooks, and API integrations during development * Apply traffic transformations and header manipulation in your dev environment * Debug production-like traffic patterns before deploying * Share work-in-progress with teammates and stakeholders securely ## 1. Create internal endpoints for your local services Start [Agent Endpoints](/docs/gateway/endpoints/agent-endpoints/) for your local development services using internal URLs. Replace `$PORT` with your actual service ports. ```bash theme={null} # Backend API (for example, Express, Flask, FastAPI) ngrok http $BACKEND_PORT --url https://api-dev.internal # Frontend development server (for example, React, Vue, Next.js) ngrok http $FRONTEND_PORT --url https://app-dev.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. Consider using a subdomain like `dev.yourcompany.com` or `staging.yourproject.com` to clearly distinguish from production. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Apply Traffic Policy for development gateway While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. Make sure you change each of the following values: * `$GITHUB_OAUTH_CLIENT_ID`: Replace with your GitHub OAuth app client ID for development * `$GIBHUB_OAUTH_CLIENT_SECRET`: Replace with your GitHub OAuth app secret for development * `$YOUR_DEV_API_KEY`: Replace with an API key for development access * Internal service URLs: Replace with your actual internal endpoint URLs ```yaml theme={null} on_http_request: # Development environment identification - actions: - type: add-headers config: headers: x-environment: "development" x-client-ip: "${conn.client_ip}" x-client-location: "${conn.geo.city}, ${conn.geo.country}" x-forwarded-host: "${req.host}" # OAuth authentication for protected development routes - expressions: - "req.url.path.startsWith('/admin') || req.url.path.startsWith('/dashboard')" actions: - type: oauth config: provider: google client_id: "$GITHUB_OAUTH_CLIENT_ID" client_secret: "$GITHUB_OAUTH_CLIENT_SECRET" scopes: - https://www.googleapis.com/auth/userinfo.email # API key authentication for external tools and AI assistants - expressions: - "req.url.path.startsWith('/api')" - "!hasReqHeader('authorization')" actions: - type: basic-auth config: credentials: - "dev-user:$YOUR_DEV_API_KEY" # Allow webhook testing without auth (for development only) - expressions: - "req.url.path.startsWith('/webhooks')" actions: - type: add-headers config: headers: x-webhook-source: "${req.user_agent}" x-webhook-timestamp: "${timestamp(time.now)}" x-webhook-signature: "${getReqHeader('x-signature')[0]}" # Route API requests to backend - expressions: - "req.url.path.startsWith('/api')" actions: - type: forward-internal config: url: https://api-dev.internal # Route webhook requests to backend - expressions: - "req.url.path.startsWith('/webhooks')" actions: - type: forward-internal config: url: https://api-dev.internal # Route admin/dashboard to backend with auth headers - expressions: - "req.url.path.startsWith('/admin') || req.url.path.startsWith('/dashboard')" actions: - type: add-headers config: headers: x-authenticated-user: "${actions.ngrok.oauth.identity.email}" x-auth-provider: "github" - type: forward-internal config: url: https://app-dev.internal # Default route everything else to frontend - actions: - type: forward-internal config: url: https://app-dev.internal on_http_response: # Add development debugging headers - actions: - type: add-headers config: headers: x-served-by: "ngrok-dev-gateway" x-processing-timestamp: "${time.now}" # Enable CORS for development (be more restrictive in production) - actions: - type: add-headers config: headers: access-control-allow-origin: "*" access-control-allow-methods: "GET, POST, PUT, DELETE, OPTIONS, PATCH" access-control-allow-headers: "Content-Type, Authorization, X-API-Key" access-control-expose-headers: "X-Processing-Timestamp" ``` **What's happening here?** On every HTTP request, the policy adds environment identification headers for tracing, enforces OAuth authentication for admin/dashboard routes, requires API key authentication for API routes accessed by external tools and AI assistants, and allows webhook testing without authentication for easy debugging. The gateway routes traffic based on URL paths—API requests go to your backend service, admin routes go to the backend with authentication headers, and everything else goes to your frontend. On responses, it adds development-specific headers for debugging, enables permissive CORS for local development, and timestamps responses for request tracking. ## 5. Try out your development gateway Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. Test different aspects of your development setup: ```bash theme={null} # Test your frontend application curl "https://$NGROK_DOMAIN/" # Test API with authentication (basic auth with base64 encoded API key) # First, base64 encode the dev-user:$YOUR_DEV_API_KEY pair # Example: dev-user:my-api-key becomes ZGV2LXVzZXI6bXktYXBpLWtleQ== curl -H "Authorization: Basic ZGV2LXVzZXI6bXktYXBpLWtleQ==" \ "https://$NGROK_DOMAIN/api/users" # Test OAuth protected admin route (will redirect to GitHub) curl "https://$NGROK_DOMAIN/admin/dashboard" ``` When using tools like Lovable, Cursor, or other AI coding assistants: 1. They can access your API schema: `https://$NGROK_DOMAIN/api/schema` 2. Test frontend builds against your real backend: `https://$NGROK_DOMAIN/api/*` 3. Receive webhooks for real-time collaboration: `https://$NGROK_DOMAIN/webhooks/ai-assist` ## What's next? * Read about [other gateway shapes](https://ngrok.com/blog-post/api-gateway-shapes-patterns-2025) you can build with ngrok * Learn about [securing developer environments](/docs/gateway/examples/secure-developer-environments) for team-wide development workflows * Explore [webhook integrations](/docs/integrations) for testing with real services during development * Set up [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to debug request flows and authentication issues * Configure [custom domains](/docs/gateway/domains/custom-domains) for more professional development URLs # Switch Traffic Between Blue-Green Deployments Source: https://ngrok.com/docs/gateway/examples/blue-green-deployments Test two production environments, either for monolithic or microservices architectures, side-by-side with a testing header and instant rollouts/rollbacks. Blue-green deployments make your release processes safer and less prone to error by using an API gateway to switch production traffic between a live environment (`blue`) and a new version (`green`). You can route specific traffic to the green environment for final tests, and once you make the switch to green, you can roll back to blue if you find issues. ngrok makes blue-green deployments easier by helping you: * Specify in your Traffic Policy which environments are currently live and testing, and switch between them freely for each new blue-green deployment. * Allow requests with specific headers to access your testing environment. * Handle both **whole-environment** and **scoped (service-specific)**. In this example, the `blue` deployment is your live deployment, while `green` is where you're testing new features. You can also add an `x-deployment-version` header to requests to route requests to the testing deployment manually. ## 1. Create endpoints for your services Start internal [Agent Endpoints](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your service listens, for each of your services on the systems where they run. You can also use one of the [SDKs](/docs/agent-sdks/#example-usage) or the [Kubernetes Operator](/docs/getting-started/kubernetes/ingress/). ```bash theme={null} ngrok http $PORT --url https://service-blue.internal ngrok http $PORT --url https://service-green.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Add routing to your blue environment with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor, depending on how you architected your services and deploy them. ```yaml theme={null} on_http_request: - actions: - type: set-vars config: vars: live: blue test: green - expressions: - "req.headers.exists_one(x, x == 'x-deployment-version')" - "req.headers['x-deployment-version'].join(',') == ${vars.test}" actions: - type: forward-internal config: url: https://service-${vars.test}.internal - actions: - type: forward-internal config: url: https://service-${vars.live}.internal ``` **What's happening here?** This policy first assigns `blue`/`green` values to the appropriate environment. It then plumbs those values into the rest of the policy so that you don't have to manually change your headers or internal URLs each time you do a blue-green deployment. The policy then checks whether the `x-deployment-version` header exists and its value equals the current `test` variable. If both are `true`, it routes the request to your test environment. The policy then sends all other requests to your current live environment. In this example, you have `auth` and `accounts` microservices at the `/api/auth` and `/api/accounts` paths, respectively. You're blue-green deploying just the auth service at two internal endpoints: `https://auth-blue.internal` and `https://auth-green.internal`. ```yaml theme={null} on_http_request: - actions: - type: set-vars config: vars: live: blue test: green - expressions: - "req.path.startsWith('/api/auth')" - "req.headers.exists_one(x, x == 'x-deployment-version')" - "req.headers['x-deployment-version'].join(',') == ${vars.test}" actions: - type: forward-internal config: url: https://auth-${vars.test}.internal - expressions: - "req.path.startsWith('/api/auth')" actions: - type: forward-internal config: url: https://auth-${vars.live}.internal - expressions: - "req.path.startsWith('/api/accounts')" actions: - type: forward-internal config: url: https://accounts.internal ``` **What's happening here?** This policy first assigns `blue`/`green` values to the appropriate environment. It then plumbs those values into the rest of the policy so that you don't have to manually change your headers or internal URLs each time you do a blue-green deployment. The policy then checks whether the request is to the `/api/auth` path, contains the `x-deployment-version` header, and the header's value equals the current `test` variable. If all are `true`, it routes the request to the test deployment of *just* your auth service. Next, the policy checks whether the request is to the `/api/auth` path, but without the header, and routes it to the live version. Finally, it routes requests to the `/api/accounts` path to that service independent of your ongoing blue-green rollout. ## 5. Switch services or environments over Edit your policy again to switch your environments and their states, where `green` becomes live and `blue` is your new testing environment. ```yaml theme={null} on_http_request: - actions: - type: set-vars config: vars: live: green test: blue ``` If you need to roll back, change the variables back to their original state. ## What's next? * View your [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to see how your blue and green environments are behaving, particularly after making the switch, to know exactly when and why to roll back. * Learn how to do [canary deployments](/docs/gateway/examples/canary-deployments) with custom traffic splitting as a lower-cost, but more complex, alternative to blue-green. * Explore other common gateway setups, like [multiplexing to many services](/docs/gateway/examples/multiplex) or shipping a [custom "maintenance mode"](/docs/gateway/examples/maintenance-mode/) during an outage or planned downtime a blue-green deployment can't help with. * Start automating your deployment strategies with the ngrok [API](/docs/api/) or the [Kubernetes Operator](/docs/gateway/k8s/). # Distribute Traffic Between Canary Deployments Source: https://ngrok.com/docs/gateway/examples/canary-deployments Use ngrok to validate how features or versions of your services behave by distributing production traffic randomly or via a specific header. Canary deployments are a trusted method for rolling out new features or major versions of your services. By controlling who accesses the new version, either through the random distribution of traffic or a specific header attached to requests from certain customers, you can see exactly if and where something breaks before releasing it to all your users. With ngrok, you can handle ingress to canary deployments by: * Choosing between random distribution across all your customers or applying a specific header to some customers' requests to opt them in to a new version, that is, a feature flag. * Applying different policies to each version depending on how they behave. * Testing a migration from one environment to another, like on-premises VMs to a cloud-based Kubernetes cluster. ## 1. Create endpoints for your services Start internal [Agent Endpoints](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your service listens, for each of your services on the systems where they run. You can also use one of the [SDKs](/docs/agent-sdks/#example-usage) or the [Kubernetes Operator](/docs/getting-started/kubernetes/ingress/). ```bash theme={null} ngrok http $PORT --url https://service.internal ngrok http $PORT --url https://service-canary.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Add canary routing with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy one of the two policies below and paste it into the Traffic Policy editor. The choice depends on whether you want to distribute traffic randomly or by a header like `x-canary`. ```yaml theme={null} on_http_request: - expressions: - "rand.double() <= 0.2" actions: - type: forward-internal config: url: https://service-canary.internal - actions: - type: forward-internal config: url: https://service.internal ``` **What's happening here?** This policy first returns a random `double` between `0` and `1`. If that double is less than or equal to `0.2`, it routes the request to your canary deployment, effectively routing 20% of incoming traffic to the canary. The policy then routes the remaining 80% of traffic to your stable version. ```yaml theme={null} on_http_request: - expressions: - "req.headers.exists_one(x, x == 'x-canary')" - "req.headers['x-canary'].join(',') == 'true'" actions: - type: forward-internal config: url: https://service-canary.internal - actions: - type: forward-internal config: url: https://service.internal ``` **What's happening here?** This policy checks the incoming request for the presence of the `x-canary` and validates that its value is `true`. If both are true, then the policy routes the request to your canary deployment. In the absence of an `x-canary: true` header, the policy route traffic to your stable version. ## 5. Try out your endpoints Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. If you chose random distribution, you should see each service responding at roughly the percentage you defined. If you choose to distribute by header, add `x-canary: true` to your requests to verify ngrok is routing traffic to your canary. ## 6. Tweak the distribution to your canary If you opted for random distribution, you should increase the volume of traffic forwarded to your canary at regular intervals as you gain confidence in its stability. For example, to split traffic evenly between your two versions: ```yaml theme={null} on_http_request: - expressions: - "rand.double() <= 0.5" actions: - type: forward-internal config: url: https://service-canary.internal - actions: - type: forward-internal config: url: https://service.internal ``` ## 7. Cut over to the new version and clean up When you promote your canary deployment to stable, you can edit your policy to route traffic to the new version and close down the ngrok agent forwarding to `https://service.internal`. ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://service-canary.internal ``` If you want to maintain `https://service.internal` as the canonical name for the stable production deployment, you can: 1. Start another agent forwarding to your new version at this canonical internal URL. 2. Edit your policy to route to it. 3. Shut down the agent forwarding to `https://service-canary.internal`. ## What's next? * View your [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to see how your canary behaves with production traffic, which may signal that you pause or roll back the deployment entirely. * Learn how to implement [blue-green deployments](/docs/gateway/examples/blue-green-deployments) if you prefer faster rollouts at the expense of maintaining two production environments. * Explore other common gateway setups, like [multiplexing to many services](/docs/gateway/examples/multiplex) or shipping a [custom "maintenance mode"](/docs/gateway/examples/maintenance-mode/) during an outage or planned downtime. * Start automating your deployment strategies with the ngrok [API](/docs/api/) or the [Kubernetes Operator](/docs/gateway/k8s/). # Deploy Custom Error Pages Source: https://ngrok.com/docs/gateway/examples/custom-error-pages Instead of a blank page or HTTP error code, ensure your services automatically failover to error pages with clear details and instructions. When your upstream nodes or services go offline, you don't want your users to see a blank page, a standalone HTTP error code, or an ngrok-branded error page. A custom error page gives you the chance to communicate clearly and reinforce your brand even during downtime. With ngrok’s Traffic Policy engine, you can define exactly what your users see when your upstream service fails. This example walks you through how to route requests to your internal service, and then fall back to a branded error page if that service is unavailable. ## 1. Create an endpoint for your service Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your service listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url https://service.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Add routing to your service and error handling with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://service.internal on_error: continue - type: custom-response config: status_code: 503 headers: content-type: text/html body: | Error at ${req.host}!

Sorry, something went wrong!

We're having trouble reaching ${req.host}.

  • Time: ${timestamp(time.now)}
  • Path: ${req.url.path}
  • Method: ${req.method}
  • Region: ${conn.server_region}
``` **What's happening here?** This policy forwards all HTTP requests to the internal Agent Endpoint you created at `https://service.internal`. If forwarding to `https://service.internal` fails, the `on_error: continue` configuration on your `forward-internal` action means that the policy continues to the next rule, which sends a custom `503` error response with the HTML specified in the `body`. ## 5. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. If you take down either your upstream service or the internal Agent Endpoint, you'll see your custom error message. ## What's next? * See other examples of using the [`custom-response`](/docs/gateway/traffic-policy/actions/custom-response/#examples) to create error pages or messages, such as a JSON-based response for an API service. * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to see who and when users hit your custom error page. * Add the [`log` action](/docs/gateway/traffic-policy/actions/log) before the `custom-response` to send error events over to your observability platform for enriched debugging. # Database Gateway Source: https://ngrok.com/docs/gateway/examples/database-gateway Securely expose databases to external clients with strict authentication, rate limiting, and query transformation to prevent runaway costs and data leaks. A database gateway provides secure access to databases exposed to external clients. It enforces authentication, rate limiting, and logging before requests reach your database, while optionally transforming queries to prevent costly operations or data exposure. With this setup, you can: * Enforce strict mTLS certificate authentication before any database access * Rate limit requests per client to prevent abuse and runaway costs * Block dangerous queries to protect sensitive data * Support secure database replication across clouds without exposing credentials ## 1. Create an endpoint for your database service Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/) for your database proxy or HTTP database interface, replacing `$PORT` based on where it listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $DATABASE_PROXY_PORT --url https://database-service.internal ``` For databases that don't natively support HTTP, consider using a database proxy like [PostgREST](https://postgrest.org/) for PostgreSQL, [Hasura](https://hasura.io/) for GraphQL, or a custom HTTP wrapper that translates requests to your database's native protocol. ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. (Optional) Create a vault and secrets For production environments, store your CA certificate securely using [Traffic Policy Secrets](/docs/gateway/traffic-policy/secrets). This step is optional—you can also include the certificate directly in your policy. Create a vault to store your CA certificate: ```bash theme={null} ngrok api vaults create --name "database-auth" --description "Database gateway CA certificate" ``` Add your CA certificate to the vault using the vault ID from the response: ```bash theme={null} # Add CA certificate for mTLS authentication ngrok api secrets create \ --name "client-ca-certificate" \ --value "-----BEGIN CERTIFICATE-----\nYour CA certificate content here\n-----END CERTIFICATE-----" \ --vault-id "vault_2yNPzuk6GjHrx3mlOCkJK42RsdR" ``` ## 5. Apply Traffic Policy to your Cloud Endpoint While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. ```yaml theme={null} on_tcp_connect: # Enable mTLS certificate authentication - actions: - type: terminate-tls config: mutual_tls_certificate_authorities: - "${secrets.get('database-auth', 'client-ca-certificate')}" mutual_tls_verification_strategy: "require-and-verify" on_http_request: # Rate limit per client certificate subject - actions: - type: rate-limit config: name: "Database access rate limiting per certificate" algorithm: "sliding_window" capacity: 500 rate: "1h" bucket_key: ["actions.ngrok.terminate_tls.client.subject"] # Block dangerous SQL operations - expressions: - "req.url.query.contains('DROP') || req.url.query.contains('DELETE') || req.url.query.contains('TRUNCATE')" actions: - type: custom-response config: status_code: 403 headers: content-type: "application/json" body: | { "error": "Forbidden operation detected", "message": "DROP, DELETE, and TRUNCATE operations are not allowed through this gateway", "timestamp": "${timestamp(time.now)}" } # Forward to database service - actions: - type: forward-internal config: url: https://database-service.internal ``` **What's happening here?** This policy enforces mTLS certificate authentication where clients must present valid certificates signed by your trusted CA. Clients are rate limited based on their certificate identity, dangerous SQL operations are blocked, and authenticated requests are forwarded to your database service. If you don't have certificates, you can generate them using the steps in the [mTLS example documentation](/docs/gateway/traffic-policy/actions/terminate-tls/#enabling-mutual-tls): ```bash theme={null} # Generate CA private key and certificate openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:2048 openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=DatabaseCA" # Generate client private key and certificate openssl genpkey -algorithm RSA -out client.key -pkeyopt rsa_keygen_bits:2048 openssl req -new -key client.key -out client.csr -subj "/CN=DatabaseClient" openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256 ``` Add the contents of `ca.crt` to your vault as `client-ca-certificate`. ## 6. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. Test the mTLS authentication and rate limiting: ```bash theme={null} # Use client certificates (after generating them with the commands above) curl --cert client.crt --key client.key \ "https://$NGROK_DOMAIN/api/users?limit=10" # This should be rate limited after exceeding the configured threshold for i in {1..600}; do curl --cert client.crt --key client.key \ "https://$NGROK_DOMAIN/api/users?limit=1" done ``` ## What's next? * Read about [other gateway shapes](https://ngrok.com/blog-post/api-gateway-shapes-patterns-2025) you can build with ngrok * Learn more about [mTLS certificate authentication](/docs/gateway/traffic-policy/actions/terminate-tls) for high-security environments * For token-based authentication, explore [JWT validation](/docs/gateway/traffic-policy/actions/jwt-validation) as a scalable alternative to basic authentication * Set up [comprehensive logging](/docs/gateway/traffic-policy/actions/log) to send database access events to your SIEM or monitoring platform * Use [URL rewriting](/docs/gateway/traffic-policy/actions/url-rewrite) to transform database queries or add security constraints * View database access patterns in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to identify potential security issues # Access CI/CD Deploy Previews on Secure URLs Source: https://ngrok.com/docs/gateway/examples/ephemeral-workloads Expose deploy previews and CI test environments on secure, short-lived URLs with ngrok endpoints and Traffic Policy. Deploy previews and CI test environments are ephemeral by nature—they spin up for a branch, get tested, and disappear. But accessing them securely and reliably can be tricky: you need a public URL for external testing tools or manual review, but you don't want zombie previews leaking your roadmap to the public internet. ngrok makes ephemeral workloads easier by letting you: * Expose staging or preview builds on-demand with secure, authenticated URLs. * Use dynamic endpoint URLs based on branch names, PR numbers, or commit SHAs. * Apply Traffic Policy to add authentication, logging, and access control to every preview. * Clean up automatically when CI jobs finish. ## What you'll need * A CI/CD platform like GitHub Actions, GitLab CI, or Jenkins * A [pay-as-you-go](/docs/pricing-limits/) account for custom domains ## 1. Reserve a wildcard domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a [custom wildcard domain](/docs/gateway/domains/custom-domains/) like `*.preview.example.com`. You'll then need to set up CNAME records with your DNS provider. This wildcard lets you dynamically create preview URLs like `pr-123.preview.example.com` for each pull request or branch. ## 2. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 3. Add routing with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://${req.host.split(".preview.example.com")[0]}.internal ``` **What's happening here?** This policy dynamically routes each preview URL to a matching internal Agent Endpoint. Requests to `pr-123.preview.example.com` get forwarded to `https://pr-123.internal`, which connects to the ephemeral container or service running your deploy preview. ## 4. Add authentication to your previews Deploy previews shouldn't be publicly accessible. Use the [OAuth action](/docs/gateway/traffic-policy/actions/oauth) to require reviewers to authenticate before accessing any preview and deny all requests from those without a `example.com` email: ```yaml theme={null} on_http_request: - actions: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@example.com')" actions: - type: deny - actions: - type: forward-internal config: url: https://${req.host.split(".preview.example.com")[0]}.internal ``` This ensures only authenticated users from your domain can access previews, while keeping the dynamic routing intact. ## 5. Start an Agent Endpoint from your CI pipeline In your CI/CD workflow, start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/) that matches the preview URL pattern. The exact setup depends on your CI platform, but here's a GitHub Actions example: ```yaml theme={null} name: Deploy Preview on: pull_request: types: [opened, synchronize, reopened] jobs: preview: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build and start app run: | # Build your app npm install && npm run build # Start your app in the background npm run start & sleep 5 - name: Install ngrok run: | curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \ | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \ | sudo tee /etc/apt/sources.list.d/ngrok.list sudo apt update && sudo apt install ngrok - name: Start ngrok tunnel env: NGROK_AUTHTOKEN: ${{ secrets.NGROK_AUTHTOKEN }} run: | ngrok config add-authtoken $NGROK_AUTHTOKEN ngrok http 3000 --url https://pr-${{ github.event.pull_request.number }}.internal & sleep 5 - name: Run tests against preview run: | curl -sSf https://pr-${{ github.event.pull_request.number }}.preview.example.com - name: Comment preview URL uses: actions/github-script@v7 with: script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `🚀 Deploy preview available at: https://pr-${{ github.event.pull_request.number }}.preview.example.com` }) ``` **What's happening here?** This workflow builds your app, starts it on port 3000, then creates an internal Agent Endpoint at `https://pr-{PR_NUMBER}.internal`. The Cloud Endpoint you configured earlier routes traffic from `pr-{PR_NUMBER}.preview.example.com` to this internal endpoint. When the job finishes, the agent stops and the preview disappears. ## 6. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## 7. Route external testing tools (optional) If you use external testing services like BrowserStack, Sauce Labs, or a third-party QA platform, they can hit your preview URLs directly. Add IP restrictions to allow only your testing provider: ```yaml theme={null} on_http_request: - expressions: - "!conn.client_ip in ['']" actions: - type: deny - actions: - type: forward-internal config: url: https://${req.host.split(".preview.example.com")[0]}.internal ``` Or combine OAuth for manual reviewers with IP allowlisting for automated tests: ```yaml theme={null} on_http_request: - expressions: - "conn.client_ip in ['']" actions: - type: forward-internal config: url: https://${req.host.split(".preview.example.com")[0]}.internal - actions: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@example.com')" actions: - type: deny - actions: - type: forward-internal config: url: https://${req.host.split(".preview.example.com")[0]}.internal ``` ## What's next? * View your preview traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to debug failing tests or observe how reviewers interact with previews. * Explore other CI/CD patterns like [blue-green deployments](/docs/gateway/examples/blue-green-deployments/) or [canary deployments](/docs/gateway/examples/canary-deployments/) for production rollouts. * Use the [Kubernetes Operator](/docs/gateway/k8s/) to manage ephemeral workloads in Kubernetes clusters with the same patterns. # Implement the 'Front Door' Pattern Source: https://ngrok.com/docs/gateway/examples/front-door-pattern By using Cloud Endpoint and internal Agent Endpoints together, you loosely double your gateway and services for extra flexibility. The Front Door pattern is a common API gateway design where a single public endpoint serves as the centralized entrance to your upstream services. Why use this pattern? It creates a clear boundary between your public-facing gateway and your internal services and creates a consistent interface for exposing them to the public internet. This simplifies how you manage ingress, helps you route traffic to multiple services, and gives you a unified place to apply gateway-wide policies like authentication, rate limiting, and more. With this pattern, you can: * Host any number of services under a single hostname (for example, `your-company.com`) by routing via path, subdomain, headers, or any other [Traffic Policy variable](/docs/gateway/traffic-policy/variables). * Apply certain policies like authentication at your gateway, then layer in other policies for specific services. * Prevent your services or their host systems from being exposed to the public internet. * Give infrastructure and platform teams control over creating consistent global policy while also allowing developers to manage how traffic reaches their services. ## 1. Create an endpoint for your service Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your service listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url https://service.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Add routing to your service with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: http://service.internal ``` **What's happening here?** The Traffic Policy engine forwards all HTTP requests to the internal Agent Endpoint you created at `https://service.internal`. ## 5. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## Optional: Add a second service and routing If you have another service to host under the front door pattern, start another agent. ```bash theme={null} ngrok http $PORT --url https://service-two.internal ``` Next, update your policy to route traffic based on the path. Copy and paste the policy below into the ngrok dashboard, replacing `/one` and `/two` with the paths you'd like to use for routing traffic to each service. ```yaml theme={null} on_http_request: - expressions: - "req.url.includes('/one')" actions: - type: forward-internal config: url: http://service.internal - expressions: - "req.url.includes('/two')" actions: - type: forward-internal config: url: http://service-two.internal ``` **What's happening here?** The Traffic Policy engine forwards all HTTP requests to the `/one` path to the internal Agent Endpoint at `https://service.internal` and requests to the `/two` path to `https://service-two.internal`. ## What's next? * Explore [more routing options](/docs/gateway/traffic-policy/examples/route-requests/) like routing by path, header, cookie, geographic location, or IP intelligence. * [Add authentication](/docs/gateway/traffic-policy/examples/add-authentication/) to your endpoints with OAuth, OIDC, JWT validation, or Basic Auth. * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to find potential issues or observe patterns of traffic you may want to [block using additional policies](/docs/gateway/traffic-policy/examples/block-unwanted-requests/) on your Cloud Endpoint. # Support Multiple Authentication Methods with Traffic Policy Source: https://ngrok.com/docs/gateway/examples/ip-restrictions-basic-auth Support multiple ways for your users to connect to upstream services with a flexible OR Operator pattern. Some services need to support different groups of users with different types of credentials. Internal teams might use static IPs, but external users authenticate using Basic Auth. With ngrok’s Traffic Policy engine, you can let both types of users through safely without making any changes to your upstream service. This example walks you through how to apply multiple authentication layers to a single endpoint using [action result variables](/docs/gateway/traffic-policy/variables/action/) and expressions that mimic an `OR` Operator. You'll allow requests from trusted IPs `OR` those with valid Basic Auth credentials, and then reject all others. ## 1. Start an endpoint for your service Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your service listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url https://service.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Add layers of authentication with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. You will need to change: * `$TRUSTED_IP_FOO`/`$TRUSTED_IP_BAR`: Replace with public IPs of those who should have access to your service or an [IP policy](https://dashboard.ngrok.com/ip-restrictions) in your ngrok dashboard. ```yaml theme={null} on_http_request: # Check whether a request's IP matches your allow list. - actions: - type: restrict-ips config: # By specifying `false`, you allow the action to run and generate # response variables, like `action.ngrok.restrict_ips.action, which # allow you to more precisely customize how your policy responds. enforce: false allow: - $TRUSTED_IP_FOO/32 - $TRUSTED_IP_BAR/32 # If IP restriction fails, try Basic Auth - expressions: - actions.ngrok.restrict_ips.action == "deny" actions: - type: basic-auth config: enforce: false credentials: - user:password1 - admin:password2 # Forward the request if either the IP restriction OR the Basic Auth actions were successful. - expressions: - "actions.ngrok.restrict_ips.action == 'allow' || actions.ngrok.basic_auth.credentials.authorized == true" actions: - type: forward-internal config: url: https://service.internal # Catch-all custom response for any unauthenticated requests. - actions: - type: custom-response config: status_code: 401 headers: {} body: >- Unauthorized
🚫 Access denied
``` **What's happening here?** The first section of this policy checks every HTTP request to see if its source IP matches one on your `allow` list for `restrict-ips`, but doesn't actually enforce the restriction there. Instead, the expression that follows the first action checks whether the [`restrict-ips` action result variable](/docs/gateway/traffic-policy/actions/restrict-ips/#action-result-variables) `actions.ngrok.restrict_ips.action` is set to `allow`, which means that it would've allowed the request directly if you had set `enforce: true`. If it's false—the request didn't match the IP `allow` list—then the policy then validates whether it contains a valid Basic Auth header. Next, the policy checks whether either IP restrictions `OR` Basic Auth were successful, and if so, forwards to your upstream service. A catch-all rule then sends a custom error response to all other requests. ## 6. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. Add base64-encoded `username:password` pair as an `Authorization: Bearer` header to verify your `basic-auth` action works as expected. ```bash theme={null} curl https://$NGROK_DOMAIN \ --header 'Authorization: Bearer $BASE64_CREDENTIALS' ``` ## What's next? * Explore [other authentication methods](/docs/gateway/traffic-policy/examples/add-authentication/) like OAuth, OIDC, JWT validation, or SAML that you can layer in using the same pattern. * Use the action result variables and [CEL interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation) to add specific error messages to your catch-all `custom-response` action. * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to see requests that failed to authenticate but should have—for example, maybe a specific IP address is missing from your `restrict-ips` allow list or a user who doesn't realize they're using an invalid JWT token. # Secure Admin Dashboards Source: https://ngrok.com/docs/gateway/examples/lock-admin-dashboards Add multiple layers of protection and authentication to your dashboards to prevent threat actors from even attempting attacks. Even if your admin dashboards and internal tools already have built-in authentication, you can make them even secure using ngrok and Traffic Policy to restrict specific types of traffic from even hitting your endpoint. You can restrict access to specific IP addresses, block traffic from problematic sources, restrict requests to only your geographical area, or all the above. In this example, this guide assumes that your admin tool is part of your public-facing service, available on a path like `/admin`. ## 1. Start an endpoint for your service Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your service listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url https://service.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Restrict access to your admin path with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. You may need to change: * `/admin`: Replace with the path of your admin tools. * `1.2.3.4/5.6.7.8`: Replace with public IPs of your admins. * `US`: Replace with the country code where your admins would log in to. ```yaml theme={null} on_http_request: - expressions: - "req.url.path.includes('/admin')" - "conn.client_ip.geo.location.country_code != 'US' || req.user_agent.is_bot || 'proxy.anonymous.tor' in conn.client_ip.categories || 'blocklist.greensnow_co' in conn.client_ip.categories" actions: - type: deny - expressions: - "req.url.path.includes('/admin')" actions: - type: restrict-ips config: allow: - 1.2.3.4/32 - 5.6.7.8/32 - actions: - type: forward-internal config: url: https://service.internal ``` **What's happening here?** Your policy checks every HTTP request and filters out only those to the `/admin` path, first denying all requests from non-U.S. traffic, bot traffic, Tor networks, and all IPs on the GreenSnow blocklist. For all requests not immediately denied, ngrok then also restricts access to only specific and trusted IP addresses before forwarding to your upstream service. ngrok forwards all requests to paths other than `/admin` without these filters or actions. ## 5. Try out your restricted admin tools Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## What's next? * Explore other examples of using the [`forward-internal`](/docs/gateway/traffic-policy/actions/forward-internal/#examples) and [`deny`](/docs/gateway/traffic-policy/actions/deny/#examples-on_http_request). * View who's trying to access your admin tools in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). # Deploy a Consistent Maintenance Message with Traffic Policy Source: https://ngrok.com/docs/gateway/examples/maintenance-mode Learn how to use Traffic Policy to immediately deploy a consistent maintenance message without needing to spin up a new backend service or change DNS records. Whenever you're making infrastructure changes, a major upgrade to your services during planned downtime, or experiencing an incident, you still want to maintain a public-facing presence on your endpoints to tell customers what's going on. With ngrok, you can use Traffic Policy and the [`custom-response`](/docs/gateway/traffic-policy/actions/custom-response/) action to immediately deploy a consistent maintenance message without needing to spin up a new backend service or change DNS records to point to a third-party app. ## 1. Create an endpoint for your service Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your service listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url https://service.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Create a maintenance page on your Cloud Endpoint with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. ```yaml theme={null} on_http_request: - actions: - type: custom-response config: body: "We're currently undergoing maintenance. Please check back later!" - type: forward-internal config: url: https://service.internal ``` **What's happening here?** This policy "interrupts" every request by providing a custom response containing your maintenance message. When your maintenance window is over and you're ready to route traffic to your upstream service, you can remove or comment out the `custom-response` action. ```yaml theme={null} on_http_request: - actions: # - type: custom-response # config: # body: "We're currently undergoing maintenance. Please check back later!" - type: forward-internal config: url: https://service.internal ``` ## 5. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## What's next? * Learn more about using Traffic Policy to return custom responses by [trying out the examples](/docs/gateway/traffic-policy/actions/custom-response/#examples). * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). # Microservices API Gateway Source: https://ngrok.com/docs/gateway/examples/microservices-gateway Coordinate ingress traffic for microservices without a full service mesh, using ngrok to handle north-south traffic patterns with authentication and routing. A microservices gateway coordinates ingress traffic for distributed services without requiring a full service mesh. It handles north-south traffic (public internet to your services) with JWT authentication and intelligent routing, providing a central place to route, secure, and monitor distributed microservices. With this setup, you can: * Handle public API traffic with JWT authentication and rate limiting * Route requests to different microservices based on URL paths * Apply consistent authentication and authorization policies * Deploy and test services independently without rewriting routing logic * Debug distributed request flows from a central gateway ## 1. Install the ngrok Kubernetes Operator Install the ngrok Kubernetes Operator in your cluster. For detailed installation instructions, see the [Kubernetes CRDs getting started guide](/docs/getting-started/kubernetes/crds). ## 2. Create AgentEndpoints for your microservices Create `AgentEndpoint` CRDs to expose your microservices. These create internal endpoints that are not publicly accessible but can be routed through your gateway. **User service:** ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: user-service namespace: default spec: url: https://user-service.internal upstream: url: http://user-service.default.svc.cluster.local:8080 description: "User service" ``` **Order service:** ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: order-service namespace: default spec: url: https://order-service.internal upstream: url: http://order-service.default.svc.cluster.local:8080 description: "Order service" ``` **Notification service:** ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: notification-service namespace: default spec: url: https://notification-service.internal upstream: url: http://notification-service.default.svc.cluster.local:8080 description: "Notification service" ``` Apply these configurations: ```bash theme={null} kubectl apply -f user-service.yaml kubectl apply -f order-service.yaml kubectl apply -f notification-service.yaml ``` ## 3. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 4. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 5. (Optional) Create secrets for authentication Store your JWT validation keys using [Traffic Policy Secrets](/docs/gateway/traffic-policy/secrets). ```bash theme={null} # Create vault for microservices auth ngrok api vaults create --name "microservices-auth" --description "Microservices authentication secrets" # Add JWT validation key ngrok api secrets create \ --name "jwt-public-key" \ --value "-----BEGIN PUBLIC KEY-----\nYour JWT validation public key here\n-----END PUBLIC KEY-----" \ --vault-id "$VAULT_ID" ``` ## 6. Apply Traffic Policy to your Cloud Endpoint Configure your Cloud Endpoint with JWT authentication and routing to your microservices. Make sure you change each of the following values: * `$JWT_ISSUER`: Replace with your JWT issuer URL * `$JWT_AUDIENCE`: Replace with your JWT audience claim * `$JWT_PUBLIC_KEY_URL`: Replace with your JWKS endpoint URL * Service URLs: Replace with your actual internal service endpoints ```yaml theme={null} on_http_request: # WAF-like protection using OWASP CRS - actions: - type: owasp-crs-request config: on_error: halt # JWT validation for public API access - actions: - type: jwt-validation config: issuer: allow_list: - value: "https://$JWT_ISSUER" audience: allow_list: - value: "$JWT_AUDIENCE" http: tokens: - type: "access_token" method: "header" name: "Authorization" prefix: "Bearer " jws: allowed_algorithms: ["RS256", "ES256"] keys: sources: additional_jkus: ["$JWT_PUBLIC_KEY_URL"] # Rate limiting per JWT subject - actions: - type: rate-limit config: name: "API rate limiting per user" algorithm: "sliding_window" capacity: 1000 rate: "1h" bucket_key: ["actions.ngrok.jwt_validation.tokens[0].payload.sub"] # Route to user service - expressions: - "req.url.path.startsWith('/api/users')" actions: - type: forward-internal config: url: https://user-service.internal # Route to order service - expressions: - "req.url.path.startsWith('/api/orders')" actions: - type: forward-internal config: url: https://order-service.internal # Route to notification service - expressions: - "req.url.path.startsWith('/api/notifications')" actions: - type: forward-internal config: url: https://notification-service.internal on_http_response: # OWASP protection for responses - actions: - type: owasp-crs-response config: on_error: halt # Add service tracing headers - actions: - type: add-headers config: headers: x-service-version: "v1.0" x-processed-at: "${time.now}" x-client-ip: "${conn.client_ip}" ``` **What's happening here?** This policy creates a secure, centralized API gateway for your microservices architecture. On every HTTP request, the policy first applies OWASP Core Rule Set protection to block malicious requests like SQL injection and XSS attacks, then validates JWT tokens to ensure only authenticated users can access your APIs, and applies per-user rate limiting based on the JWT subject to prevent abuse. The gateway intelligently routes traffic to the appropriate microservice based on URL paths—user management requests go to the user service, order-related requests to the order service, and notification requests to the notification service. On responses, it applies OWASP protection to scan outgoing content for vulnerabilities and adds service tracing headers for observability and debugging across your distributed system. ## 7. Try out your microservices gateway Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. Test the public API gateway: ```bash theme={null} # Get a JWT token (replace with your auth provider) JWT_TOKEN="your_jwt_token_here" # Test user service curl -H "Authorization: Bearer $JWT_TOKEN" \ "https://$NGROK_DOMAIN/api/users/123" # Test order service curl -H "Authorization: Bearer $JWT_TOKEN" \ "https://$NGROK_DOMAIN/api/orders/456" # Test notification service curl -H "Authorization: Bearer $JWT_TOKEN" \ "https://$NGROK_DOMAIN/api/notifications/789" ``` ## What's next? * Explore [other authentication methods](/docs/gateway/traffic-policy/examples/add-authentication/) like OAuth, OIDC, or Basic Auth. * Learn about [more routing options](/docs/gateway/traffic-policy/examples/route-requests/) like routing by header, cookie, or geographic location. * Read about [other gateway shapes](https://ngrok.com/blog-post/api-gateway-shapes-patterns-2025) you can build with ngrok. * Learn about [AgentEndpoint CRDs](/docs/gateway/k8s/crds/agentendpoint) for more advanced Kubernetes configurations. * Use [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to debug microservice request flows. * Configure [endpoint pooling](/docs/gateway/k8s/guides/pooling) for high availability across multiple service replicas. # Secure Your Public Minecraft Server Source: https://ngrok.com/docs/gateway/examples/minecraft Unwanted players can quickly destroy your hard work, but you can restrict access by letting in a specific set of IP addresses. Unwanted players joining your Minecraft server can quickly destroy your hard work before you have a chance to kick them out. The good news is since you're using ngrok, you can restrict access to your Minecraft server by only letting in a specific set of IP addresses. This example is focused on securing your Minecraft server using Traffic Policy. For more general instructions on using ngrok with Minecraft, including limitations of our [free plan](/docs/pricing-limits/free-plan-limits/) and troubleshooting common issues, see our [using ngrok with Minecraft](/docs/using-ngrok-with/minecraft/) guide. ## 1. Create a Traffic Policy file On the system where your Minecraft server runs, create a file named `minecraft.yaml` and paste in the policy below, replacing `$YOUR_IP` and `$FRIEND_IP` with public IPs. If you or your friends don't know theirs, ngrok has a simple [helper page](https://ipv4.ngrok.com/) that returns your public IP address. ```yaml theme={null} on_tcp_connect: - actions: - type: restrict-ips config: enforce: true allow: - $YOUR_IP/32 - $ANOTHER_FRIEND_IP/32 ``` **What's happening here?** This policy checks whether every TCP connection originates from an IP address matching one on the `allow` list. If they match, the policy allows them connect to your ngrok agent and your Minecraft server, but if they don't, ngrok denies the request. ## 2. Start your Minecraft endpoint On the same system where Minecraft runs, start your ngrok agent on port `25565`, which is the default for Minecraft, and include the `minecraft.yaml` file you just created. ```bash theme={null} ngrok tcp 25565 --traffic-policy-file minecraft.yaml ``` Once the agent starts, take note of the random public TCP address assigned to your Minecraft server, like `tcp://8.tcp.us-cal-1.ngrok.io:10891`. ## 4. Try out your Minecraft endpoint You and others can now connect to your Minecraft server at the public TCP address. As long as your IPs match those in the `allow` list, you'll be able to connect. ## Optional: Get a permanent TCP address With a production [pay-as-you-go account](/docs/pricing-limits/), you can reserve a permanent TCP address for your Minecraft server, which means you and others won't need to change the URL after restartng the server or its host machine. Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve the TCP address. Next, restart your agent, replacing `tcp://1.tcp.ngrok.io:12345` with the TCP address you reserved. ```bash theme={null} ngrok tcp 25565 --url tcp://1.tcp.ngrok.io:12345 --traffic-policy-file minecraft.yaml ``` ## What's next? * Read the [Minecraft guide](/docs/using-ngrok-with/minecraft/) for details on free plan limitations and troubleshooting. * View your Minecraft traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to verify that your IP restrictions are working. * Track your monthly bandwidth and Traffic Policy executions on your [usage page](https://dashboard.ngrok.com/usage) to stay aware of any limitations you might reach, particularly on ngrok's [free plan](/docs/pricing-limits/free-plan-limits/). * Read more about [Traffic Policy](/docs/traffic-policy), [core concepts](/docs/gateway/traffic-policy/how-it-works), and [actions](/docs/gateway/traffic-policy/actions) you might want to implement next. # Multiplex to Internal Services from a Single Domain Source: https://ngrok.com/docs/gateway/examples/multiplex Dynamically serve any number of API or web app services from a single fully qualified domain name (FQDN) without complex networking configurations. Multiplexing is a common gateway design where you host multiple services from a single fully qualified domain name (FQDN) like `example.com`. For example, `app.example.com` can serve a static web app while `api.example.com` serves a JSON API using entirely different services and infrastructure. With ngrok Cloud Endpoints and Traffic Policy, you can dynamically determine where your traffic is forwarded to. ngrok simplifies multiplexing by letting you: 1. Put a URL online to accept internet traffic to a [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/), which allows you to control how that traffic accesses the internal resources where your [internal Agent Endpoints](/docs/gateway/endpoints/agent-endpoints/) are running. This is called the ["front door"](/docs/gateway/examples/front-door-pattern) pattern. 2. Use [Traffic Policy](/docs/traffic-policy) to create dynamic routing topologies based on subdomains or headers. 3. Add new services behind your gateway without service discovery, manual registration, certificate management, split-horizon DNS zones, or any other complex networking configurations. 4. Remove the need to manage multiple domains or subdomains for each service, which can be cumbersome and error-prone. ## 1. Create endpoints for your services Start internal [Agent Endpoints](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your service listens, for each of your services on the systems where they run. You can also use one of the [SDKs](/docs/agent-sdks/#example-usage) or the [Kubernetes Operator](/docs/getting-started/kubernetes/ingress/). ```bash theme={null} ngrok http $PORT --url https://foo.internal ngrok http $PORT --url https://bar.internal ngrok http $PORT --url https://baz.internal ``` Make sure the hostname of the internal endpoint matches the subdomain or header value you want to use for routing. * `https://foo.example.com` → `https://foo.internal` * `x-service: bar` → `https://bar.internal` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a [wildcard domain](/docs/gateway/domains/custom-domains/) like `*.example.com`. You'll then need to set up CNAME records with your DNS provider. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Add multiplexing with Traffic Policy While still viewing your new Cloud Endpoint in the dashboard, copy one of the three policies below and paste it into the Traffic Policy editor. The choice depends on whether you want to route by hostname/subdomain, header value, or with global variables that are accessed at runtime. ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://${req.host.split(".$NGROK_DOMAIN")[0]}.internal ``` **What's happening here?** This policy forwards every HTTP request to one of your internal Agent Endpoints, which in turn forward traffic to your internal services. The [`split` macro](/docs/gateway/traffic-policy/macros/#stringsplit---list) dynamically routes traffic based on the incoming hostname so that the policy forwards requests to the `foo` subdomain to `https://foo.internal`, and so on. ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://${getReqHeader('x-service')[0]}.internal ``` **What's happening here?** This policy forwards every HTTP request to one of your internal Agent Endpoints, which in turn forward traffic to your internal services. The [`split` macro](/docs/gateway/traffic-policy/macros/#stringsplit---list) dynamically routes traffic based on the `x-service` header so that the policy forwards requests with the `x-service: foo` header to `https://foo.internal`, and so on. ```yaml theme={null} on_http_request: - actions: - type: set-vars config: allowed_subdomains: - user3 - user2 - user1 subdomain: ${getReqHeader('host')[0].split('.', 1)[0]} - expressions: - "!(vars.subdomain in vars.allowed_subdomains)" actions: - type: custom-response config: body: 'Denied.' - actions: - type: forward-internal config: url: 'http://${vars.subdomain}.internal' ``` **What's happening here?** This policy first sets a variable `allowed_subdomains` to a list of subdomains that are allowed to access the service. Then it checks if the incoming request's subdomain is in that list. If not, it returns a custom response with a `Denied` message. If the subdomain is allowed, it forwards the request to the corresponding internal Agent Endpoint. ## 5. Try out your endpoints Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. When you try various subdomains or header values in your requests, you should see your various APIs or apps as they're connected to your internal Agent Endpoints. ## What's next? * Explore [more routing options](/docs/gateway/traffic-policy/examples/route-requests/), like routing by cookie, query parameter, geographic location, IP intelligence, or device type. * [Add authentication](/docs/gateway/traffic-policy/examples/add-authentication/) like OAuth, OIDC, or JWT validation to some or all of your multiplexed services, or even [combine multiple methods](/docs/gateway/examples/ip-restrictions-basic-auth/) to let users authenticate with one `OR` another method. * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to ensure multiplexing works as expected or to find potentially abusive patterns you can block using [IP Intelligence](/docs/gateway/traffic-policy/variables/ip-intel/) or the [OWASP action](/docs/gateway/traffic-policy/actions/owasp-crs-request/). # Securely Put Your Self-Hosted n8n Workflows Online Source: https://ngrok.com/docs/gateway/examples/n8n Get your n8n instance online to share with colleagues or receive webhook data with security measures designed for production usage. n8n allows you to self-host, which is great when you're testing workflows, but when you're ready to turn your instance into a production-ready automation solution and don't want to buy a domain or pay for hosting, you might feel lost. ngrok works with the [self-hosted and enterprise editions](https://docs.n8n.io/hosting/) of n8n, including Docker, Docker Compose, and npm-based installs, to make them securely available on the public internet. That way, you can share n8n with colleagues or accept webhooks to fire off workflows. Building with LLMs? We want your feedback! Sign up for early access to [ngrok's AI Gateway](https://ngrok.ai) and our team will reach out to learn more about your needs. ## 1: Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 2: Run n8n + ngrok To run n8n and ngrok in Docker, you can either use Docker Compose to run both services together or run them as separate containers. In both cases, you can use a `.env` file to store configuration values. Replace `NGROK_DOMAIN` and `NGROK_AUTHTOKEN` with your values, as well as `N8N_HOST` and `WEBHOOK_URL` with your reserved domain. If you don't already have an authtoken, you can find it in your dashboard [here](https://dashboard.ngrok.com/get-started/your-authtoken). ```js .env highlight={1,2,5,6} theme={null} NGROK_DOMAIN=your-n8n-instance.ngrok.app NGROK_AUTHTOKEN=your-ngrok-authtoken N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true N8N_RUNNERS_ENABLED=true N8N_HOST=your-n8n-instance.ngrok.app WEBHOOK_URL="https://your-n8n-instance.ngrok.app/" TZ="Europe/London" GENERIC_TIMEZONE="Europe/London" ``` Once that file is saved, you can either create a `docker-compose.yaml` file to run both services together or run them as separate containers with `docker run`. ```yaml docker-compose.yaml theme={null} services: n8n: image: docker.n8n.io/n8nio/n8n restart: always ports: - "127.0.0.1:5678:5678" env_file: .env volumes: - n8n_data:/home/node/.n8n ngrok: image: ngrok/ngrok:latest restart: always depends_on: - n8n env_file: .env command: http --url=${NGROK_DOMAIN} n8n:5678 ports: - "4040:4040" # ngrok web UI volumes: n8n_data: ``` ```bash docker theme={null} docker network create n8n docker run -d --name n8n \ --network n8n \ -p 5678:5678 \ -v n8n_data:/home/node/.n8n \ --env-file .env \ docker.n8n.io/n8nio/n8n docker run -d \ --name ngrok \ --network n8n \ -p 4040:4040 \ --env-file .env \ ngrok/ngrok:latest http --url=your-n8n-instance.ngrok.app n8n:5678 ``` If you're using Docker Compose, you'll need to run the following command from the same directory where your `docker-compose.yaml` file is located. ```bash theme={null} docker compose up ``` ## 3. Try out your n8n endpoint You can now access your n8n instance at the domain you reserved in step 1. Visit it in your browser to go through the first time n8n admin account setup. ## Optional: Secure your n8n dashboard with Traffic Policy Even though n8n comes with built-in user administration, you can protect the sign-in page from automated attacks by restricting access to only trusted IPs. Go to the [IP address helper tool](https://ipv4.ngrok.com/) to get your public IP address. Next, create a file named `n8n.yaml` on the same system where n8n runs and paste in the following policy, replacing `$YOUR_IP`. ```yaml n8n.yaml theme={null} on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - "$YOUR_IP/32" ``` **What's happening here?** This policy applies the [`restrict-ips`](/docs/gateway/traffic-policy/actions/restrict-ips) Traffic Policy action and allows only devices with a matching IP address to access your n8n instance. All other requests are denied at ngrok's network without reaching your service. Then make the following modifications to your `docker-compose.yaml` file to mount the policy file into the ngrok container and tell ngrok to use it. ```yaml docker-compose.yaml highlight={17-19} theme={null} services: n8n: image: docker.n8n.io/n8nio/n8n restart: always ports: - "127.0.0.1:5678:5678" env_file: .env volumes: - n8n_data:/home/node/.n8n ngrok: image: ngrok/ngrok:latest restart: always depends_on: - n8n env_file: .env command: http --url=${NGROK_DOMAIN} --traffic-policy-file /etc/n8n.yaml n8n:5678 volumes: - ./n8n.yaml:/etc/n8n.yaml ports: - "4040:4040" # ngrok web UI volumes: n8n_data: ``` ```bash docker highlight={7-8} theme={null} docker kill ngrok docker run -d \ --name ngrok \ --network n8n \ -p 4040:4040 \ --env-file .env \ -v $(pwd)/n8n.yaml:/etc/n8n.yaml \ ngrok/ngrok:latest http --url=your-n8n-instance.ngrok.app --traffic-policy-file /etc/n8n.yaml n8n:5678 ``` If you're using Docker Compose, restart your setup to apply the new policy. ```bash theme={null} docker compose down docker compose up ``` ## Optional: Enforce verification on incoming webhooks If you want to receive webhooks from external services, but also want to control precisely which services can send these requests and during what conditions, you can use the [`verify-webhooks` action](/docs/gateway/traffic-policy/actions/verify-webhook). For example, if you want to verify a webhook from GitHub, you can edit your `n8n.yaml` file and paste in the policy below. Replace `$WORKFLOW_UUID` with your workflow's path and `$SECRET` with the secret you configured when you created your webhook. ```yaml n8n.yaml theme={null} on_http_request: - expressions: - "req.url.path.contains('/webhook/$WORKFLOW_UUID')" actions: - type: verify-webhook config: provider: github secret: $SECRET ``` **What's happening here?** This policy filters for *only* traffic that contains the specific `/webhook/$WORKFLOW_UUID` path. It then verifies that the traffic both originates from GitHub (or another [supported provider](/docs/gateway/traffic-policy/actions/verify-webhook/#supported-providers)) and contains the appropriate secret. You'll need to restart your ngrok container to apply the new policy. ### Configure both IP restrictions and webhook verification If you configured IP restrictions earlier, and *then* set up webhook verification, you'll find that your webhooks won't work because you haven't added the provider's IP to your `restrict-ips` action to allow them. Because it's next to impossible to identify all the IPs where a provider's webhooks might come from, it's easier to disable IP restrictions on the `/webhooks/` path. Edit your `n8n.yaml` file with the policy below. ```yaml n8n.yaml theme={null} on_http_request: - expressions: - "req.url.path.contains('/webhook/$WORKFLOW_UUID')" actions: - type: verify-webhook config: provider: github secret: $SECRET - expressions: - "!req.url.path.contains('/webhook/')" actions: - type: restrict-ips config: enforce: true allow: - "$YOUR_IP/32" ``` **What's happening here?** This policy first verifies any incoming webhooks as before. Then, the policy applies IP restrictions to only traffic *not* to the `/webhook/` path, which allows your webhooks provider's request to reach n8n successfully. ## What's next? * Watch the livestreamed [n8n workshop](https://www.youtube.com/watch?v=Og1S25RvIi8) to see this example deployed live. * Read more about [Traffic Policy](/docs/traffic-policy), [core concepts](/docs/gateway/traffic-policy/how-it-works), and [actions](/docs/gateway/traffic-policy/actions) you might want to implement next. * View your Ollama traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). # Expose and Secure Your Self-Hosted Odoo Instance Source: https://ngrok.com/docs/gateway/examples/odoo Use ngrok with Odoo when you want to securely expose Odoo's services to the internet without complex network setups. [Odoo](https://www.odoo.com/) is an open-source ERP and business management suite that you can self-host for development, demos, or integrations. When you need to share your instance over HTTPS or accept webhooks without deploying to the public internet, ngrok gives you a stable URL and powerful Traffic Policy controls to lock down access. This guide follows the style of the [n8n example](/docs/gateway/examples/n8n/) and adds Odoo-specific steps, including using a static domain and applying a Traffic Policy. ## What you'll need * An ngrok account. * The ngrok Agent CLI installed. * Odoo running locally (default: `http://localhost:8069`). * (Recommended) Admin access in Odoo to change System Parameters. ## 1. Reserve a static domain A static domain ensures your URL doesn't change across restarts (important for Odoo links, redirects, and webhooks). In the ngrok dashboard, go to **Domains** → **New +**. Reserve a domain (this example uses `https://your-odoo.ngrok.app`) and save it as an environment variable: ```bash theme={null} export NGROK_DOMAIN=your-odoo.ngrok.app ``` Why a static domain? Odoo stores and uses absolute links. A stable host prevents broken links in emails, reports, and third-party callbacks. ## 2. Start the ngrok endpoint for Odoo Expose Odoo on port `8069` at your static domain: ```bash theme={null} ngrok http --domain=$NGROK_DOMAIN 8069 ``` Open `https://$NGROK_DOMAIN` to see the Odoo login page (`/web/login`). ## 3. (Recommended) Configure Odoo for a proxy and stable URLs Make sure Odoo generates correct HTTPS links and doesn't rewrite your base URL. ### Enable proxy mode If ngrok (or any reverse proxy) terminates TLS, enable Odoo's proxy mode: ```ini title="odoo.conf" theme={null} [options] proxy_mode = True ``` Restart Odoo after changing the config. ### Freeze the base URL Odoo updates `web.base.url` based on the host used at login. To keep it stable: In Odoo, go to **Settings** → **Technical** → **System Parameters** (enable developer mode if needed). Create or update: * `web.base.url` → `https://$NGROK_DOMAIN` * `web.base.url.freeze` → `True` This prevents unexpected rewrites if someone visits via a different hostname. ## 4. Apply a Traffic Policy (secure your admin) Protect sensitive paths like `/web` and `/web/login` using Traffic Policy. Create a policy file (such as `odoo-policy.yaml`) and run the agent with `--traffic-policy-file`. ### Option A: Restrict admin to your IP ```yaml title="odoo-policy.yaml" theme={null} on_http_request: - if: request.path.startsWith("/web") actions: - type: restrict-ips config: # Replace with your public IP/CIDR, such as "203.0.113.7/32" allow: ["$YOUR_IP_CIDR"] ``` Run: ```bash theme={null} ngrok http --domain=$NGROK_DOMAIN 8069 \ --traffic-policy-file odoo-policy.yaml ``` Only the allowed IPs can reach `/web*`; other requests are blocked before they hit Odoo. ### Option B: Require Basic Auth on admin ```yaml title="odoo-policy.yaml" theme={null} on_http_request: - if: request.path.startsWith("/web") actions: - type: basic-auth config: realm: "Odoo Admin" credentials: - "admin:changeMeNow123" ``` Run with the same `--traffic-policy-file` flag. Visitors to `/web*` must pass HTTP Basic Auth before Odoo's own login. You can combine rules—for example, applying Basic Auth for `/web*` and providing unrestricted access for public website paths. ## 5. Test your endpoint From a terminal: ```bash theme={null} curl -I https://$NGROK_DOMAIN ``` Expected: HTTP/2 200 and Odoo's login route (`/web/login`) when loaded in a browser. ## Troubleshooting * **Blank or mixed-content pages**: Ensure `proxy_mode = True` and you're visiting `https://$NGROK_DOMAIN`. * **Redirect loops or wrong links**: Verify `web.base.url` is exactly `https://$NGROK_DOMAIN` and `web.base.url.freeze = True`. * **403 when applying policy**: Confirm your IP/CIDR is correct or disable the policy while debugging. * **Port issues**: Odoo defaults to `8069`; double-check the port if customized. ## What's next? * Layer additional [policies](/docs/gateway/traffic-policy/) (OAuth/OIDC, rate limits, IP allow/deny lists) as needed. * Use the ngrok dashboard's [Traffic Inspector](/docs/obs/traffic-inspection/) to watch live requests and responses while testing. * Reserve a custom domain you own and enable HTTPS via ngrok for a branded URL. # Offload Analytics to a Self-Hosted Service Source: https://ngrok.com/docs/gateway/examples/offload-analytics With http-request, you can capture observability data from your gateway and send it to a self-hosted analytics service securely and without instrumenting your code. ngrok and Traffic Policy let you integrate your ["front door" gateway](/docs/gateway/examples/front-door-pattern) with an analytics service you host within your network to capture vital information about your traffic as a part of the request-response lifecycle. With this gateway setup and the [`http-request` action](/docs/gateway/traffic-policy/actions/http-request), you can: * Host your analytics service in any network, region, or cloud. * Leave your analytics service completely unexposed to the public internet. * Simplify your upstream services by removing libraries and code for instrumenting them directly. * Configure for exactly the timeout and retry conditions your services require. ## 1. Create endpoints for your services Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your upstream service listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url https://service.internal ``` Start a second endpoint for your analytics service. ```bash theme={null} ngrok http $PORT --url https://analytics.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Send request data to your analytics service with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://service.internal on_http_response: - actions: # Send authenticated request to analytics service - type: http-request config: url: https://analytics.internal/api/request method: POST headers: Authorization: "Bearer " Content-Type: "application/json" body: | { "path": "${req.url.path}", "method": "${req.method}", "status_code": "${res.status_code}", "country_code": "${conn.geo.country_code}", "region": "${conn.server_region}", } timeout: 1s ``` **What's happening here?** This policy sends all requests directly to your upstream service at `https://service.internal`. As part of the response lifecycle, this policy also sends an authenticated request to your analytics service with a body containing attributes, injected with CEL interpolation, about the user's request and your server's response. The `http-request` action is purposely given a short timeout and no retry logic to ensure this analytics request doesn't add unnecessarily slow your service's response time. ## 5. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## What's next? * Explore other examples of using the [`http-request`](/docs/gateway/traffic-policy/actions/http-request/#examples) and [`forward-internal`](/docs/gateway/traffic-policy/actions/forward-internal/#examples) Traffic Policy actions. * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). * Add even more logging flexibility, plus access to the entire [eventing system](/docs/obs/), with the [`logs` action](/docs/gateway/traffic-policy/actions/log/). # Expose and Secure Your Self-Hosted Ollama API Source: https://ngrok.com/docs/gateway/examples/ollama Use ngrok to create a public endpoint for your local LLM, enabling remote access and integration, while also adding authentication and access control. Ollama is a locally deployed AI model runner, designed to allow users to download and execute large language models (LLMs) locally on your machine. By combining Ollama with ngrok, you can give your local LLM an endpoint on the internet, enabling remote access and integration with other applications. But putting your entire Ollama API on the public internet might expose your LLM to abuse—instead, you can use Traffic Policy to add a layer of authentication to restrict access to only yourself or trusted colleagues. ## 1. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 2. Create a Traffic Policy file On the system where Ollama runs, create a file named `ollama.yaml` and paste in the following policy: ```yaml theme={null} on_http_request: - actions: - type: add-headers config: headers: host: localhost ``` **What's happening here?** This policy rewrites the `Host` header of every HTTP request to `localhost` so that Ollama accepts the requests. ## 3. Start your Ollama endpoint On the same system where Ollama runs, start the agent on port `11434`, which is the default for Ollama, and reference the `ollama.yaml` file you just created. Be sure to also change `$NGROK_URL` to the domain you reserved earlier. ```bash theme={null} ngrok http 11434 --url https://$NGROK_URL.ngrok.app --traffic-policy-file ollama.yaml ``` ## 4. Try out your Ollama endpoint You can use `curl` in your terminal to send a prompt to your LLM through your `$NGROK_DOMAIN`, replacing `$MODEL` with the [Ollama model](https://ollama.com/library) you pulled. ```bash theme={null} curl https://$NGROK_DOMAIN/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "$MODEL", "messages": [ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": "Hello!" } ] }' ``` ## Optional: Protect your Ollama instance with basic auth You may not want everyone to be able to access your LLM. ngrok can quickly add authentication to your LLM without any changes to your Ollama configuration. Edit your `ollama.yaml` file and add in the policy below. ```yaml theme={null} on_http_request: - actions: - type: basic-auth config: realm: sample-realm credentials: - user:password1 - admin:pasword2 enforce: true - type: add-headers config: headers: host: localhost ``` **What's happening here?** This policy first checks whether the incoming HTTP request contains the appropriate `Authorization: Basic` header and a [base64-encoded](https://en.wikipedia.org/wiki/Base64) version of one of the `username:password` pairs you specified in `ollama.yaml`. Only requests with valid Basic Auth are passed through to your ngrok agent and forwarded to your Ollama API. Restart your ngrok agent to apply the new policy. ```bash theme={null} ngrok http 11434 --url https://$NGROK_URL.ngrok.app --traffic-policy-file ollama.yaml ``` You can test your policy by sending the same LLM prompt to Ollama's API with the `Authorization: Basic` header, once again replacing `$NGROK_DOMAIN` and `$MODEL`. ```bash theme={null} curl https://$NGROK_DOMAIN/v1/chat/completions \ -H "Content-Type: application/json" \ -H 'Authorization: Basic dXNlcjpwYXNzd29yZDE=' \ -d '{ "model": "$MODEL", "messages": [ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": "Hello!" } ] }' ``` If you send the same request *without* the Authorization header, you should receive a `401 Unauthorized` response. Your personal LLM is now locked down to only accept authenticated users. ## What's next? * Read more about [Traffic Policy](/docs/traffic-policy), [core concepts](/docs/gateway/traffic-policy/how-it-works), and [actions](/docs/gateway/traffic-policy/actions) you might want to implement next, like [IP restrictions](/docs/gateway/traffic-policy/actions/restrict-ips) instead of Basic Auth. * Explore other ways to [block unwanted requests](/docs/gateway/traffic-policy/examples/block-unwanted-requests/) from Tor users, search or AI bots, and more to protect your self-hosted LLM. * View your Ollama traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). # Self-Hosted PaaS Alternative Gateway Source: https://ngrok.com/docs/gateway/examples/paas-alternative-gateway Get the polish of managed platforms like Heroku or Vercel while maintaining full control over your infrastructure with load balancing, security, and observability. A self-hosted PaaS alternative gateway provides the developer experience and features of managed platforms without surrendering control of your infrastructure. It handles TLS termination, custom domains, load balancing, authentication, observability, and security—perfect for teams migrating from Heroku or Vercel while keeping the operational polish. With this setup, you can: * Load balance across multiple app instances with automatic failover * Terminate TLS and use custom domains professionally * Add authentication and security without changing your application code * Monitor traffic and performance with comprehensive logging * Protect against attacks with WAF-like rules and rate limiting * Handle geo-aware routing and DDoS protection at the edge ## 1. Create endpoints for your application instances Start multiple [Agent Endpoints](/docs/gateway/endpoints/agent-endpoints/) with the same URL to create an endpoint pool for automatic load balancing. Replace `$PORT` with your application ports. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). If you only have one instance now, you can add additional replicas later for redundancy through load balancing. ```bash theme={null} # Instance 1 ngrok http $PORT --url https://service.internal --pooling-enabled # Instance 2 (optional) ngrok http $PORT --url https://service.internal --pooling-enabled # Instance 3 (optional) ngrok http $PORT --url https://service.internal --pooling-enabled ``` By using the same URL with `--pooling-enabled`, ngrok automatically creates an [Endpoint Pool](/docs/gateway/endpoints/endpoint-pooling) that distributes traffic round-robin across all healthy instances with automatic failover. ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. For a production PaaS alternative, consider using a [custom domain](/docs/gateway/domains/custom-domains/) you own instead of an ngrok subdomain. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Create a vault and secrets Store your authentication and API secrets securely using [Traffic Policy Secrets](/docs/gateway/traffic-policy/secrets). First, create a vault to store your application secrets: ```bash theme={null} ngrok api vaults create --name "paas-gateway" --description "PaaS gateway authentication and API secrets" ``` Then add your secrets using the vault ID from the response: ```bash theme={null} # OAuth client secret for app authentication ngrok api secrets create \ --name "oauth-client-secret" \ --value "your_oauth_client_secret_here" \ --vault-id "$VAULT_ID" # API key for admin access ngrok api secrets create \ --name "admin-api-key" \ --value "your_admin_api_key_here" \ --vault-id "$VAULT_ID" ``` ## 5. Apply Traffic Policy to your Cloud Endpoint While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. Make sure you change each of the following values: * `$GITHUB_CLIENT_ID`: Replace with your GitHub OAuth app client ID * `$YOUR_ADMIN_EMAIL`: Replace with your admin email address for OAuth access * `$YOUR_ADMIN_USERNAME`: Replace with the username for API authentication * `https://service.internal`: Replace with your actual pooled endpoint URL ```yaml theme={null} on_http_request: # WAF-like protection using OWASP CRS - actions: - type: owasp-crs-request config: on_error: halt # Rate limiting for DDoS protection - actions: - type: rate-limit config: name: "Global rate limiting" algorithm: "sliding_window" capacity: 1000 rate: "1m" bucket_key: ["conn.client_ip"] # Block bot traffic and unwanted crawlers - expressions: - "req.user_agent.is_bot || 'proxy.anonymous.tor' in conn.client_ip.categories" actions: - type: deny config: status_code: 404 # Block specific AI crawlers - expressions: - "('com.anthropic' in conn.client_ip.categories) || ('com.openai' in conn.client_ip.categories) || ('com.perplexity' in conn.client_ip.categories)" actions: - type: deny config: status_code: 404 # Admin authentication for /admin paths - expressions: - "req.url.path.startsWith('/admin')" actions: - type: oauth config: provider: google client_id: "$GITHUB_CLIENT_ID" client_secret: "${secrets.get('paas-gateway', 'oauth-client-secret')}" scopes: - https://www.googleapis.com/auth/userinfo.email # Restrict admin access to specific email - expressions: - "req.url.path.startsWith('/admin')" - "actions.ngrok.oauth.identity.email != '$YOUR_ADMIN_EMAIL'" actions: - type: custom-response config: status_code: 403 headers: content-type: "text/plain" body: "Access denied. Contact your administrator." # API authentication for /api paths - expressions: - "req.url.path.startsWith('/api')" actions: - type: basic-auth config: credentials: - "$YOUR_ADMIN_USERNAME:${secrets.get('paas-gateway', 'admin-api-key')}" realm: "API Access" # Forward to your application pool (automatic load balancing) - actions: - type: forward-internal config: url: https://service.internal on_http_response: # OWASP protection for responses - actions: - type: owasp-crs-response config: on_error: halt # Add security headers (based on OWASP recommendations) - actions: - type: add-headers config: headers: # Prevent clickjacking attacks x-frame-options: "DENY" # Prevent MIME-sniffing x-content-type-options: "nosniff" # Control referrer information referrer-policy: "strict-origin-when-cross-origin" # Enforce HTTPS (adjust max-age as needed) strict-transport-security: "max-age=31536000; includeSubDomains" # Cross-origin isolation (modern browsers) cross-origin-opener-policy: "same-origin" ``` **What's happening here?** This policy creates a production-ready PaaS alternative. On every HTTP request, the policy enforces OWASP WAF protection, DDoS mitigation via rate limiting, bot blocking for unwanted crawlers and Tor traffic, and authentication for admin/API routes, before forwarding your request to the pool of upstream services. On every HTTP response, the policy enforces WAF rules on the headers and body, then adds [common security headers](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html) to the response. ## 6. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. Test a few of the features you added with Traffic Policy: ```bash theme={null} # Test WAF with a potentially malicious request curl https://$NGROK_DOMAIN/files?name=../../../../etc/passwd" # Test admin authentication (will redirect to OAuth) curl "https://$NGROK_DOMAIN/admin/dashboard" # Test API authentication (basic auth with base64 encoded credentials) # First, base64 encode the $YOUR_ADMIN_USERNAME:your_admin_api_key_here pair # Example: admin:secret-api-key becomes YWRtaW46c2VjcmV0LWFwaS1rZXk= curl -H "Authorization: Basic YWRtaW46c2VjcmV0LWFwaS1rZXk=" \ "https://$NGROK_DOMAIN/api/stats" # Test bot blocking (should get 404) curl -H "User-Agent: GPTBot" \ "https://$NGROK_DOMAIN/" ``` ## What's next? * Read about [other gateway shapes](https://ngrok.com/blog-post/api-gateway-shapes-patterns-2025) you can build with ngrok * Learn about [Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling) for simpler automatic load balancing * Explore [custom domains](/docs/gateway/domains/custom-domains) to use your own domain instead of ngrok subdomains * Use [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to monitor performance and debug issues * Add [circuit breaker protection](/docs/gateway/traffic-policy/actions/circuit-breaker) to handle upstream failures gracefully # Pre-Tier Requests by IP Address Source: https://ngrok.com/docs/gateway/examples/pre-tier-requests Use existing customer data and your service's pricing model to apply the proper rate limit to a user's IP address. If you have a public API that your customers integrate into their software or services, you'll want to implement a rate limiting policy to prevent misuse, ensure fair access for all users, and prevent heavy load from slowing down your entire infrastructure. Based on your packaging and pricing model, you may also need to implement different rate limit limits for customers based on their "tier" and how much they pay. ngrok and Traffic Policy let you validate an incoming user's request against identity or CRM services with data about their tier and selectively apply the proper rate limits to their IP address. ## 1. Create endpoints for your services Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your upstream service listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url https://service.internal ``` Start a second endpoint for your identity service. ```bash theme={null} ngrok http $PORT --url https://id.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Validate requests against your internal identity service with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. You'll need to change: * `https://id.internal/api/user`: Replace the path according to your identity service's API. * `$ID_API_TOKEN`: Replace with an API key or other authentication method generated by your identity service. * `{ "user": "${req.headers['Authorization']}" }`: Replace with an appropriate body based on how your user's requests are structured. ```yaml theme={null} on_http_request: - actions: - type: http-request config: url: https://id.internal/api/user method: POST headers: Authorization: "Bearer $ID_API_KEY" Content-Type: "application/json" body: | { "token": "${req.headers['Authorization']}" } # If the request failed, short-circuit the request with an error response. - expressions: - "actions.ngrok.http_request.res.status_code < '200' && actions.ngrok.http_request.res.status_code >= '300'" actions: - type: custom-response config: headers: content-type: application/json body: | { "message": "Request failed: ${actions.ngrok.http_request.res.status_code}" } # If the request was successful, capture the value of the user's tier for reuse. - expressions: - "actions.ngrok.http_request.res.status_code == 200" actions: - type: set-vars config: vars: tier: json.decode(actions.ngrok.http_request.res.body).tier.value # Check the user's tier based on the variable and apply the rate limit. - expressions: - "vars.tier == 'bronze'" actions: - type: rate-limit config: name: BronzeLimit algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - conn.client_ip - expressions: - "vars.tier == 'silver'" actions: - type: rate-limit config: name: SilverLimit algorithm: sliding_window capacity: 100 rate: 60s bucket_key: - conn.client_ip - expressions: - "vars.tier == 'gold'" actions: - type: rate-limit config: name: GoldLimit algorithm: sliding_window capacity: 1000 rate: 60s bucket_key: - conn.client_ip - actions: - type: forward-internal config: url: https://service.internal ``` **What's happening here?** This policy first sends an authenticated HTTP request to your internal identity/CRM service at `https://id.internal`. This request's body contains the `Authorization` token your user added as a header to their request. If your identity service responds with a `400`-level error code, the policy short-circuits the request and responds with a custom error response. If your identity service responds with a `200` error code, then the policy saves the user's tier as a `vars.tier` variable. The policy then applies different rate limiting capacities to the user's IP address based on the `tier` data stored in your identity service. Finally, the policy forwards the authenticated and rate limited request to your upstream service. ## Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## What's next? * Explore other examples of using the [`http-request`](/docs/gateway/traffic-policy/actions/http-request/#examples) and [`forward-internal`](/docs/gateway/traffic-policy/actions/forward-internal/#examples) Traffic Policy actions. * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to understand how requests flow into your identity service and watch error rates for potential issues, which may influence how you tweak the [retry logic and timeout behavior](/docs/gateway/traffic-policy/actions/http-request/#retry-logic) of your `http-request` action. # Intercept and Rewrite Headers in HTTP Redirect Responses Source: https://ngrok.com/docs/gateway/examples/rewrite-headers-redirects By routing to internal services and editing headers, you can manipulate traffic in ways that allow you to seamlessly access customer networks and upstream services despite their network topology. Many customers use ngrok for [site-to-site connectivity](/docs/gateway/site-to-site-connectivity/). This is usually pretty straightforward, but occasionally a service requires a specific `Host` header or uses internal DNS names or redirect flows that break in an outbound tunneled environment (for example, `302` redirects to `internal.corp.local`). With ngrok, you can use the [`url-rewrite` action](/docs/gateway/traffic-policy/actions/url-rewrite) in a Traffic Policy to intercept `302` redirect headers coming from customer apps to preserve UX and agent behavior. ## 1. Create an endpoint for the customer's upstream service Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where the upstream service listens and using a URL namespacing scheme that logically separates individual services and customers. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). Depending on how you work with your customer, you may be creating this endpoint or instructing them how to do it themselves. ```bash theme={null} ngrok http $PORT --url https://service-01.customer-abc.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Use Traffic Policy to route traffic and edit headers While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor, then replace `$NGROK_DOMAIN`. ```yaml theme={null} on_http_request: - actions: - type: add-headers config: headers: host: 127.0.0.1 - type: forward-internal config: url: https://service-01.customer-abc.internal on_http_response: - expressions: - "res.status_code == 302" actions: - type: set-vars config: vars: orig: "${url.parse(res.location).path}" - type: remove-headers config: headers: - Location - type: add-headers config: headers: Location: "$NGROK_DOMAIN/${vars.orig}" ``` **What's happening here?** First, this policy rewrites the `Host` header on all requests to `127.0.0.1` so that your customer's upstream service recognizes the request as coming from the same local network, not through ngrok, then forwards the request to the internal Agent Endpoint you created in the first step. During the HTTP response, the policy then checks whether the status code is `302`, which indicates content has moved. If the status code is `302`, then the policy saves the existing path to a variable, removes the `Location` header, and writes a new `Location` header by building a new URL based on `$NGROK_URL` and the original path. ## What's next? * Follow along with the [comprehensive site-to-site guide](/docs/gateway/site-to-site-connectivity/) for ways to make your setup more production-ready, like creating Service Users to isolate agents and white-labeling ngrok with a custom connect URL. * Read more about [Traffic Policy](/docs/traffic-policy), [core concepts](/docs/gateway/traffic-policy/how-it-works), and [actions](/docs/gateway/traffic-policy/actions) you might want to implement next. * View your site-to-site traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). # Route API and App Traffic by User Agent Source: https://ngrok.com/docs/gateway/examples/route-api-app-traffic-user-agent Automatically enforce the right type of AuthN and route requests to the appropriate upstream service from the same hostname and path. If you're serving both web app and API services from the same hostname, you can use ngrok and the Traffic Policy engine to: 1. Automatically enforce OAuth or JWT validation on browser or curl/machine-to-machine traffic, respectively. 2. Route only authenticated requests to your upstream app and API services. This way, your users can hit the same hostname, authenticate themselves, and get the response they expect, while you know that all your services are protected. ## 1. Set up your JWT provider You can use any provider/issuer, but there is a guide for defining an API and [generating tokens with Auth0](/docs/integrations/jwt-validation/auth0). ## 2. Start internal endpoints for your services Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your app service listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http 8080 --url https://web-service.internal ``` Start a second endpoint for your API service. ```bash theme={null} ngrok http 8081 --url https://api-service.internal ``` ## 3. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 4. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 5. Add JWT validation, OAuth, and routing with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. You may need to change: * `$YOUR_JWT_DOMAIN`: The domain name for your tenant at your JWT provider—for example, with Auth0, it looks something like `https://example.us.auth0`. * `$YOUR_EMAIL_DOMAIN`: The domain name associated with your organization's Google accounts for OAuth checks. ```yaml theme={null} on_http_request: - expressions: # Check if the client's user agent does not contain 'Mozilla', which means they are an API user - "!(req.user_agent.contains('Mozilla'))" actions: # If true, apply the `jwt-validation` action - type: "jwt-validation" config: issuer: allow_list: - value: "https://$YOUR_JWT_DOMAIN" http: tokens: - type: "jwt" method: "header" name: "Authorization" prefix: "Bearer " jws: allowed_algorithms: - "RS256" keys: sources: additional_jkus: - "https://$YOUR_JWT_DOMAIN/.well-known/jwks.json" - type: "forward-internal" config: url: https://api-service.internal - expressions: # Check if the client's user agent contains 'Mozilla', which means they are a browser user - "req.user_agent.contains('Mozilla')" actions: # If true, apply the oauth action - type: oauth config: provider: google - expressions: # Check again whether the client's user agent contains 'Mozilla,' then also check whether the email they used in the OAuth flow matches $YOUR_DOMAIN, and if both are `true`, then forward to your - "req.user_agent.contains('Mozilla')" - "actions.ngrok.oauth.identity != null && actions.ngrok.oauth.identity.email.endsWith('@$YOUR_EMAIL_DOMAIN')" actions: - type: "forward-internal" config: url: https://web-service.internal - expressions: # In all other situations, use `custom-response` to respond with a generaic unauthorized error - type: "custom-response" config: status_code: 403 ``` **What's happening here?** In the first section, this policy checks whether the request's user agent comes from an API user, then applies the `jwt-validation` action. If the request has the appropriate `Authorization: Bearer …` header, then ngrok forwards it to your API service. In the second section, it then checks whether the user agent is from a human/browser, then asks them to authenticate with Google's OAuth. If the account they sign in to matches `$YOUR_EMAIL_DOMAIN`, then ngrok forwards it to your web app service. In all other cases, the policy returns a `403 Forbidden` response because the request was not properly authenticated. ## 6. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. In your terminal, make sure you add your JWT as a header: ```bash theme={null} curl https://your-service.ngrok.app \ --header 'Authorization: Bearer $JWT_FROM_PROVIDER' ``` ## What's next? * Explore [other authentication methods](/docs/gateway/traffic-policy/examples/add-authentication/) like Basic Auth, OIDC, or SAML. * Learn about [more routing options](/docs/gateway/traffic-policy/examples/route-requests/) beyond user agent, like routing by path, header, cookie, or geographic location. * Explore all the configuration options in the [`jwt-validation`](/docs/gateway/traffic-policy/actions/jwt-validation) and [`oauth`](/docs/gateway/traffic-policy/actions/oauth) actions. * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). # Route to Endpoints by Geography Source: https://ngrok.com/docs/gateway/examples/route-by-geography Show users content based on their region, comply with regulations, or ensure traffic hits the nearest endpoint for the best latency. To route traffic to specific regions, you can use ngrok's Cloud Endpoints, internal Agent Endpoints, and Traffic Policy engine to match incoming traffic based on the [client's IP geolocation data](/docs/gateway/traffic-policy/variables/connection/#conngeocountry-code) using [ISO 3166 country codes](https://www.iso.org/iso-3166-country-codes.html), then forward requests to the appropriate regional endpoints. With this setup, you can: * Showcase user content tailored to their region, like country-specific pricing. * Comply with regulations like GDPR. * Ensure your customers' traffic is routed to the nearest possible endpoint for the best latency. This also implements the ['front door' pattern](/docs/gateway/examples/front-door-pattern), which creates a loose coupling between the traffic management logic at your gateway and your services. ## 1. Start endpoints for your services On a server designated for U.S. traffic, start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your service listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url https://us-service.internal ``` On a server designated for Canadian traffic, start a second endpoint. ```bash theme={null} ngrok http $PORT --url https://ca-service.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Add routing to your services with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. ```yaml theme={null} on_http_request: - expressions: # Check if the client is from the U.S. - "conn.geo.country_code == 'US'" actions: # Forward the request to the U.S. internal service - type: forward-internal config: url: http://us-service.internal - expressions: # Check if the client is from Canada - "conn.geo.country_code == 'CA'" actions: # Forward the request to the CA internal service - type: forward-internal config: url: http://ca-service.internal - actions: # Forward all other requests to a "default" service of your choosing - type: forward-internal config: url: http://us-service.internal ``` **What's happening here?** This policy checks every HTTP request to see where it originated from. If that's the U.S. or Canada, ngrok forwards the traffic to its respective endpoint. If the traffic comes from any other country, ngrok forwards it to a "default" service of your choosing. ## 5. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## What's next? * Explore [other routing methods](/docs/gateway/traffic-policy/examples/route-requests/), like routing by path, header, cookie, or IP intelligence. * [Add authentication](/docs/gateway/traffic-policy/examples/add-authentication/) to your regional endpoints with OAuth, OIDC, or JWT validation. * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). * Check out [Endpoint Pools](/docs/gateway/endpoints/endpoint-pooling/) for dead-simple load balancing. # Route to Services Based on OIDC Authentication Source: https://ngrok.com/docs/gateway/examples/route-by-oidc Use the data returned by your OIDC provider to route authenticated requsets to specific upstream services. With ngrok, you can use OpenID Connect to do more than authenticate requests—you can also use the data returned by your OIDC provider to route requests to specific upstream services. This pattern allows you to grant credentialed users and remote devices access to services on your internal ngrok network without maintaining a complex routing topology. Example use cases include internal management UIs or API services. ## 1. Set up your OIDC provider You can use any provider with the [`oidc` Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc/#overview). You'll need the base URL of your Open ID provider, and, in most cases, a client ID and secret. These are referred to as `$ISSUER_URL`, `$CLIENT_ID`, and `$CLIENT_SECRET`. ## 2. Start endpoints for your services Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where one of your upstream services listen. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url https://service-one.internal ``` Start a second Agent Endpoint for another service you want to route to, replacing `$OTHER_PORT` with the port for this service. ```bash theme={null} ngrok http $OTHER_PORT --url https://service-two.internal ``` Repeat the process for as many services as you need to route to. ## 3. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 4. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 5. Enforce OIDC authentication and routing with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor, replacing `$ISSUER_URL`, `$CLIENT_ID`, and `$CLIENT_SECRET` with the appropriate values. ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: "$ISSUER_URL" client_id: "$CLIENT_ID" client_secret: "$CLIENT_SECRET" scopes: - openid - expressions: - actions.ngrok.oidc.identity.name == 'Bob' actions: - type: forward-internal config: url: https://service-one.internal - expressions: - actions.ngrok.oidc.identity.name == 'Alice' actions: - type: forward-internal config: url: https://service-two.internal ``` **What's happening here?** This policy first requires OIDC authentication on every request using your issuer URL, client ID, and client secret. If you authenticate successfully, this policy then filters your request based on the name as returned from your OIDC provider and routes your request to a specific service. ## 6. Try out your endpoints Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## What's next? * Explore [other authentication methods](/docs/gateway/traffic-policy/examples/add-authentication/) like OAuth, Basic Auth, JWT validation, or SAML. * Learn about [other routing options](/docs/gateway/traffic-policy/examples/route-requests/) beyond OIDC identity, like routing by path, header, or geographic location. * Read the guides on [securing access to remote devices](/docs/gateway/device-gateway/agent/) or [site-to-site connectivity](/docs/gateway/site-to-site-connectivity/) for more comprehensive setups involving Service Users, scoped auth tokens, and mTLS. * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). # Create Secure Public Developer Environments Source: https://ngrok.com/docs/gateway/examples/secure-developer-environments Use a shared wildcard endpoint to control how your developers expose their services, route requests to the right local service, and apply centralized Traffic Policies like auth or IP restrictions. Developers often need to expose local services—whether for testing webhooks, integrating with a SaaS, or demoing in-progress features—but you don't want them poking holes in your firewall or deploying shadow IT to get the job done. ngrok lets you define exactly how your peers can create these public developer environments, then apply policies like authentication across them consistently. With this setup, you can: * Give each developer their own public URL like `bob.devtest.example.com` to route public traffic or webhooks into their local development environment. * Route requests to the right internal service using domain-based matching. * Apply consistent policies like IP restrictions or mTLS to every developer endpoint from a single gateway. ## What you'll need * A [production pay-as-you-go](/docs/pricing-limits/) account for creating [wildcard endpoints](/docs/pricing-limits/#wildcard-endpoints) ## 1. Let developers create endpoints for their services Each developer can start their own [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints) using an internal URL that's namespaced to your shared ngrok account, like `bob.internal`, replacing `$PORT` based on where their service listens. They can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url http://bob.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a [wildcard domain](/docs/gateway/domains/custom-domains/) like `*.devtest.example.com`. You'll then need to set up CNAME records with your DNS provider. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the wildcard endpoint pattern (using the domain you just reserved) to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Route traffic to developer environments with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://${req.host.split(".devtest.example.com")[0]}.internal ``` **What's happening here?** This policy dynamically forwards every request to a specific internal endpoint based on the hostname of the request. For example, if someone sends a request to `bob.devtest.example.com`, this policy extracts `bob` from the hostname string and forwards it to `bob.internal`, which in turn connects to that developer's local environment. You can extend this to any number of developer environments by having them create new Agent Endpoints on internal URLs like `https://alice.internal` and so on. ## Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## What's next? * Read the [best practices for developer productivity](/docs/gateway/security-dev-productivity/) guide for ways to improve this example with developer-specific authtokens and custom connect URLs. * Explore other examples of using the [`forward-internal`](/docs/gateway/traffic-policy/actions/forward-internal/#examples) Traffic Policy actions. # Validate Requests Against Identity Service Source: https://ngrok.com/docs/gateway/examples/validate-requests-identity Enrich your upstream service with customer details, use other authentication methods, and simplify developer experience by adding identity logic to your gateway through an HTTP request. When you use ngrok as your gateway, you can then use Traffic Policy and the [`http-request` action](/docs/gateway/traffic-policy/actions/http-request) to make requests to your other services as part of the request lifecycle and before the request ever hits your upstream service. By validating requests against an internal identity service, you can: * Add details about a customer to the request, without storing it at your gateway, to enrich your upstream service and its response. * Use an authentication method not currently supported by a Traffic Policy action, like API keys you've provisioned through your API's developer portal. * Apply ["pre-tiering"](/docs/gateway/traffic-policy/actions/rate-limit) to your customers requests based on data associated with their account at the [`rate-limit`](/docs/gateway/traffic-policy/actions/rate-limit) action. * Simplify the developer experience for your customers by not requiring they include their information as headers or in the request body. ## 1. Create endpoints for your services Start an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/), replacing `$PORT` based on where your upstream service listens. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). ```bash theme={null} ngrok http $PORT --url https://service.internal ``` Start a second endpoint for your identity service. ```bash theme={null} ngrok http $PORT --url https://id.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. Validate requests against your internal identity service with Traffic Policy While viewing your new Cloud Endpoint in the dashboard, copy the policy below and paste it into the Traffic Policy editor. You'll need to change: * `https://id.internal/api/user`: Replace the path according to your identity service's API. * `$ID_API_TOKEN`: RReplace with an API key or other authentication method generated by your identity service. ```yaml theme={null} on_http_request: - actions: - type: http-request config: url: https://id.internal/api/user method: POST headers: Authorization: "Bearer $ID_API_KEY" Content-Type: "application/json" body: | { "path": "${req.url.path}", "token": "${req.headers['Authorization']}", } timeout: 1s # If the identity server responds with a `200 OK` status code, then assume the user is authenticated and their request should continue - expressions: - "actions.ngrok.http_request.res.status_code == 200" actions: - type: forward-internal config: url: https://service.internal ``` **What's happening here?** This policy first sends an authenticated HTTP request to your internal identity service at `https://id.internal` with a body containing a few attributes injected with CEL interpolation, including the `Bearer ...` token they included as a header with their request. If the identity service responds with a `200 OK` status code, the policy assumes they were authenticated correctly and then forwards their request to your upstream service. ## Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## What's next? * See how to use your identity service's customer data to ["pre-tier" requests](/docs/gateway/examples/pre-tier-requests) based on whether a user is part of the `Bronze`, `Silver`, and `gold` tier. * Explore other examples of using the [`http-request`](/docs/gateway/traffic-policy/actions/http-request/#examples) and [`forward-internal`](/docs/gateway/traffic-policy/actions/forward-internal/#examples) Traffic Policy actions. * View your traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). # Webhook Gateway Source: https://ngrok.com/docs/gateway/examples/webhook-gateway Centralize webhook validation and routing from third-party providers like Stripe, Twilio, and Slack to secure your integrations and eliminate repetitive implementation. Instead of implementing webhook validation and routing logic separately in every service, a webhook gateway provides a single, secure entry point for all third-party webhooks from providers like Stripe, Twilio, Slack, and GitHub. This centralized approach validates webhook signatures, prevents tampering, and routes authenticated requests to the appropriate internal services in production environments. With this setup, you can: * Validate webhook signatures from 70+ supported providers in a single place * Route authenticated webhooks to appropriate services based on the provider * Prevent webhook spoofing and tampering with cryptographic verification * Standardize webhook handling across your entire infrastructure * Apply consistent logging and monitoring to all webhook traffic If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). ## 1. Create endpoints for your services Start internal [Agent Endpoints](/docs/gateway/endpoints/agent-endpoints/) for the services that will handle webhooks from different providers. You can also use one of the [SDKs](/docs/agent-sdks) or the [Kubernetes Operator](/docs/k8s). For a payment service handling Stripe webhooks: ```bash theme={null} ngrok http $PAYMENT_SERVICE_PORT --url https://payment-service.internal ``` For a notification service handling Slack webhooks: ```bash theme={null} ngrok http $NOTIFICATION_SERVICE_PORT --url https://notification-service.internal ``` For a deployment service handling GitHub webhooks: ```bash theme={null} ngrok http $DEPLOYMENT_SERVICE_PORT --url https://deployment-service.internal ``` ## 2. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 3. Create a Cloud Endpoint Navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints?sortBy=updatedAt\&orderBy=desc) of the ngrok dashboard, then click **New +** and **Cloud Endpoint**. In the **URL** field, enter the domain you just reserved to finish creating your [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/). ## 4. (Optional) Create a vault and secrets For production environments, store your webhook secrets securely using [Secrets for Traffic Policy](/docs/gateway/traffic-policy/secrets). This step is optional—you can also use plaintext secrets directly in your policy. Create a vault to store your webhook secrets: ```bash theme={null} ngrok api vaults create --name "webhook-secrets" --description "Webhook validation secrets" ``` Add your credentials to the vault, replacing the names and values, plus changing `$VAULT_ID` to match the vault ID from the response: ```bash theme={null} # Add Stripe webhook secret ngrok api secrets create \ --name "stripe-secret" \ --value "your_stripe_secret_here" \ --vault-id "vault_2yNPzuk6GjHrx3mlOCkJK42RsdR" # Add Slack webhook secret ngrok api secrets create \ --name "slack-secret" \ --value "your_slack_verification_token_here" \ --vault-id "vault_2yNPzuk6GjHrx3mlOCkJK42RsdR" # Add GitHub webhook secret ngrok api secrets create \ --name "github-secret" \ --value "your_github_webhook_secret_here" \ --vault-id "vault_2yNPzuk6GjHrx3mlOCkJK42RsdR" ``` ## 5. Apply Traffic Policy to your Cloud Endpoint While viewing your new Cloud Endpoint in the dashboard, copy one of the policies below and paste it into the Traffic Policy editor. ```yaml theme={null} on_http_request: - expressions: # Route Stripe webhooks to payment service - "req.url.path.startsWith('/stripe')" actions: - type: verify-webhook config: provider: stripe secret: "${secrets.get('webhook-secrets', 'stripe-secret')}" - type: forward-internal config: url: https://payment-service.internal - expressions: # Route Slack webhooks to notification service - "req.url.path.startsWith('/slack')" actions: - type: verify-webhook config: provider: slack secret: "${secrets.get('webhook-secrets', 'slack-secret')}" - type: forward-internal config: url: https://notification-service.internal - expressions: # Route GitHub webhooks to deployment service - "req.url.path.startsWith('/github')" actions: - type: verify-webhook config: provider: github secret: "${secrets.get('webhook-secrets', 'github-secret')}" - type: forward-internal config: url: https://deployment-service.internal ``` **What's happening here?** This policy routes incoming webhook requests based on their URL path. Each route first validates the webhook signature using secrets stored in your encrypted vault, then forwards authenticated requests to the appropriate internal service. If signature verification fails, the request is automatically rejected, protecting your services from spoofed or tampered webhooks. ```yaml theme={null} on_http_request: - expressions: # Route Stripe webhooks to payment service - "req.url.path.startsWith('/stripe')" actions: - type: verify-webhook config: provider: stripe secret: "whsec_your_stripe_secret_here" - type: forward-internal config: url: https://payment-service.internal - expressions: # Route Slack webhooks to notification service - "req.url.path.startsWith('/slack')" actions: - type: verify-webhook config: provider: slack secret: "your_slack_verification_token_here" - type: forward-internal config: url: https://notification-service.internal - expressions: # Route GitHub webhooks to deployment service - "req.url.path.startsWith('/github')" actions: - type: verify-webhook config: provider: github secret: "your_github_webhook_secret_here" - type: forward-internal config: url: https://deployment-service.internal ``` **What's happening here?** This policy routes incoming webhook requests based on their URL path. Each route first validates the webhook signature using plaintext secrets, then forwards authenticated requests to the appropriate internal service. If signature verification fails, the request is automatically rejected, protecting your services from spoofed or tampered webhooks. ## 6. Try out your endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. Configure your webhook endpoints in each provider's dashboard: * **Stripe**: `https://$NGROK_DOMAIN/stripe` * **Slack**: `https://$NGROK_DOMAIN/slack` * **GitHub**: `https://$NGROK_DOMAIN/github` When webhooks are sent from these providers, they'll be validated and routed to your internal services automatically. ## What's next? * Read about [other gateway shapes](https://ngrok.com/blog-post/api-gateway-shapes-patterns-2025) you can build with ngrok * View the complete list of [70+ supported webhook providers](/docs/gateway/traffic-policy/actions/verify-webhook/#supported-providers) including Twilio, Shopify, and DocuSign * Add the [`log` action](/docs/gateway/traffic-policy/actions/log) to send webhook events to your observability platform for monitoring and debugging * Use [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) to monitor webhook delivery and troubleshoot validation failures * Learn more about [webhook signature verification](/docs/gateway/traffic-policy/actions/verify-webhook) and security best practices # Accelerate and Secure your Wordpress Installation Source: https://ngrok.com/docs/gateway/examples/wordpress ngrok improves your existing Wordpress installation by managing your TLS certificates, accelerating your blog on the global load balancer, and protecting it from DDoS attacks. ngrok works out of the box with any up-to-date Wordpress installation where you have access to the host machine's terminal and filesystem. ngrok improves your existing Wordpress installation by: * Managing and auto-renewing your TLS certificates * Distributing and accelerating your blog on a global content delivery network * Protecting your blog from DDoS attacks with the firewall ## 1. Reserve a domain Navigate to the [**Domains** section](https://dashboard.ngrok.com/domains) of the ngrok dashboard and click **New +** to reserve a free static domain like or a [custom domain](/docs/gateway/domains/custom-domains/) you already own. We'll refer to this domain as `$NGROK_DOMAIN` from here on out. ## 2. Create a Traffic Policy file On the system where Wordpress runs, create a file named `wordpress.yaml` and paste in the following policy: ```yaml theme={null} on_http_request: - actions: - type: add-headers config: headers: host: localhost ``` **What's happening here?** This policy rewrites the `Host` header of every HTTP request to `localhost` so that Wordpress accepts the requests. ## 3. Start your Wordpress endpoint On the same system where Wordpress runs, start the agent on port `80`, which is the default for Wordpress, and referencing the `wordpress.yaml` file you just created. ```bash theme={null} ngrok http 80 --url https://your-service.ngrok.app --traffic-policy-file wordpress.yaml ``` ## 4. Try out your Wordpress endpoint Visit the domain you reserved either in the browser or in the terminal using a tool like `curl`. You should see the app or service at the port connected to your internal Agent Endpoint. ## Optional: Restrict access to the admin dashboard The WP Admin dashboard is protected by a username and password, but is often subjected to attacks. You can use the [`restrict-ips` Traffic Policy action](/docs/gateway/traffic-policy/actions/restrict-ips/) to allow only yours or other trusted IPs. Edit your `wordpress.yaml` file with the following policy, replacing `$YOUR_IP`. ```yaml theme={null} on_http_request: - expressions: - "req.url.includes('/wp-admin')" actions: - type: restrict-ips config: enforce: true allow: - $YOUR_IP/32 - actions: - type: add-headers config: headers: host: localhost ``` **What's happening here?** This policy first checks whether an HTTP request's URL contains the `/wp-admin` path. If it does, then it applies the `restrict-ips` action to allow only the addresses you explicitly added. Then, as before, the policy rewrites the `Host` header of every HTTP request to `localhost` so that Wordpress accepts the requests. Finally, restart your ngrok agent to apply the new policy. ```bash theme={null} ngrok http 80 --url https://your-service.ngrok.app --traffic-policy-file wordpress.yaml ``` ## What's next? * Read more about [Traffic Policy](/docs/traffic-policy) and its [core concepts](/docs/gateway/traffic-policy/how-it-works). * View your Wordpress traffic in [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). * Use the [`rewrite` action](/docs/gateway/traffic-policy/actions/redirect) to create ["pretty" blog URLs](/docs/gateway/traffic-policy/examples/url-rewrites/). # How does ngrok's Gateway work? Source: https://ngrok.com/docs/gateway/how-it-works Learn how ngrok's global cloud service and agent software work together to provide secure ingress to your applications. ngrok operates a global network of servers called the *ngrok cloud service* where it accepts traffic to your upstream services from clients on the internet. The URLs that it receives traffic on are your *endpoints*. You configure *modules* that ngrok uses to authenticate, transform, and accelerate that traffic as it's sent to your upstream service. Unlike traditional reverse proxies, ngrok's Gateway doesn't transmit traffic to your upstream services by forwarding to IP addresses. Instead, you run a small piece of software alongside your service called an *agent* which connects to ngrok's global service via secure, outbound persistent TLS connections. When traffic is received on your endpoints at ngrok's cloud service, it's transmitted to the agent via those TLS connections and finally from the agent to your upstream service. You can choose how to run the agent software in different form factors—it's lightweight and easy to install: 1. **As a service:** Run a small side process called the [ngrok agent](/docs/gateway/agent/) as a background OS service. 2. **As an interactive CLI:** Run the [ngrok agent](/docs/gateway/agent/) interactively from the command line while developing and testing. 3. **As an SDK embedded in your app:** Include a small [Agent SDK](/docs/agent-sdks) library directly into your application software that returns a socket-like object. 4. **As a Kubernetes Controller:** Run the [ngrok Kubernetes Operator](/docs/gateway/k8s/) in a Kubernetes environment. ## Gateway versus traditional reverse proxies ngrok's Gateway doesn't forward to IP addresses like traditional reverse proxies; instead, it sends connections to your upstream service via a lightweight piece of agent software running alongside or within your application. This unique architecture confers several important benefits over the traditional model: First, it means you can run your services *anywhere*—any cloud such as AWS or Azure, any application platform like Heroku, an on-prem data center, a Raspberry Pi in your home, or even on your laptop. Second, it allows ngrok to provide ingress with zero networking configuration. You don't need to work with arcane networking primitives like DNS, IPs, certificates, or ports—that configuration is pushed to ngrok's cloud service and it's all handled automatically for you. Third, ngrok can protect you from attacks and enforce authentication without the concern that someone could "go around" ngrok by discovering your upstream IP addresses. # Secure your applications with OAuth 2.0 Source: https://ngrok.com/docs/gateway/identity-aware-proxy/securing-with-oauth Learn how to secure your ngrok endpoints with OAuth 2.0. ngrok simplifies networking by creating secure ingress to any app, device, or service with a single command or line of code. It's an all-in-one cloud networking platform that secures, transforms, and routes traffic to services running anywhere. If you're exposing endpoints with ngrok, it's important to keep them safe and secure. One way to protect them is by using OAuth, which requires visitors to sign in to view your app or webpage. This guide will walk you through the process of securing your ngrok endpoints with OAuth 2.0. *Note: OAuth is included on the ngrok free plan up to 5 MAUs.* To secure your applications with OAuth 2.0, you need to create a Traffic Policy file and use the `--traffic-policy-file` flag: Create a file called `oauth-policy.yml`: ```yaml theme={null} on_http_request: - actions: - type: oauth config: provider: github ``` Then run your tunnel with the policy file: ``` ngrok http 80 --traffic-policy-file=oauth-policy.yml ``` By adding the OAuth flag in the example above, only users who log into GitHub can access your applications. ngrok supports various major OAuth providers such as Google, GitHub, and Microsoft, as well as any solution compatible with OAuth, including OpenID Connect and SAML. This includes Okta, Auth0, and many other identity providers. [Learn more about creating custom OAuth applications here.](/docs/integrations/oauth/google-oauth) After you add OAuth to secure your application, you may want to further restrict access to specific domains or individual users. This granularity is supported by the `--oauth-allow` flags. To restrict access by domain, use an expression to check the email domain after the OAuth action: ```yaml theme={null} on_http_request: - actions: - type: oauth config: provider: google - expressions: - "!(actions.ngrok.oauth.identity.email.endsWith('@acme.com') || actions.ngrok.oauth.identity.email.endsWith('@doe.com'))" actions: - type: deny ``` Then run: ``` ngrok http 8080 --traffic-policy-file=oauth-policy.yml ``` To restrict access by email, use an expression to check if the email is in the allowed list: ```yaml theme={null} on_http_request: - actions: - type: oauth config: provider: google - expressions: - "!(actions.ngrok.oauth.identity.email in ['john@acme.com', 'jane@doe.com'])" actions: - type: deny ``` Then run: ``` ngrok http 8080 --traffic-policy-file=oauth-policy.yml ``` Besides OAuth 2.0, ngrok offers other access controls such as Webhook Verification, Basic Authentication, IP restrictions, and Mutual TLS. Depending on what you’re building, your combination of these controls may differ. # Kubernetes Operator Changelog Source: https://ngrok.com/docs/gateway/k8s/changelog Learn about changes to ngrok's Kubernetes Operator. All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 0.22.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.21.0...ngrok-operator-0.22.0](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.21.0...ngrok-operator-0.22.0) Before upgrading, review the [Helm chart 0.23 to 0.24 upgrade guide](https://github.com/ngrok/ngrok-operator/blob/main/docs/upgrading-to-0.24.md). Moving `0.22.0-rc.2` to `0.22.0`. See the `0.22.0-rc.1` and `0.22.0-rc.2` notes below for changes. ## 0.22.0-rc.2 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.22.0-rc.1...ngrok-operator-0.22.0-rc.2](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.22.0-rc.1...ngrok-operator-0.22.0-rc.2) ### Fixed * Dual-write the legacy string form of `KubernetesOperator.status.enabledFeatures` so rolling back to the prior release doesn't crash on decode by @alex-bezek in [#865](https://github.com/ngrok/ngrok-operator/pull/865) * Downgrade `golang.ngrok.com/ngrok/v2` to `v2.1.1` to avoid an upstream connection leak in `v2.1.2+` HTTP forwarding by @alex-bezek in [#866](https://github.com/ngrok/ngrok-operator/pull/866) ## 0.22.0-rc.1 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.21.0...ngrok-operator-0.22.0-rc.1](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.21.0...ngrok-operator-0.22.0-rc.1) For upgrade steps and required manifest migrations, see the [Helm chart 0.23 to 0.24 upgrade guide](https://github.com/ngrok/ngrok-operator/blob/main/docs/upgrading-to-0.24.md). ### Breaking Changes * Migrated ingress class handling to the new `ngrok.com` API group by @alex-bezek in [#819](https://github.com/ngrok/ngrok-operator/pull/819) * Migrated finalizers to the `ngrok.com` API group, writing the legacy finalizer while dual-reading both by @alex-bezek in [#820](https://github.com/ngrok/ngrok-operator/pull/820) * Migrated internally written labels and annotations from `k8s.ngrok.com` to `ngrok.com` by @alex-bezek in [#821](https://github.com/ngrok/ngrok-operator/pull/821) * Set up passive migrations to let CloudEndpoints reference NgrokTrafficPolicy resources under the new API group by @alex-bezek in [#823](https://github.com/ngrok/ngrok-operator/pull/823) * Migrated default metadata handling from the legacy kubernetes-ingress-controller by @alex-bezek in [#838](https://github.com/ngrok/ngrok-operator/pull/838) * Cleaned up user-facing `k8s.ngrok.com` references to use the new `ngrok.com` API group by @alex-bezek in [#849](https://github.com/ngrok/ngrok-operator/pull/849) * Overhauled KubernetesOperator status: replaced phase enums with structured Conditions and added structured drain reporting by @alex-bezek in [#846](https://github.com/ngrok/ngrok-operator/pull/846) * Consolidated status conditions across CRDs by @jonstacks in [#828](https://github.com/ngrok/ngrok-operator/pull/828) * Cleaned up Domain and IPPolicy status: dropped spec-duplicated fields and the dead `Progressing` condition by @alex-bezek in [#851](https://github.com/ngrok/ngrok-operator/pull/851) * Tightened CRD field validation, added `omitempty` on optional fields, and fixed Domain schema issues by @alex-bezek in [#843](https://github.com/ngrok/ngrok-operator/pull/843) * Restricted `clientCertificateRefs` on AgentEndpoint to same-namespace secrets by @jonstacks in [#826](https://github.com/ngrok/ngrok-operator/pull/826). Reported by [@thesecguy45](https://github.com/thesecguy45). * Support `map[string]string` for CRD metadata fields (K8SOP-295) by @alex-bezek in [#858](https://github.com/ngrok/ngrok-operator/pull/858) * Updated Go and third-party dependencies by @jonstacks in [#822](https://github.com/ngrok/ngrok-operator/pull/822) ### Added * Support agent TLS termination on AgentEndpoint by @jonstacks in [#814](https://github.com/ngrok/ngrok-operator/pull/814) * Added KubernetesOperator certificate renewal and per-connection forwarder cert reload for bindings by @copilot-swe-agent in [#818](https://github.com/ngrok/ngrok-operator/pull/818) * Added top-level `status.observedGeneration` to all CRDs by @alex-bezek in [#845](https://github.com/ngrok/ngrok-operator/pull/845) * Added additional CRD status fields and validation by @alex-bezek in [#850](https://github.com/ngrok/ngrok-operator/pull/850) * Allow chainsaw tests to reserve domains via a configurable suffix by @jonstacks in [#827](https://github.com/ngrok/ngrok-operator/pull/827) ### Changed * Replaced `NgrokTrafficPolicy.status.policy` with `Ready`/`Valid` conditions and added printer columns by @alex-bezek in [#852](https://github.com/ngrok/ngrok-operator/pull/852) * Aligned IPPolicy and CloudEndpoint predicates with the common controller pattern by @alex-bezek in [#854](https://github.com/ngrok/ngrok-operator/pull/854) * Used `maps.Equal` when the map type is known at compile time by @jonstacks in [#825](https://github.com/ngrok/ngrok-operator/pull/825) ### Fixed * Skip unparseable endpoints in the BoundEndpoint poller by @jonstacks in [#813](https://github.com/ngrok/ngrok-operator/pull/813) ## 0.21.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.20.3...ngrok-operator-0.21.0](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.20.3...ngrok-operator-0.21.0) Moving `0.21.0-rc.1` to `0.21.0`. See the `0.21.0-rc.1` notes below for changes. ## 0.21.0-rc.1 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.20.3...ngrok-operator-0.21.0-rc.1](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.20.3...ngrok-operator-0.21.0-rc.1) ### Breaking Changes * Renamed `URI` field to `URL` across CRDs for consistency by @sabrina-ngrok in [#779](https://github.com/ngrok/ngrok-operator/pull/779) * Replaced panics with error returns in bindings port allocator, fixed data races and nil panics by @alex-bezek in [#801](https://github.com/ngrok/ngrok-operator/pull/801) * Overhauled RBAC across all operator deployments — hand-managed in Helm, removed controller-gen markers by @alex-bezek in [#804](https://github.com/ngrok/ngrok-operator/pull/804) ### Added * Show Ready Condition's reason and message in `-o wide` output for CRDs by @alex-bezek in [#772](https://github.com/ngrok/ngrok-operator/pull/772) * Add `description` and `metadata` annotations support for ingress and gateway by @sabrina-ngrok in [#788](https://github.com/ngrok/ngrok-operator/pull/788) ### Changed * Update Go to 1.26.1 and refresh dependencies by @jonstacks in [#785](https://github.com/ngrok/ngrok-operator/pull/785) * Update full install manifests to reference main branch by @sabrina-ngrok in [#782](https://github.com/ngrok/ngrok-operator/pull/782) ### Fixed * Fix driver sync to not log errors for non-error scenarios by @alex-bezek in [#768](https://github.com/ngrok/ngrok-operator/pull/768) * Fix "object has been modified" errors using patch for finalizers and RetryOnConflict for status updates by @alex-bezek in [#773](https://github.com/ngrok/ngrok-operator/pull/773) * Fix ProxyProtocolVersion kubebuilder enum validation to use string values by @copilot-swe-agent in [#792](https://github.com/ngrok/ngrok-operator/pull/792) * Fix KubernetesOperator controller: error swallowing, nil pointer panics, and dead code by @alex-bezek in [#797](https://github.com/ngrok/ngrok-operator/pull/797) * Fix Gateway and HTTPRoute controllers: validation logic, informer cache corruption, and cross-namespace matching by @alex-bezek in [#798](https://github.com/ngrok/ngrok-operator/pull/798) * Fix CloudEndpoint to use ReconcileStatus for IsNotFound handling by @alex-bezek in [#799](https://github.com/ngrok/ngrok-operator/pull/799) * Fix manager driver: lost status conditions, reconcile churn, and informer cache corruption by @alex-bezek in [#800](https://github.com/ngrok/ngrok-operator/pull/800) * Fix agent driver: context propagation, channel safety, and stale status conditions by @alex-bezek in [#802](https://github.com/ngrok/ngrok-operator/pull/802) * Fix misc issues: data race in ChannelHealthChecker, swallowed errors, and incorrect error messages by @alex-bezek in [#803](https://github.com/ngrok/ngrok-operator/pull/803) * Skip redundant ngrok API updates for CloudEndpoint by @jonstacks in [#806](https://github.com/ngrok/ngrok-operator/pull/806) ## 0.20.3 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.20.2...ngrok-operator-0.20.3](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.20.2...ngrok-operator-0.20.3) ### Fixed * Fixed several bugs where the operator would overwrite statuses and create resources for Ingress/Gateway objects managed by other controllers. This includes fixing HTTPRoute parent ref merging, Gateway status overwrites, and Domain CR creation for non-ngrok classes. by @alex-bezek in [#781](https://github.com/ngrok/ngrok-operator/pull/781) ## 0.20.2 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.20.1...ngrok-operator-0.20.2](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.20.1...ngrok-operator-0.20.2) ### Added * Added emission of warning logs in the operator if Notices are passed from server-side through ConnResponse by @sabrina-ngrok [#766](https://github.com/ngrok/ngrok-operator/pull/766) ### Fixed * Fixed the initial seeding of Ingress objects and other resources into the Operators internal driver respect filtering based on the `watchNamespace` value by @alex-bezek in [#771](https://github.com/ngrok/ngrok-operator/pull/771) * Fixed the operator to not apply finalizers to `HttpRoutes` for non-ngrok Gateway Classes by @jonstacks in [#770](https://github.com/ngrok/ngrok-operator/pull/770) * Fixed the Bindings Forwarder pod's k8s client to list pods across namespaces instead of just pods in the namespace its running in by @sabrina-ngrok in [#774](https://github.com/ngrok/ngrok-operator/pull/774/) ## 0.20.1 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.20.0...ngrok-operator-0.20.1](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.20.0...ngrok-operator-0.20.1) ### Fixed * fix(domains): Don't reserve domains for TCP addresses by @jonstacks in [#761](https://github.com/ngrok/ngrok-operator/pull/761) * fix(domains): Don't delete internal domains. Remove finalizer and update status to prevent wedged domains with non-zero deletion timestamps by @jonstacks in [#763](https://github.com/ngrok/ngrok-operator/pull/763) ## 0.20.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.19.1...ngrok-operator-0.20.0](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.19.1...ngrok-operator-0.20.0) ### Added * feat: support Domain `resolves_to` field by @andrew-harris-at-ngrok in [#746](https://github.com/ngrok/ngrok-operator/pull/746) * feat: Label operator created resources consistently with `k8s.ngrok.com/controller-name` and `k8s.ngrok.com/controller-namespace` labels by @jonstacks in [#741](https://github.com/ngrok/ngrok-operator/pull/741) * Add pods to bindings forwarder manager cache and new field indexer for IP-based pod lookup by @sabrina-ngrok in [#742](https://github.com/ngrok/ngrok-operator/pull/742) * Add PodIdentity to bindings proto by @sabrina-ngrok in [#743](https://github.com/ngrok/ngrok-operator/pull/743) * Get pod from cache with client IP, load pod identity with information and pass through binding connection by @sabrina-ngrok in [#749](https://github.com/ngrok/ngrok-operator/pull/749) * Allow helm uninstall to be configured to handle cleaning up api resources by @alex-bezek in [#750](https://github.com/ngrok/ngrok-operator/pull/750) ### Changed * chore(deps): Update go to 1.25.5 by @jonstacks in [#724](https://github.com/ngrok/ngrok-operator/pull/724) * Remove labels from pod identity by @sabrina-ngrok in [#748](https://github.com/ngrok/ngrok-operator/pull/748) * Remove the deprecated domain annotation on Load balancer services by @alex-bezek in [#722](https://github.com/ngrok/ngrok-operator/pull/722) * Remove the deprecated cloud endpoint domain status by @alex-bezek in [#727](https://github.com/ngrok/ngrok-operator/pull/727) * chore(devenv): Move more tools into our nix devenv by @jonstacks in [#726](https://github.com/ngrok/ngrok-operator/pull/726) * chore: Rename symbol tcpRoute -> tlsRoute by @jonstacks in [#739](https://github.com/ngrok/ngrok-operator/pull/739) ### Fixed * fix: correct namespace comparison in secretReferencedByGateway by @alex-bezek in [#745](https://github.com/ngrok/ngrok-operator/pull/745) * Don't create internal domains for ingress and gateway objects by @alex-bezek in [#744](https://github.com/ngrok/ngrok-operator/pull/744) * Make agent endpoint controller respect the watchNamespace flag by @alex-bezek in [#740](https://github.com/ngrok/ngrok-operator/pull/740) * Push events to ingress about domain conditions via driver by @alex-bezek in [#730](https://github.com/ngrok/ngrok-operator/pull/730) * Update ngrok go sdk to the latest version to get a bug fix by @alex-bezek in [#723](https://github.com/ngrok/ngrok-operator/pull/723) * Create NewTrafficPolicyFromJson deserializer checking for unknown keys and implement it in translating ingresses by @sabrina-ngrok in [#718](https://github.com/ngrok/ngrok-operator/pull/718) ## 0.19.1 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.19.0...ngrok-operator-0.19.1](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.19.0...ngrok-operator-0.19.1) ### Added * feat: Add util documentation and tests by @jonstacks in [#707](https://github.com/ngrok/ngrok-operator/pull/707) ### Fixed * Don't create domains for k8s bound endpoints by @alex-bezek in [#705](https://github.com/ngrok/ngrok-operator/pull/705) * Remove the setting the ready reason to reconciling by @alex-bezek in [#712](https://github.com/ngrok/ngrok-operator/pull/712) * fix: Created condition on existing IP Policy Rules by @jonstacks in [#702](https://github.com/ngrok/ngrok-operator/pull/702) ## 0.19.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.18.0...ngrok-operator-0.19.0](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.18.0...ngrok-operator-0.19.0) ### Added * Add status and conditions to AgentEndpoint CRD by @alex-bezek in [#673](https://github.com/ngrok/ngrok-operator/pull/673) * Configure status and conditions for Domain CRD to track provisioning process by @alex-bezek in [#678](https://github.com/ngrok/ngrok-operator/pull/678) * Add status and conditions to CloudEndpoint CRD by @alex-bezek in [#682](https://github.com/ngrok/ngrok-operator/pull/682) * Add status and conditions to IPPolicy CRD by @sabrina-ngrok in [#684](https://github.com/ngrok/ngrok-operator/pull/684) * Add status and conditions to BoundEndpoint CRD by @alex-bezek in [#688](https://github.com/ngrok/ngrok-operator/pull/688) ### Changed * Use domain ref in statuses by @alex-bezek in [#679](https://github.com/ngrok/ngrok-operator/pull/679) * Reload certs on any changes and handle symlinks to certs by @kmvachhani in [#670](https://github.com/ngrok/ngrok-operator/pull/670) * chore(deps): Update Go to 1.24.9 by @jonstacks in [#683](https://github.com/ngrok/ngrok-operator/pull/683) * chore(deps): Dependency updates by @jonstacks in [#672](https://github.com/ngrok/ngrok-operator/pull/672) * chore: Clean up test pointer functions by @jonstacks in [#690](https://github.com/ngrok/ngrok-operator/pull/690) ### Fixed * fix: Load Balancer service status not working when using default mapping strategy by @jonstacks in [#693](https://github.com/ngrok/ngrok-operator/pull/693) * fix: Agent endpoint controller watch to find endpoints based on secret changes by @alex-bezek in [#674](https://github.com/ngrok/ngrok-operator/pull/674) * fix: Wait for gateway to be deleted before continuing the test to solve race condition by @alex-bezek in [#675](https://github.com/ngrok/ngrok-operator/pull/675) * fix: Too frequent reconciles for lb-services by @jonstacks in [#694](https://github.com/ngrok/ngrok-operator/pull/694) ## 0.18.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.17.0...ngrok-operator-0.18.0](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.17.0...ngrok-operator-0.18.0) Warning: The ngrok-operator image version `0.18.0` and above include a breaking change due to the sunsetting of some ngrok platform features. Before you upgrade to this version of the image, please see [https://github.com/ngrok/ngrok-operator/discussions/654](https://github.com/ngrok/ngrok-operator/discussions/654). ### Removed * \[Breaking Change!] Remove Deprecated CRDs by @jonstacks in [#664](https://github.com/ngrok/ngrok-operator/pull/664). ### Changed * chore(deps): bump golang.org/x/oauth2 from 0.23.0 to 0.27.0 by @dependabot in [#668](https://github.com/ngrok/ngrok-operator/pull/668). * feat: Migrate to ngrok-go v2 by @jonstacks in [#667](https://github.com/ngrok/ngrok-operator/pull/667). ## 0.17.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.16.1...ngrok-operator-0.17.0](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.16.1...ngrok-operator-0.17.0) ### Added * feat: Add a reclaimPolicy to domains by @jonstacks in [#641](https://github.com/ngrok/ngrok-operator/pull/641) * Add gatewayclass to store by @jonstacks in [#650](https://github.com/ngrok/ngrok-operator/pull/650) * feat(gateway-api): Set accepted conditions on gateways & httproutes by @jonstacks in [#651](https://github.com/ngrok/ngrok-operator/pull/651) * feat(gateway-api): Add gateay status addresses by @jonstacks in [#653](https://github.com/ngrok/ngrok-operator/pull/653) * feat(gateway-api): Validate gateway listeners hostname and port by @jonstacks in [#658](https://github.com/ngrok/ngrok-operator/pull/658) * feat(cloud-endpoints): Add CloudEndpoint ID to printer column by @jonstacks in [#647](https://github.com/ngrok/ngrok-operator/pull/647) ### Changed * bake ca into image by @Megalonia in [#626](https://github.com/ngrok/ngrok-operator/pull/626) * chore(testing): Add tests for Domain Reconciler by @jonstacks in [#646](https://github.com/ngrok/ngrok-operator/pull/646) * Combine binaries by @masonj5n in [#648](https://github.com/ngrok/ngrok-operator/pull/648) * refactor: Update statuses concurrently and pre-calculate domains by @jonstacks in [#649](https://github.com/ngrok/ngrok-operator/pull/649) * perf: Enable 'use-errors-new' revive linter by @jonstacks in [#659](https://github.com/ngrok/ngrok-operator/pull/659) * chore(deprecation): Add deprecation notice to CRDs by @jonstacks in [#655](https://github.com/ngrok/ngrok-operator/pull/655). See [discussion](https://github.com/ngrok/ngrok-operator/discussions/654) for details. * chore(deps): Updated to go 1.24 by @jonstacks in [#642](https://github.com/ngrok/ngrok-operator/pull/642) and [#661](https://github.com/ngrok/ngrok-operator/pull/661). * chore: Use slices and maps from stdlib by @jonstacks in [#638](https://github.com/ngrok/ngrok-operator/pull/638) ### Fixed * Periodically re-reconcile boundendpoints and refactor connectivity check by @masonj5n in [#628](https://github.com/ngrok/ngrok-operator/pull/628). * fix: Hanging connections by @jonstacks in [#657](https://github.com/ngrok/ngrok-operator/pull/657) * fix: httproute should enqueue requests when gateways are deleted by @jonstacks in [#660](https://github.com/ngrok/ngrok-operator/pull/660) ## 0.16.1 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.16.0...ngrok-operator-0.16.1](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.16.0...ngrok-operator-0.16.1) ### Added * enable more golangci linters by @Alice-Lilith in [#630](https://github.com/ngrok/ngrok-operator/pull/630) ### Removed * remove generated bindings from ingress/gateway resources by @Alice-Lilith in [#635](https://github.com/ngrok/ngrok-operator/pull/635) ### Fixed * fix(api-manager): panic when TCP/TLS Route CRDs are not installed. by @jonstacks in [#636](https://github.com/ngrok/ngrok-operator/pull/636) ## 0.16.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.15.1...ngrok-operator-0.16.0](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.15.1...ngrok-operator-0.16.0) ### Added * Consume IngressEndpoint from status by @masonj5n in [#607](https://github.com/ngrok/ngrok-operator/pull/607) * add new mapping strategy by @Alice-Lilith in [#612](https://github.com/ngrok/ngrok-operator/pull/612) * add support for TCPRoute & TLSRoute by @Alice-Lilith in [#621](https://github.com/ngrok/ngrok-operator/pull/621) * Lb services reserve tcp addr for endpoints by @jonstacks in [#622](https://github.com/ngrok/ngrok-operator/pull/622) ### Changed * unconditionally create operator with ngrok api by @masonj5n in [#614](https://github.com/ngrok/ngrok-operator/pull/614) * only make tunnels for edges strategy by @Alice-Lilith in [#623](https://github.com/ngrok/ngrok-operator/pull/623) ### Fixed * add deployment name and version to \_update calls by @Megalonia in [#610](https://github.com/ngrok/ngrok-operator/pull/610) * fix: Agent endpoint domain strategy by @jonstacks in [#613](https://github.com/ngrok/ngrok-operator/pull/613) * fix: Empty TLS cert on creation when bindings are enabled by @jonstacks in [#615](https://github.com/ngrok/ngrok-operator/pull/615) * fix: traffic-policy annotation not working when using mapping-strategy of edges by @jonstacks in [#616](https://github.com/ngrok/ngrok-operator/pull/616) * set operator description from values.yaml description by @masonj5n in [#605](https://github.com/ngrok/ngrok-operator/pull/605) * fix: Retry cloud endpoint creation on pooling state errors by @jonstacks in [#624](https://github.com/ngrok/ngrok-operator/pull/624) ## 0.15.1 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.15.0...ngrok-operator-0.15.1](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.15.0...ngrok-operator-0.15.1) ### Changed * chore(deps): Upgrade go to 1.24.1 by @jonstacks in [#601](https://github.com/ngrok/ngrok-operator/pull/601) * chore(lint): Enable go-critic by @jonstacks in [#604](https://github.com/ngrok/ngrok-operator/pull/604) ### Fixed * fix: HTTP/2 upstreams for edges & endpoints by @jonstacks in [#606](https://github.com/ngrok/ngrok-operator/pull/606). Fixes [#572](https://github.com/ngrok/ngrok-operator/issues/572). * fix: add support for https agent endpoints from ingress/gwapi by @Alice-Lilith in [#599](https://github.com/ngrok/ngrok-operator/pull/599) ## 0.15.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.14.3...ngrok-operator-0.15.0](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.14.3...ngrok-operator-0.15.0) ### Added * Gateway API to Endpoints conversion by @Alice-Lilith in [#588](https://github.com/ngrok/ngrok-operator/pull/588) * Gateway API ReferenceGrant support (endpoints only) by @Alice-Lilith in [#589](https://github.com/ngrok/ngrok-operator/pull/589). When using the Gateway API configuration and translating to endpoints, ReferenceGrants will be required for cross-namespace references as dictated by Gateway API. You may opt-out of support for ReferenceGrants and always allow cross namespace references using the `--disable-reference-grants` in the pod arguments or using the helm value `gateway.disableReferenceGrants`. * Support for the `k8s.ngrok.com/bindings` annotation (endpoints only) to set bindings on endpoints generated from Ingresses and Gateway API config by @Alice-Lilith in [#593](https://github.com/ngrok/ngrok-operator/pull/593). * Implemented support for configuring upstream client certificates in the `AgentEndpoint` respirces and Gateway API Gateway Backend TLS config to configure client certificates to be sent to upstream services (endpoints only) bby @Alice-Lilith in [#594](https://github.com/ngrok/ngrok-operator/pull/594) ### Changed * Endpoints are now the default for translations from `Ingress`, LoadBalancer Service, and Gateway API config by @Alice-Lilith in [#596](https://github.com/ngrok/ngrok-operator/pull/596) The `k8s.ngrok.com/mapping-strategy: endpoints` annotation is no longer required for endpoints conversion. Similarly, you may still use Edges instead with the `k8s.ngrok.com/mapping-strategy: edges` annotation, but Edges will be removed in a future release. * Auto enable Gateway API support by @Alice-Lilith in [#592](https://github.com/ngrok/ngrok-operator/pull/592). Instead of needing to opt-in to Gateway API support, we now enable it by default if the Gateway API CRDs are detected. You may still opt-out using the `enable-feature-gateway` flag on the pod arguments or using the helm value `gateway.enabled`. * Replaced binding name printcolumn with endpoint selectors for KubernetesOperator resources by @masonj5n in [#586](https://github.com/ngrok/ngrok-operator/pull/586) * Updated notes.txt to reflect bindings API changes by @masonj5n in [#585](https://github.com/ngrok/ngrok-operator/pull/585) * Support for latest bindings configuration (endpoints only) with the `k8s.ngrok.com/bindings` annotation to set bindings on endpoints generated from LoadBalancer Service resoruces by @Megalonia in [#590](https://github.com/ngrok/ngrok-operator/pull/590) ## 0.14.3 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.14.2...ngrok-operator-0.14.3](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.14.2...ngrok-operator-0.14.3) ### Added * Endpoint pooling SDK support and auto pooling for `AgentEndpoint` resources by @jonstacks in [#581](https://github.com/ngrok/ngrok-operator/pull/581) * Endpoint pooling support for `CloudEndpoint` resources (default=false), also supported on `Ingress`/`Service` resources that create endpoints using the `"k8s.ngrok.com/mapping-strategy": "endpoints"` annotation when `"k8s.ngrok.com/pooling-enabled": "true"` annotation is supplied by @Alice-Lilith in [#582](https://github.com/ngrok/ngrok-operator/pull/582) ## 0.14.2 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.14.1...ngrok-operator-0.14.2](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.14.1...ngrok-operator-0.14.2) ### Added * Add conversion support from ingress to endpoints by @Alice-Lilith in [#562](https://github.com/ngrok/ngrok-operator/pull/562) * feat: Add trafficpolicy package and conversion util by @jonstacks in [#564](https://github.com/ngrok/ngrok-operator/pull/564) * feat: Copy domain status to cloud endpoint status by @jonstacks in [#566](https://github.com/ngrok/ngrok-operator/pull/566) * feat: Opt-in to endpoints for Load balancer Services by @jonstacks in [#568](https://github.com/ngrok/ngrok-operator/pull/568) * feat(ci): Use codecov for coverage reports by @jonstacks in [#571](https://github.com/ngrok/ngrok-operator/pull/571) ### Changed * chore(deps): Update ngrok-api-go by @jonstacks in [#560](https://github.com/ngrok/ngrok-operator/pull/560) * Change `allowed_urls` to `endpoint_selectors` by @masonj5n in [#573](https://github.com/ngrok/ngrok-operator/pull/573) * chore(ci): Make codecov patch status informational for now as well by @jonstacks in [#577](https://github.com/ngrok/ngrok-operator/pull/577) * update use endpoints annotation by @Alice-Lilith in [#579](https://github.com/ngrok/ngrok-operator/pull/579) ### Fixed * fix(service-controller): Service controller uses configured cluster domain by @jonstacks in [#552](https://github.com/ngrok/ngrok-operator/pull/552) * fix(ngrok-api-go): Update to client that doesn't panic for get\_bound\_endpoints by @jonstacks in [#561](https://github.com/ngrok/ngrok-operator/pull/561) * fix: managerdriver tests not being run by @jonstacks in [#569](https://github.com/ngrok/ngrok-operator/pull/569) * fix(ci): Disable bindings for e2e tests by @jonstacks in [#570](https://github.com/ngrok/ngrok-operator/pull/570) * add newly created agent endpoints to the map by @Alice-Lilith in [#574](https://github.com/ngrok/ngrok-operator/pull/574) * fix(httpsedges): HTTPS Edges should retry on hostport already in use by @jonstacks in [#576](https://github.com/ngrok/ngrok-operator/pull/576) ### Removed * Remove binding name by @masonj5n in [#567](https://github.com/ngrok/ngrok-operator/pull/567) ## 0.14.1 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.14.0...ngrok-operator-0.14.1](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.14.0...ngrok-operator-0.14.1) ### Fixed * Fix http endpoint scheme by @jonstacks in [#549](https://github.com/ngrok/ngrok-operator/pull/549) ## 0.14.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.7...ngrok-operator-0.14.0](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.7...ngrok-operator-0.14.0) * Update ngrok-operator version to `0.14.0` * Update Helm chart version to `0.17.0-rc.1` ### Added * add agentendpoint crd by @Alice-Lilith in [#525](https://github.com/ngrok/ngrok-operator/pull/525) * agent endpoints work continued by @Alice-Lilith in [#538](https://github.com/ngrok/ngrok-operator/pull/538) * make protocol optional for agentendpoint upstreams by @Alice-Lilith in [#547](https://github.com/ngrok/ngrok-operator/pull/547) * remove boilerplate type field from trafficPolicy field by @Alice-Lilith in [#548](https://github.com/ngrok/ngrok-operator/pull/548) ### Changed * Error with invalid API key by @hjkatz in [#524](https://github.com/ngrok/ngrok-operator/pull/524) ### Fixed * fix: Re-create tunnel if forwardsTo or appProto changes by @jonstacks in [#527](https://github.com/ngrok/ngrok-operator/pull/527) * adjust bindings-forwarder deployment template by @masonj5n in [#529](https://github.com/ngrok/ngrok-operator/pull/529) * skip no-op status and annotation updates for boundendpoint reconciliation by @masonj5n in [#537](https://github.com/ngrok/ngrok-operator/pull/537) * fix endpoint url validation helper and add tests by @Alice-Lilith in [#544](https://github.com/ngrok/ngrok-operator/pull/544) * fix(agent-endpoints): Delete agent endpoint instead of tunnel by @jonstacks in [#543](https://github.com/ngrok/ngrok-operator/pull/543) ### Internal / CI * Add artifacthub badge by @hjkatz in [#513](https://github.com/ngrok/ngrok-operator/pull/513) * feat: add chainsaw based e2e tests by @eddycharly in [#506](https://github.com/ngrok/ngrok-operator/pull/506) * e2e updates / fixes 1 by @hjkatz in [#526](https://github.com/ngrok/ngrok-operator/pull/526) * Trigger ci e2e with Makefile change by @hjkatz in [#528](https://github.com/ngrok/ngrok-operator/pull/528) * Use correct namespace for debugging by @hjkatz in [#530](https://github.com/ngrok/ngrok-operator/pull/530) * Ensure build-and-test runs on push events by @hjkatz in [#531](https://github.com/ngrok/ngrok-operator/pull/531) * E2E 5, E5E by @hjkatz in [#532](https://github.com/ngrok/ngrok-operator/pull/532) * Fix typo for changes to tests ; Add scripts/e2e.sh too by @hjkatz in [#534](https://github.com/ngrok/ngrok-operator/pull/534) * Checkout fork PR HEAD for e2e tests by @hjkatz in [#535](https://github.com/ngrok/ngrok-operator/pull/535) * Enable deny gate for 'safe to test' label by @hjkatz in [#539](https://github.com/ngrok/ngrok-operator/pull/539) * Add found labels debug message by @hjkatz in [#540](https://github.com/ngrok/ngrok-operator/pull/540) * feat: Use a merge group for e2e tests by @jonstacks in [#542](https://github.com/ngrok/ngrok-operator/pull/542) * Add some e2e tests as a feature branch by @hjkatz in [#533](https://github.com/ngrok/ngrok-operator/pull/533) * feat(ci): Update release script by @jonstacks in [#545](https://github.com/ngrok/ngrok-operator/pull/545) ### New Contributors * @eddycharly made their first contribution in [https://github.com/ngrok/ngrok-operator/pull/506](https://github.com/ngrok/ngrok-operator/pull/506) * @masonj5n made their first contribution in [https://github.com/ngrok/ngrok-operator/pull/529](https://github.com/ngrok/ngrok-operator/pull/529) * @Alice-Lilith made their first contribution in [https://github.com/ngrok/ngrok-operator/pull/525](https://github.com/ngrok/ngrok-operator/pull/525) ## 0.13.7 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.6...ngrok-operator-0.13.7](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.6...ngrok-operator-0.13.7) * Update ngrok-operator version to `0.13.7` * Update Helm chart version to `0.16.4` ### Fixed * Use GPG Key name instead of ID ## 0.13.6 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.5...ngrok-operator-0.13.6](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.5...ngrok-operator-0.13.6) * Update ngrok-operator version to `0.13.6` * Update Helm chart version to `0.16.3` ### Changed * Updated GPG Key ## 0.13.5 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.4...ngrok-operator-0.13.5](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.4...ngrok-operator-0.13.5) * Update ngrok-operator version to `0.13.5` * Update Helm chart version to `0.16.2` ### Added * Sign ngrok-operator Helm chart with GPG key by @hjkatz in [#514](https://github.com/ngrok/ngrok-operator/pull/514) ### Fixed * Update README.md with new rename by @hjkatz in [#516](https://github.com/ngrok/ngrok-operator/pull/516) ## 0.13.4 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.3...ngrok-operator-0.13.4](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.3...ngrok-operator-0.13.4) * Update ngrok-operator version to `0.13.4` * Update Helm chart version to `0.16.1` ### Added * Add `scripts/release.sh` and `make release` by @hjkatz in [#507](https://github.com/ngrok/ngrok-operator/pull/507) [#509](https://github.com/ngrok/ngrok-operator/pull/509) [#510](https://github.com/ngrok/ngrok-operator/pull/510) ## 0.13.3 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.1...ngrok-operator-0.13.3](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.1...ngrok-operator-0.13.3) ### Added * Add support for 1-click demo mode by @hjkatz in [#503](https://github.com/ngrok/ngrok-operator/pull/503) * Enable automatic Helm releases for `ngrok/ngrok-operator` in `.github/workflows` by @hjkatz in (this PR) ### Fixed * Hide `kind: KubernetesOperator` API registration behind the `bindings.enable` feature flag by @hjkatz in [#504](https://github.com/ngrok/ngrok-operator/pull/504) ## 0.13.2 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.1...ngrok-operator-0.13.2](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.1...ngrok-operator-0.13.2) ### Added * Support allowedURLs by @hjkatz in [#496](https://github.com/ngrok/ngrok-operator/pull/496) ### Fixed * fix: Clear status and re-reconcile if httpsedge is not found by @jonstacks in [#501](https://github.com/ngrok/ngrok-operator/pull/501) * Use the previously ingress in the error messages by @alex-bezek in [#500](https://github.com/ngrok/ngrok-operator/pull/500) ## 0.13.1 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.0...ngrok-operator-0.13.1](https://github.com/ngrok/ngrok-operator/compare/ngrok-operator-0.13.0...ngrok-operator-0.13.1) ### Changed * Use goroutine instead of errGroup by @hjkatz in [#497](https://github.com/ngrok/ngrok-operator/pull/497) * Reduce polling interval to 10 seconds by @hjkatz in [#491](https://github.com/ngrok/ngrok-operator/pull/491) ### Fixed * fix: domain stuck when ID is not found by @jonstacks in [#488](https://github.com/ngrok/ngrok-operator/pull/488) * Ensure the TLS secret is valid otherwise upsert by @hjkatz in [#486](https://github.com/ngrok/ngrok-operator/pull/486) * Use unique context for endpoint poller reconcile actions by @hjkatz in [#489](https://github.com/ngrok/ngrok-operator/pull/489) * fix: Make sure we update the status by @jonstacks in [#493](https://github.com/ngrok/ngrok-operator/pull/493) * Add more logging for binding forwarder mux handshake by @hjkatz in [#494](https://github.com/ngrok/ngrok-operator/pull/494) * fix: Better migration path from the ngrok kuberntes-ingress-controller to the ngrok-operator by @jonstacks in [#495](https://github.com/ngrok/ngrok-operator/pull/495) ## 0.13.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/kubernetes-ingress-controller-0.12.2...ngrok-operator-0.13.0](https://github.com/ngrok/ngrok-operator/compare/kubernetes-ingress-controller-0.12.2...ngrok-operator-0.13.0) ### :warning: :warning: :warning: Notice :warning: :warning: :warning: This version of the controller is not backwards compatible with previous versions and is only compatible with version 0.16.0 of the `ngrok/ngrok-operator` helm chart and later. Using this version or later of the controller with the `ngrok/kubernetes-ingress-controller` helm chart will result in the controller not functioning correctly. Even though we are in major version 0, and semver v2.0.0 allows that anything may change until a 1.0.0 release, we try not to break backwards compatibility. However, this change is necessary to support new features and improvements in the operator. ### Added #### Kubernetes Operator The operator installation will now be registered with the ngrok API. This will allow you to view the status of the operator in the ngrok dashboard, see what version of the operator is running, and power new features in the future. This is powered by a new `KubernetesOperator` CRD that is created by the operator in its own namespace when it starts up. * Register operator by @jonstacks in [#457](https://github.com/ngrok/ngrok-operator/pull/457) * Add status to KubernetesOperator by @hjkatz in [#467](https://github.com/ngrok/ngrok-operator/pull/467) * fix: Add nil checks to prevent potential panics by @jonstacks in [#483](https://github.com/ngrok/ngrok-operator/pull/483) #### Endpoint Bindings (private beta) Endpoint bindings is a new feature that allows you to securely access a ngrok endpoint no matter where it is running. Specifically, Kubernetes bound endpoints allow you to project services running outside of your Kubernetes cluster or in other clusters into your cluster as native Kubernetes services. * Add feature flag support for bindings by @hjkatz in [#424](https://github.com/ngrok/ngrok-operator/pull/424) * feat: Initial bindings driver by @stacks in [#450](https://github.com/ngrok/ngrok-operator/pull/450) * Modify EndpointBinding CRD to reflect cardinality of bound Endpoints by @hjkatz in [#452](https://github.com/ngrok/ngrok-operator/pull/452) * Implement AggregateBindingEndpoints for interacting with the ngrok api by @hjkatz in [#453](https://github.com/ngrok/ngrok-operator/pull/453) * Implement BindingEndpoint polling by @hjkatz in [#458](https://github.com/ngrok/ngrok-operator/pull/458) * Implement EndpointBinding -> Services creation by @hjkatz in [#459](https://github.com/ngrok/ngrok-operator/pull/459) * Implement port allocation by @hjkatz in [#460](https://github.com/ngrok/ngrok-operator/pull/460) * Bindings forwarder by @jonstacks in [#465](https://github.com/ngrok/ngrok-operator/pull/465) * Add endpoint status to EndpointBinding kubectl output by @hjkatz in [#464](https://github.com/ngrok/ngrok-operator/pull/464) * chore: Update ngrok-api-go to pull in new changes by @jonstacks in [#468](https://github.com/ngrok/ngrok-operator/pull/468) * Ensure endpoint poller does not start until k8sop is regestered with API by @hjkatz in [#470](https://github.com/ngrok/ngrok-operator/pull/470) * Rename EndpointBinding to BoundEndpoint by @hjkatz in [#475](https://github.com/ngrok/ngrok-operator/pull/475) * Implement Target Metadata by @hjkatz in [#477](https://github.com/ngrok/ngrok-operator/pull/477) * Bindings forwarder implementation by @jonstacks in [#476](https://github.com/ngrok/ngrok-operator/pull/476) * Ensure KubernetesOperator.Status.EnabledFeatures is set properly from the API by @hjkatz in [#480](https://github.com/ngrok/ngrok-operator/pull/480) * Add equality tests for Target.Metadata by @hjkatz in [#482](https://github.com/ngrok/ngrok-operator/pull/482) * feat: BoundEndpointPoller polls from the API by @jonstacks in [#481](https://github.com/ngrok/ngrok-operator/pull/481) #### Cloud Endpoints (private beta) [Cloud Endpoints](https://ngrok.com/docs/network-edge/cloud-endpoints/) can now be created and managed by the operator via a new `CloudEndpoint` CRD. * Allow configuring ngrok Cloud Endpoints using CRDs by @alex-bezek in [#471](https://github.com/ngrok/ngrok-operator/pull/471) ### Changed #### Ingress/Gateway * Seed additional types when first starting by @alex-bezek in [#431](https://github.com/ngrok/ngrok-operator/pull/431). #### Traffic Policy Updates `TrafficPolicy` CRD and inline policy to support new phase-based names as well as the new `TrafficPolicy` API. * update traffic policy for phase-based naming by @TheConcierge in [#456](https://github.com/ngrok/ngrok-operator/pull/456) #### Splitting controllers into multiple manager instances The controllers have been split into multiple manager instances to improve performance and scalability. This now allows the ngrok agent manager which handles traffic to run independently of the API managers which reconcile CRDs with the ngrok API. This change also allows for more fine-grained control over the controllers and their resources. * refactor: Split the agent and API controllers by @jonstacks in [#446](https://github.com/ngrok/ngrok-operator/pull/446) ### Fixes #### Gateway API * fix: Add `GatewayClass` controller by @jonstacks in [#484](https://github.com/ngrok/ngrok-operator/pull/484) ### Documentation * Update README.md to use ngrok Kubernetes Operator instead of ingress controller. by @stmcallister in [#433](https://github.com/ngrok/ngrok-operator/pull/433) ### New Contributors * @TheConcierge made their first contribution in [#456](https://github.com/ngrok/ngrok-operator/pull/456) ## 0.12.2 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/kubernetes-ingress-controller-0.12.1...kubernetes-ingress-controller-0.12.2](https://github.com/ngrok/ngrok-operator/compare/kubernetes-ingress-controller-0.12.1...kubernetes-ingress-controller-0.12.2) ### Added * feat: Ability to specify cluster domain [#339](https://github.com/ngrok/ngrok-operator/pull/339). Thank you, @fr6nco ! * feat: Support for wildcard domains [#412](https://github.com/ngrok/ngrok-operator/pull/412) ### Changed * chore: Clean up predicate filters [#409](https://github.com/ngrok/ngrok-operator/pull/409) * refactor: Easier to read driver seed [#411](https://github.com/ngrok/ngrok-operator/pull/411) ### Fixed * fix(store): Multiple ingress rules per ingress not working [#413](https://github.com/ngrok/ngrok-operator/pull/413) ## 0.12.1 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/kubernetes-ingress-controller-0.12.0...kubernetes-ingress-controller-0.12.1](https://github.com/ngrok/ngrok-operator/compare/kubernetes-ingress-controller-0.12.0...kubernetes-ingress-controller-0.12.1) ### Fixed * fix(service-controller): Updates not working [#406](https://github.com/ngrok/ngrok-operator/pull/406) * fix: Deleting ngrok LoadBalancer services hanging [#404](https://github.com/ngrok/ngrok-operator/pull/404) ## 0.12.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/kubernetes-ingress-controller-0.11.0...kubernetes-ingress-controller-0.12.0](https://github.com/ngrok/ngrok-operator/compare/kubernetes-ingress-controller-0.11.0...kubernetes-ingress-controller-0.12.0) ### Added * feat: Auto-provision domain for TLS Edges [#386](https://github.com/ngrok/ngrok-operator/pull/386) * feat: Support for Load Balancer services [#387](https://github.com/ngrok/ngrok-operator/pull/387) * feat: Support TLS termination in modulesets for Load Balancer Services [#388](https://github.com/ngrok/ngrok-operator/pull/388) ### Changed * Switching over README to Operator [#351](https://github.com/ngrok/ngrok-operator/pull/351) * chore: Remove custom code for non leader-elected controllers [#383](https://github.com/ngrok/ngrok-operator/pull/383) * refactor: annotations parsers to handle client.Object instead of just networking.Ingress by [#384](https://github.com/ngrok/ngrok-operator/pull/384) * chore: Turn on golangci-lint [#385](https://github.com/ngrok/ngrok-operator/pull/385) ### Fixed * fix: TLSEdge not reconciling changes to hostports [#390](https://github.com/ngrok/ngrok-operator/pull/390) * assign tunnel group lable by httproute namespace [#393](https://github.com/ngrok/ngrok-operator/pull/393) ## 0.11.0 **Full Changelog**: [https://github.com/ngrok/ngrok-operator/compare/kubernetes-ingress-controller-0.10.4...kubernetes-ingress-controller-0.11.0](https://github.com/ngrok/ngrok-operator/compare/kubernetes-ingress-controller-0.10.4...kubernetes-ingress-controller-0.11.0) ### Added * create policy kind [#361](https://github.com/ngrok/ngrok-operator/pull/361) * initial policy controller update [#364](https://github.com/ngrok/ngrok-operator/pull/364) * root-cas setting [#371](https://github.com/ngrok/ngrok-operator/pull/371) Takes an install option for --set rootCAs=host and plumb the isHostCA check into the caCerts for it to just get the host certs. * feat: Add support for mutualTLS [#373](https://github.com/ngrok/ngrok-operator/pull/373) * Add GatewayClass to cachestore [#376](https://github.com/ngrok/ngrok-operator/pull/376) * Add extensionRef support for policy crd inclusion [#377](https://github.com/ngrok/ngrok-operator/pull/377) ### Changed * ngrok client api update [#367](https://github.com/ngrok/ngrok-operator/pull/367) * switch edge kinds to raw json policy [#368](https://github.com/ngrok/ngrok-operator/pull/368) * modules to traffic policy [#370](https://github.com/ngrok/ngrok-operator/pull/370) * Update nix flake, go version, and Makefile dep versions [#379](https://github.com/ngrok/ngrok-operator/pull/379) ### Fixes * fix: panics in oauth providers [#374](https://github.com/ngrok/ngrok-operator/pull/374) * Handle non-existent backend IDs more gracefully [#380](https://github.com/ngrok/ngrok-operator/pull/380) * Fixes not all reserved addrs being returned while iterating [#381](https://github.com/ngrok/ngrok-operator/pull/381) ## 0.10.4 ### Added * Add the `--api-url` option This can be used to set the endpoint for the ngrok API. It can be set through via the helm `apiURL` value. * Set metadata for edges created by the gateway * Add gateway to client info comment ### Changed * Controller will now start without having session established. Any operations that require tunnels will return error, while it is trying to create a session. Its ready and health checks now depend on the status of this session - `ready` will not return `ok` until connection was established, and `health` check will return error if this connection had authentication issues. ### Fixed * Search for backend service using the `HTTPRoute` namepace ## 0.10.3 ### Added * Support for Gateway api ## 0.10.2 ### Added * Support for [Traffic Policies](https://ngrok.com/docs/http/traffic-policy/) [#334](https://github.com/ngrok/ngrok-operator/pull/334) * Support for [Application protocol](https://kubernetes.io/docs/concepts/services-networking/service/#application-protocol) on target services to support HTTP/2. [#323](https://github.com/ngrok/ngrok-operator/pull/323) ### Fixed * The `Status.LoadBalancer[].Hostname` field is now propagated from `Domain` CNAME status updates. [#342](https://github.com/ngrok/ngrok-operator/pull/342) ## 0.10.1 ### Fixed * IPPolicy controller wasn't applying the attached rules, leaving the IP policy in its current state [#315](https://github.com/ngrok/ngrok-operator/pull/315) ## 0.10.0 ### Added * TLSEdge CRD, see the [TCP and TLS Edges Guide](https://github.com/ngrok/ngrok-operator/blob/main/docs/user-guide/tcp-tls-edges.md) for more details. ### Fixed * Added support for TLS Renegotiation for backends that use it [#314](https://github.com/ngrok/ngrok-operator/pull/314) ## 0.9.1 ### Fixed * Send FQDN in SNI when using backend https [#304](https://github.com/ngrok/ngrok-operator/pull/304) ## 0.9.0 ### Changed * Update ngrok-go to 1.4.0 [#298](https://github.com/ngrok/ngrok-operator/pull/298) * Tunnels are now unique in their respective namespace, not across the cluster [#281](https://github.com/ngrok/ngrok-operator/pull/281) * The CRs that ingress controller creates are uniquely marked and managed by it. Other CRs created manually are no longer deleted when the ingress controller is not using them [#267](https://github.com/ngrok/ngrok-operator/issues/267); fixed for tunnel in [#285](https://github.com/ngrok/ngrok-operator/pull/285) and for https edges in [#286](https://github.com/ngrok/ngrok-operator/pull/286) * Better error handling and retry, specifically for the case where we try to create an https edge for a domain which is not created yet [#283](https://github.com/ngrok/ngrok-operator/issues/283); fixed in [#288](https://github.com/ngrok/ngrok-operator/pull/288) * Watch and apply ngrok module set CR changes [#287](https://github.com/ngrok/ngrok-operator/issues/287); fixed in [#290](https://github.com/ngrok/ngrok-operator/pull/290) * Label https edges and tunnels with service UID to make them more unique within ngrok [#291](https://github.com/ngrok/ngrok-operator/issues/291); fixed in [#293](https://github.com/ngrok/ngrok-operator/pull/293) and [#302](https://github.com/ngrok/ngrok-operator/pull/302) ### Fixed * The controller stopping at the first resource create [#270](https://github.com/ngrok/ngrok-operator/pull/270) * Using `make deploy` now requires `NGROK_AUTHTOKEN` and `NGROK_API_KEY` to be set [#292](https://github.com/ngrok/ngrok-operator/pull/292) ## 0.8.1 ### Fixed * Handle special case for changing auth types that causes an error during state transition [#259](https://github.com/ngrok/ngrok-operator/pull/259) * Handle IP Policy CRD state transitions in a safer way [#260](https://github.com/ngrok/ngrok-operator/pull/260) * Better handling when changing pathType between 'Exact' and 'Prefix' [#262](https://github.com/ngrok/ngrok-operator/pull/262) ## 0.8.0 ### Changed * tunneldriver: plumb the version through ngrok-go [#228](https://github.com/ngrok/ngrok-operator/pull/228) * Support HTTPS backends via service annotation [#238](https://github.com/ngrok/ngrok-operator/pull/238) ### Fixed * Initialize route backends after module updates [#243](https://github.com/ngrok/ngrok-operator/pull/243) * validate ip restriction rules, before creating the route [#241](https://github.com/ngrok/ngrok-operator/pull/241) * Don't shadow remoteIPPolicies [#230](https://github.com/ngrok/ngrok-operator/pull/230) * resolve some linter warnings [#229](https://github.com/ngrok/ngrok-operator/pull/229) ### Documentation * Use direnv layout feature [#248](https://github.com/ngrok/ngrok-operator/pull/248) * chore(readme): improve structure and content [#246](https://github.com/ngrok/ngrok-operator/pull/246) * Added direnv and a nix devshell [#227](https://github.com/ngrok/ngrok-operator/pull/227) ### Testing Improvements * fix route modules, using ngrokmoduleset instead [#239](https://github.com/ngrok/ngrok-operator/pull/239) * Use raw yq output, split e2e runner from deployment [#235](https://github.com/ngrok/ngrok-operator/pull/235) * Added e2e config init script [#234](https://github.com/ngrok/ngrok-operator/pull/234) * Some updates to handle different cases for e2e run [#226](https://github.com/ngrok/ngrok-operator/pull/226). ## 0.7.0 ### Changed * Don't log errors on normal connection closing [#206](https://github.com/ngrok/ngrok-operator/pull/206). * Updated `golang.org/x/net` to `0.9.0` [#215](https://github.com/ngrok/ngrok-operator/pull/215). ### Fixed * Add support for named service ports [#222](https://github.com/ngrok/ngrok-operator/pull/222). ## 0.6.0 ### Changed * Added Ingress controller version to user-agent [#198](https://github.com/ngrok/ngrok-operator/pull/198). * Don't default to development mode for logging [#199](https://github.com/ngrok/ngrok-operator/pull/199). ### Fixed * Leaking TCP connections for every tunnel dial [#203](https://github.com/ngrok/ngrok-operator/pull/203). ## 0.5.0 ### Changed * Bumped go version to 1.20 [#167](https://github.com/ngrok/ngrok-operator/pull/167) * Refactored Route Module Updates to be lazy [#168](https://github.com/ngrok/ngrok-operator/pull/168) * Annotations for configuration have been removed in favor of grouping module configurations together in `NgrokModuleSet` custom resources [#170](https://github.com/ngrok/ngrok-operator/pull/170) ### Added * Ran go mod tidy and added check to make sure its tidy before merge [#166](https://github.com/ngrok/ngrok-operator/pull/166) * Added `NgrokModuleSet` CRD [#170](https://github.com/ngrok/ngrok-operator/pull/170) * Added support for Circuit Breaker route module [#171](https://github.com/ngrok/ngrok-operator/pull/171) * Added support for OIDC route module [#173](https://github.com/ngrok/ngrok-operator/pull/173) * Added support for SAML route module [#186](https://github.com/ngrok/ngrok-operator/pull/186) * Added support for OAuth route module [#192](https://github.com/ngrok/ngrok-operator/pull/192) ## 0.4.0 ### Changed * When no region override is passed to helm, the controller now does not default to the US and instead uses the closes geographic edge servers [#160](https://github.com/ngrok/ngrok-operator/pull/160) * Ingress Class has Default set to false [#109](https://github.com/ngrok/ngrok-operator/pull/109) ### Added * Allow controller name to be configured to support multiple ngrok ingress classes [#159](https://github.com/ngrok/ngrok-operator/pull/159) * Allow the controller to be configured to only watch a single namespace [#157](https://github.com/ngrok/ngrok-operator/pull/157) * Pass key/value pairs to helm that get added as json string metadata in ngrok api resources [#156](https://github.com/ngrok/ngrok-operator/pull/156) * merge all ingress objects into a single store to derive Edges. [#129](https://github.com/ngrok/ngrok-operator/pull/129), [#10](https://github.com/ngrok/ngrok-operator/pull/10), [#131](https://github.com/ngrok/ngrok-operator/pull/131), [#137](https://github.com/ngrok/ngrok-operator/pull/137) * Minimum TLS Version Route Module [#125](https://github.com/ngrok/ngrok-operator/pull/125) * Webhook Verification Route Module [#122](https://github.com/ngrok/ngrok-operator/pull/122) * Add/Remove Header Route Module [#121](https://github.com/ngrok/ngrok-operator/pull/121) * Add IP Policy CRD and IP Policy Route Module [#120](https://github.com/ngrok/ngrok-operator/pull/120) * Load certs from the directory `"/etc/ssl/certs/ngrok/"` for ngrok-go if present [#111](https://github.com/ngrok/ngrok-operator/pull/111) ### Fixed * Fix bug from Driver and Store refactor so ingress status has CNAME Targets for custom domains updated correctly [#162](https://github.com/ngrok/ngrok-operator/pull/162) * Reduce domain controller reconcile counts by not updating domains if they didn't change [#140](https://github.com/ngrok/ngrok-operator/pull/140) * Remove routes from remote API when they are removed from the ingress object [#124](https://github.com/ngrok/ngrok-operator/pull/124) ## 0.3.0 ### Changed * Renamed docker image from `ngrok/ngrok-ingress-controller` to `ngrok/kubernetes-ingress-controller`. * Added new controllers for `domains`, `tcpedges`, and `httpsedges`. * Updated go dependencies * Moved `main.go` to root of project to match what `kubebuilder` expects. * Updated `Makefile` to match what `kubebuilder` currently outputs. * Created `serverAddr` flag and plumbed it through to `ngrok-go` * Read environment variable `NGROK_API_ADDR` for an override to the ngrok API address. ## 0.2.0 ### Changed * Moved from calling ngrok-agent sidecar to using the ngrok-go library in process. ## 0.1.X ### Initial Alpha Releases The ngrok ingress controller is currently in alpha. Releases will have varying features with breaking changes. # AgentEndpoint Source: https://ngrok.com/docs/gateway/k8s/crds/agentendpoint Reference documentation for the AgentEndpoint Kubernetes custom resource for managing agent-based endpoints with the ngrok operator. ## AgentEndpoint custom resource ### **apiVersion:** `ngrok.k8s.ngrok.com/v1alpha1` ### **kind:** `AgentEndpoint` Agent Endpoints are endpoints that are only available when an agent is running. When used with the ngrok Kubernetes Operator, this means that the Agent Endpoints are tied to the Operator's `operator-agent` pod (which is deployed by default when installing the Operator). The Operator will manage, create, and delete Agent Endpoints for you according to the configuration of the `AgentEndpoint` custom resources you create. So long as at least one instance of the `operator-agent` pod is running, your Agent Endpoints will be available. You may occasionally notice the IDs of Agent Endpoints managed by the Operator change if the Operator pods restart, this will not halt traffic through your Agent Endpoints unless all of the Operator pods have stopped. See the [ngrok agent CLI configuration page](/docs/gateway/agent/config/v3/#endpoint-definitions), for more information about using the CLI to start Agent Endpoints outside of Kubernetes. Note that while using `AgentEndpoint` resources, an Agent Endpoint will be created in your ngrok account for each replica of the `ngrok-operator-agent` `Deployment`. This enables high-availability when running the `ngrok-operator-agent` deployment at a high replica count, but will result in additional charges for your ngrok account. The lifetime of these Agent Endpoints are tied to the lifetime of the `ngrok-operator-agent` pods, so scaling the deployment up/down will cause the additional Agent Endpoints in your ngrok account to be created as needed and deleted when no longer needed. ## AgentEndpoint structure and types The following outlines the high level structure and typings of an `AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: namespace: spec: url: # required upstream: # required url: # required trafficPolicy: # optional targetRef: # optional, one of inline or targetRef must be supplied name: # required inline: # optional, one of inline or targetRef must be supplied description: # optional, default: "Created by the ngrok-operator" metadata: # optional, default: "{"owned-by":"ngrok-operator"}" bindings: <[]string> # optional clientCertificateRefs: # optional - name: # required namespace: # optional ``` ## AgentEndpoint fields The following sections outline each field of the `AgentEndpoint` custom resource, whether they are required, what their default values are (if applicable), and a description of their purpose/constraints. ### `spec` `spec` defines the desired state of the `AgentEndpoint` **Type:** `Object` **Required:** yes **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.url`](#specurl) | `string` | yes | none | The unique URL for this Agent Endpoint that defines how the endpoint is accessed | | [`spec.upstream`](#specupstream) | `Object` | yes | none | The upstream that will receive traffic sent to the endpoint (unless a response is sent by a Traffic Policy action first) | | [`spec.trafficPolicy`](#spectrafficpolicy) | `Object` | no | none | Allows a Traffic Policy to be supplied for the endpoint | | [`spec.description`](#specdescription) | `string` | no | `"Created by the ngrok-operator"` | Human-readable description for this `AgentEndpoint` to help identify/describe it | | [`spec.metadata`](#specmetadata) | `string` | no | `"{"owned-by":"ngrok-operator"}"` | String of arbitrary data associated with the object in the ngrok API/Dashboard | | [`spec.bindings`](#specbindings) | `[]string` (enum) | no | none | List of Binding IDs to associate with the endpoint | | [`spec.clientCertificateRefs`](#specclientcertificaterefs) | `[]Object` | no | none | List of client certificates to present to the upstream when performing a TLS handshake | ### `spec.url` The unique URL for this Agent Endpoint. This URL defines how the endpoint is accessed. **Note:** [Pooling](/docs/gateway/endpoints/endpoint-pooling) is always enabled for `AgentEndpoint` resources. This means that if two (or more) endpoints share the same `spec.url` then requests will be balanced between them. This is not limited to endpoints within the same cluster, or endpoints of the same type. For example, an `AgentEndpoint` can be pooled with a Cloud Endpoint created in the ngrok dashboard if they share the same URL and both enable pooling. It is recommended to include a prefix/suffix in the URL to prevent unintended pooling. For example, if you are running a service `foo` in the namespace `bar` in two different Kubernetes clusters, you may want to give each one a slightly different `spec.url` that includes something such as a cluster identifier to prevent requests from being balanced between them. Unlike `AgentEndpoint` resources, the `CloudEndpoint` resource gives you the ability to configure whether pooling should be allowed or not. **Type:** `string` **Required:** yes **Default:** none The following formats are accepted: * **Domain** * When using the domain format you are only defining the domain. The scheme and port will be inferred. * example: `example.org` * **Origin** * When using the origin format you are defining the protocol, domain, and port. HTTP endpoints accept ports 80 or 443 with respective protocol. * examples: * `https://example.ngrok.app` * `https://example.ngrok.app:443` * `tcp://1.tcp.ngrok.io:12345` * `tls://example.ngrok.app` * **Scheme (shorthand)** * When using scheme you are defining the protocol and will receive back a randomly assigned ngrok address. * examples: * `https://` * `tcp://` * `tls://` * `http://` * **Empty** * When empty your endpoint will default to be https and receive back a randomly assigned ngrok address. * **Internal** * When ending your URL with .internal, an internal endpoint will be created. Internal Endpoints cannot be accessed directly, but rather can only be accessed using the forward-internal Traffic Policy action. * example: `some.domain.internal` ### `spec.upstream` Configures the upstream for the `AgentEndpoint` that should receive traffic sent to the `AgentEndpoint` **Type:** `Object` **Required:** yes **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.upstream.url`](#specupstreamurl) | `string` | yes | none | The local or remote address that incoming traffic should be forwarded to | | [`spec.upstream.protocol`](#specupstreamprotocol) | `string` (enum) | no | `http1` | The protocol to use when connecting to the upstream | ### `spec.upstream.url` The local or remote address you would like to incoming traffic to be forwarded to. **Type:** `string` **Required:** yes **Default:** none Accepted formats are: * **Origin** * When using the origin format you are defining the protocol, domain, and port. When no port is present and scheme is https or http the port will be inferred. * For https port will be `443` * For http port will be `80` * examples: * `https://example.org` * `http://example.org:80` * `tcp://127.0.0.1:80` * **Domain** * This is only allowed for https and http endpoints. For TCP and TLS endpoints host and port is required. * When using the domain format you are only defining the host. - Scheme will default to `http` - Port will default to `80` * example: `example.org` * **Scheme (shorthand)** * This only works for https and http. For tcp and tls host and port is required. * When using scheme you are defining the protocol and the port will be inferred on the local host. * For https port will be `443` * For http port will be `80` * Host will be `localhost` * example: `https://` * **Port (shorthand)** * When using port you are defining the port on the local host that will receive traffic. * Scheme will default to `http` * Host will default to `localhost` * example: `8080` ### `spec.upstream.protocol` Specifies the protocol to use when connecting to the upstream. Currently only `http1` and `http2` are supported with prior knowledge (defaulting to `http1`). **Type:** `string` (enum) **Required:** no **Default:** `http1` **Allowed Values:** `http1`,`http2` ### `spec.trafficPolicy` `spec.trafficPolicy` allows for an ngrok Traffic Policy to be used for this `AgentEndpoint`. It may be supplied inline or via a reference to an `NgrokTrafficPolicy` custom resource in the same namespace as the `AgentEndpoint`. * If `spec.trafficPolicy` is configured, then one of `spec.trafficPolicy.inline` or `spec.trafficPolicy.targetRef` must be supplied * It is invalid to supply both `spec.trafficPolicy.inline` and `spec.trafficPolicy.targetRef` For more information about writing ngrok Traffic Policies, refer to the [Traffic Policy section](/docs/gateway/traffic-policy/). **Type:** `Object` **Required:** no **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.trafficPolicy.inline`](#spectrafficpolicyinline) | `json.RawMessage` | no | none | Inline Traffic Policy configuration for this `AgentEndpoint` | | [`spec.trafficPolicy.targetRef`](#spectrafficpolicytargetref) | `Object` | no | none | Reference to an `NgrokTrafficPolicy` custom resource in the same namespace as the `AgentEndpoint` | ### `spec.trafficPolicy.inline` Allows you to supply a Traffic Policy for this `AgentEndpoint` with inline configuration. An example has been included below. For more information about writing ngrok Traffic Policies, refer to the [Traffic Policy section](/docs/gateway/traffic-policy/). **Type:** `json.RawMessage` **Required:** no **Default:** none ### `spec.trafficPolicy.targetRef` Allows you to supply a Traffic Policy for this `AgentEndpoint` as a reference to an `NgrokTrafficPolicy` custom resource in the same namespace as the `AgentEndpoint`. An example has been included below. For more information about writing ngrok Traffic Policies, refer to the [Traffic Policy section](/docs/gateway/traffic-policy/). **Type:** `Object` **Required:** no **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | `spec.trafficPolicy.targetRef.name` | `string` | yes | none | Name of an `NgrokTrafficPolicy` custom resource in the same namespace as the `AgentEndpoint` | ### `spec.description` Human-readable description of this Agent Endpoint that can be used to help identify/describe it. **Type:** `string` **Required:** no **Default:** `"Created by the ngrok-operator"` ### `spec.metadata` String of arbitrary data associated with the object in the ngrok API/Dashboard. **Type:** `string` **Required:** no **Default:** `"{"owned-by":"ngrok-operator"}"` ### `spec.bindings` List of Binding IDs to associate with the endpoint **Type:** `string` (enum) **Required:** no **Accepted Values:** `"public"`, `"internal"`, or `"kubernetes"` * A binding of `"internal"` will create an internal `AgentEndpoint` that is only accessible from other endpoints using the `forward-internal` Traffic Policy action. * Aside from setting `bindings: ["internal"]`, the `spec.url` of the `AgentEndpoint` must end in `.internal` * Setting `bindings: ["public"]` will not work for internal endpoints (endpoints that have a `spec.url` ending in `.internal`) * A binding of `"public"` will create a standard `AgentEndpoint` with its `spec.url` accessible to the public internet * Setting `bindings: ["internal"]` will not work for public endpoints * A binding of `"kubernetes"` will create a Kubernetes bound endpoint. This causes the endpoint to not be publicly accessible to the public internet and will be projected into one or more of your Kubernetes clusters that are running the ngrok Operator (depending on their endpoint selector configuration) as `Services`. * With a `"kubernetes"` binding, the `spec.url` becomes the `name.namespace` of the projected Service. * For example, with a `spec.url` of `http://my-service.my-namespace` then a Service with name `my-service` in the `my-namespace` namespace will be created by the ngrok Operator. Other services within the Kubernetes cluster can make requests to this endpoint. This enables many different use-cases such as being able to project a service in one Kubernetes cluster to another Kubernetes cluster so that services running in the other cluster can make requests to it. This has the benefit of not needing to expose the endpoint to the public internet behind a publicly accessible URL. ### `spec.clientCertificateRefs` List of references to secrets that contain client certificates to present to the upstream when performing a TLS handshake. These will be ignored if the upstream is not using TLS. For example, if the `spec.upstream.url` is `http://service.default:80` then the `clientCertificateRefs` will not be used. The secrets are expected to contain the data in the keys `tls.crt` and `tls.key`. The `namespace` field is optional, and when not supplied, the namespace of the `AgentEndpoint` will be used. When the `spec.upstream.url` has an `https://` scheme, the client certificates will have no effect because no TLS is being used when connecting to the upstream. **Type:** `[]Object` **Required:** no **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | `spec.clientCertificateRefs.name` | `string` | yes | none | Name of a `Secret` resource to pull the client certificate data from | | `spec.clientCertificateRefs.namespace` | `string` | no | none | Namespace of a `Secret` resource to pull the client certificate data from. When not supplied, the namespace of the `AgentEndpoint` is used | ## Status fields The `AgentEndpoint` resource includes status information that reflects the current state of the Agent Endpoint in the ngrok system. ### `status.assignedURL` The actual URL assigned to the endpoint. This may differ from `spec.url` when using shorthand formats (like `https://` or empty). **Type:** `string` **Example:** `https://example-agent-endpoint.ngrok.app` ### `status.trafficPolicy` Indicates which Traffic Policy is attached to the endpoint. **Type:** `string` **Possible Values:** * `inline` - Endpoint uses an inline Traffic Policy * `none` - No Traffic Policy is attached * `` - Name of the referenced NgrokTrafficPolicy resource **Example:** `inline` ### `status.domainRef` Reference to the Domain resource associated with this Agent Endpoint. **Type:** `Object` **Fields:** * `name` - Name of the Domain resource * `namespace` - Namespace of the Domain resource **Example:** ```yaml theme={null} domainRef: name: example-agent-endpoint-ngrok-app namespace: default ``` ### `status.conditions` Standard Kubernetes conditions that indicate the state of the Agent Endpoint. **Type:** `[]Condition` Each condition includes: * `type` - The condition type * `status` - `True`, `False`, or `Unknown` * `reason` - A programmatic identifier for the condition state * `message` - A human-readable description * `lastTransitionTime` - When the condition last changed * `observedGeneration` - The resource generation this condition applies to #### Condition types ##### `Ready` Indicates whether the Agent Endpoint is fully operational and ready to receive traffic. **Status Values:** * `True` - Agent endpoint is active and ready * `False` - Agent endpoint is not ready (see reason for details) **Reasons:** | Reason | Status | Meaning | | - | - | - | | `EndpointActive` | `True` | Agent endpoint is successfully created and active | | `Pending` | `False` | Endpoint creation is pending | | `Unknown` | `False` | Endpoint state is unknown | | `DomainNotReady` | `False` | Associated domain is not ready | Additionally, the `Ready` condition may reflect reasons from the associated Domain resource when domain issues prevent endpoint creation. **Example:** ```yaml theme={null} conditions: - type: Ready status: "True" reason: EndpointActive message: "AgentEndpoint is active and ready" lastTransitionTime: "2025-10-29T00:39:26Z" observedGeneration: 1 ``` ##### `EndpointCreated` Indicates whether the endpoint was successfully created in the ngrok API. **Status Values:** * `True` - Endpoint was successfully created * `False` - Failed to create endpoint **Reasons:** | Reason | Status | Meaning | | - | - | - | | `EndpointCreated` | `True` | Endpoint successfully created | | `NgrokAPIError` | `False` | API call to create endpoint failed | | `ConfigurationError` | `False` | Endpoint configuration is invalid | | `UpstreamError` | `False` | Upstream configuration is invalid | **Example:** ```yaml theme={null} conditions: - type: EndpointCreated status: "True" reason: EndpointCreated message: "Endpoint successfully created" lastTransitionTime: "2025-10-29T00:39:26Z" observedGeneration: 1 ``` ##### `TrafficPolicyApplied` Indicates whether the Traffic Policy was successfully applied to the endpoint. **Status Values:** * `True` - Traffic policy is applied * `False` - Failed to apply Traffic Policy **Reasons:** | Reason | Status | Meaning | | - | - | - | | `TrafficPolicyApplied` | `True` | Traffic policy successfully applied | | `TrafficPolicyError` | `False` | Error applying or validating Traffic Policy | **Example:** ```yaml theme={null} conditions: - type: TrafficPolicyApplied status: "True" reason: TrafficPolicyApplied message: "Traffic policy successfully applied" lastTransitionTime: "2025-10-29T00:39:26Z" observedGeneration: 1 ``` ##### `DomainReady` Indicates whether the associated domain is ready for use. This condition propagates the state from the referenced Domain resource. **Status Values:** * `True` - Domain is ready * `False` - Domain is not ready **Reasons:** This condition propagates reasons from the Domain resource's `Ready` condition, including: * `DomainActive` - Domain is successfully reserved and ready * `ProvisioningError` - Domain has DNS or certificate provisioning errors * `DomainCreationFailed` - Domain failed to be created * `DomainInvalid` - Domain format or configuration is invalid **Example:** ```yaml theme={null} conditions: - type: DomainReady status: "True" reason: DomainActive message: "Domain ready for use" lastTransitionTime: "2025-10-29T00:39:25Z" observedGeneration: 1 ``` **Example (Domain DNS Error):** ```yaml theme={null} conditions: - type: DomainReady status: "False" reason: ProvisioningError message: 'DNS_ERROR Reserved domain "*.wildcard-test.example.com" NS DNS records are not pointing at the ngrok infrastructure' lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 ``` ## Status examples ### Successfully created Agent Endpoint ```yaml theme={null} status: assignedURL: https://example-agent-endpoint.ngrok.app trafficPolicy: inline domainRef: name: example-agent-endpoint-ngrok-app namespace: default conditions: - type: DomainReady status: "True" reason: DomainActive message: "Domain ready for use" lastTransitionTime: "2025-10-29T00:39:25Z" observedGeneration: 1 - type: EndpointCreated status: "True" reason: EndpointCreated message: "Endpoint successfully created" lastTransitionTime: "2025-10-29T00:39:26Z" observedGeneration: 1 - type: TrafficPolicyApplied status: "True" reason: TrafficPolicyApplied message: "Traffic policy successfully applied" lastTransitionTime: "2025-10-29T00:39:26Z" observedGeneration: 1 - type: Ready status: "True" reason: EndpointActive message: "AgentEndpoint is active and ready" lastTransitionTime: "2025-10-29T00:39:26Z" observedGeneration: 1 ``` ### Agent Endpoint with domain not ready ```yaml theme={null} status: domainRef: name: wildcard-wildcard-test-example-com namespace: default conditions: - type: DomainReady status: "False" reason: ProvisioningError message: 'DNS_ERROR Reserved domain "*.wildcard-test.example.com" NS DNS records are not pointing at the ngrok infrastructure' lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 - type: EndpointCreated status: "False" reason: Pending message: "Waiting for domain to be ready" lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 - type: Ready status: "False" reason: ProvisioningError message: 'DNS_ERROR Reserved domain "*.wildcard-test.example.com" NS DNS records are not pointing at the ngrok infrastructure' lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 ``` ### Agent Endpoint with Traffic Policy error ```yaml theme={null} status: assignedURL: https://example-agent-endpoint.ngrok.app trafficPolicy: example-tp domainRef: name: example-agent-endpoint-ngrok-app namespace: default conditions: - type: DomainReady status: "True" reason: DomainActive message: "Domain ready for use" lastTransitionTime: "2025-10-29T00:39:25Z" observedGeneration: 1 - type: EndpointCreated status: "True" reason: EndpointCreated message: "Endpoint successfully created" lastTransitionTime: "2025-10-29T00:39:26Z" observedGeneration: 1 - type: TrafficPolicyApplied status: "False" reason: TrafficPolicyError message: "Invalid policy action type 'rate-limit-fake'. ERR_NGROK_2201" lastTransitionTime: "2025-10-29T00:39:26Z" observedGeneration: 1 - type: Ready status: "False" reason: TrafficPolicyError message: "AgentEndpoint not ready due to Traffic Policy error" lastTransitionTime: "2025-10-29T00:39:26Z" observedGeneration: 1 ``` ## Checking Agent Endpoint status You can check the status of an Agent Endpoint using kubectl: ```bash theme={null} # Check if Agent Endpoint is ready kubectl get agentendpoint example-agent-endpoint -o jsonpath='{.status.conditions[?(@.type=="Ready")]}' # Watch for Agent Endpoint to become ready kubectl wait --for=condition=Ready agentendpoint/example-agent-endpoint --timeout=60s # Get all Agent Endpoints with their ready status kubectl get agentendpoints -A -o custom-columns=\ NAME:.metadata.name,\ NAMESPACE:.metadata.namespace,\ URL:.status.assignedURL,\ TRAFFIC_POLICY:.status.trafficPolicy,\ READY:.status.conditions[?(@.type==\'Ready\')].status,\ REASON:.status.conditions[?(@.type==\'Ready\')].reason # Check domain readiness for an Agent Endpoint kubectl get agentendpoint example-agent-endpoint -o jsonpath='{.status.conditions[?(@.type=="DomainReady")]}' # Check Traffic Policy status kubectl get agentendpoint example-agent-endpoint -o jsonpath='{.status.conditions[?(@.type=="TrafficPolicyApplied")]}' ``` ## Example AgentEndpoints ### AgentEndpoint with inline Traffic Policy The following `AgentEndpoint`: * Accepts traffic on the URL `https://example-agent-endpoint.ngrok.io` * Sends traffic to `http://my-service.my-namespace:8080` * If a Traffic Policy "terminates" the request, then the traffic will not be sent to the upstream * Adds an inline Traffic Policy * If the request URL contains `?debug=true` query parameter, then the Traffic Policy will "terminate" the request and send back a custom response ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-agent-endpoint.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - name: example-rule expressions: - req.url.query_params['debug'][0]=='true' actions: - type: custom-response config: body: Example response from Traffic Policy headers: content-type: text/plain status_code: 200 ``` ### AgentEndpoint with referenced Traffic Policy The following `AgentEndpoint`: * Accepts traffic on the URL `https://example-agent-endpoint.ngrok.io` * Sends traffic to `http://my-https-service.my-namespace:8443` * Includes a client certificate from the `Secret` `my-client-cert` in the `default` namespace * If a Traffic Policy "terminates" the request, then the traffic will not be sent to the upstream * Adds a reference to a Traffic Policy that * If the request URL contains `?debug=true` query parameter, then the Traffic Policy will "terminate" the request and send back a custom response ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - name: example-rule expressions: - req.url.query_params['debug'][0]=='true' actions: - type: custom-response config: body: Example response from Traffic Policy headers: content-type: text/plain status_code: 200 --- apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-agent-endpoint.ngrok.io upstream: url: https://my-https-service.my-namespace:8443 trafficPolicy: targetRef: name: example-tp clientCertificateRefs: - name: my-client-cert namespace: default ``` # BoundEndpoint Source: https://ngrok.com/docs/gateway/k8s/crds/boundendpoint Reference documentation for the BoundEndpoint Kubernetes custom resource for managing cross-cluster networking with ngrok. ## BoundEndpoint custom resource ### **apiVersion:** `bindings.k8s.ngrok.com/v1alpha1` ### **kind:** `BoundEndpoint` `BoundEndpoint` is a type used to identify which bound endpoints you have in each Kubernetes cluster running the ngrok Operator. They are managed automatically by the ngrok Operator and should not generally need to be created/deleted manually. When you create an ngrok endpoint with a binding of `"kubernetes"` this will result in a Kubernetes bound endpoint. Kubernetes bound endpoints will be projected into your Kubernetes clusters that are running the ngrok Operator as `BoundEndpoint` resources that cause the ngrok Operator to project the endpoint into the cluster as a `Service` so that other services within the Kubernetes cluster can make requests to the endpoint by sending requests to the projected `Service` that was created for the endpoint. In-practice, this enables you to do things such as create a Kubernetes bound endpoint for a service in one cluster, and project it as a `Service` into one or more other clusters running the ngrok Operator. Doing so allows for cross-cluster networking without needing to expose your services to the public internet. The ngrok Operator does not do this by default, and you will need to enable the feature with the `bindings.enabled` helm value to create the Pods/Deployment from the Operator that enable this feature. The `BoundEndpoint` resources are automatically created/updated/deleted in response to `CloudEndpoint` and `AgentEndpoint` resources with `spec.bindings: \["kubernetes"\]` and any endpoints created from the ngrok CLI, API, and dashboard with a binding type of "kubernetes". The ngrok Operator includes a container argument `--bindings-endpoint-selectors` (helm value `bindings.endpointSelectors`) that will limit the endpoints that can be bound to the cluster that this Operator is running in. Unless this is configured, by default all Kubernetes bound endpoints will be projected into the cluster when the bindings feature is enabled. For more information about Kubernetes bound endpoints, you can reference the [Kubernetes endpoints](/docs/gateway/endpoints/kubernetes-endpoints) page. For a guide on using Kubernetes bound endpoints with the ngrok Operator, please see the [bound endpoints guide](/docs/gateway/k8s/guides/bindings). The ngrok Operator does not create the target namespace (`spec.target.namespace`) for you. If the namespace does not already exist in the cluster, projection fails and the `BoundEndpoint`'s `ServicesCreated` condition reports [`ERR_NGROK_20003`](/docs/errors/err_ngrok_20003) with a `namespaces "" not found` message — see the [example status](#bound-endpoint-with-service-creation-error) below. Create the namespace ahead of time with `kubectl create ns `. ## BoundEndpoint structure and types The following outlines the high level structure and typings of a `BoundEndpoint` ```yaml theme={null} apiVersion: bindings.k8s.ngrok.com/v1alpha1 kind: BoundEndpoint metadata: name: namespace: spec: endpointURL: # required scheme: # required port: # required target: # required service: # required namespace: # required protocol: # required port: # required metadata: # optional labels: # optional annotations: # optional ``` ## BoundEndpoint fields The following sections outline each field of the `BoundEndpoint` custom resource, whether they are required, what their default values are (if applicable), and a description of their purpose/constraints. ### `spec` `spec` defines the desired state of the `BoundEndpoint` **Type:** `Object` **Required:** yes **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.endpointURL`](#specendpointurl) | `string` | yes | none | The unique identifier representing the BoundEndpoint, format: `://.:` | | [`spec.scheme`](#specscheme) | `string` (enum) | yes | none | Describes how the data packets are framed by the pod forwarders mTLS connection | | [`spec.port`](#specport) | `uint16` | yes | none | The Service port this Endpoint uses internally to communicate with its Upstream Service | | [`spec.target`](#spectarget) | `Object` | yes | none | The target Service that this Endpoint projects | ### `spec.endpointURL` The endpointURL is the unique identifier representing the BoundEndpoint + its Endpoints * Format: `://.:` **Type:** `string` **Required:** yes **Default:** none ### `spec.scheme` Scheme is a user-defined field for endpoints that describe how the data packets are framed by the pod forwarders mTLS connection **Type:** `string (enum)` **Required:** yes **Default:** none **Allowed Values:** `tcp`, `http`, `https`, `tls` ### `spec.port` Port is the Service port this Endpoint uses internally to communicate with its Upstream Service **Type:** `uint16` **Required:** yes **Default:** none ### `spec.target` The target Service that this Endpoint projects **Type:** `Object` **Required:** yes **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.target.service`](#spectargetservice) | `string` | yes | none | The name of the Service that this Endpoint projects | | [`spec.target.namespace`](#spectargetnamespace) | `string` | yes | none | The destination Namespace for the Service this Endpoint projects | | [`spec.target.protocol`](#spectargetprotocol) | `string` (enum) | yes | none | The Service protocol this Endpoint uses | | [`spec.target.port`](#spectargetport) | `int32` | yes | none | The Service targetPort this Endpoint's Target Service uses for requests | | [`spec.target.metadata`](#spectargetmetadata) | `Object` | no | none | Annotations and labels to be added to the Service this Endpoint projects | ### `spec.target.service` The name of the Service that this Endpoint projects **Type:** `string` **Required:** yes **Default:** none ### `spec.target.namespace` The destination Namespace for the Service this Endpoint projects **Type:** `string` **Required:** yes **Default:** none ### `spec.target.protocol` The Service protocol this Endpoint uses. Currently, only `TCP` is supported. **Type:** `string (enum)` **Required:** yes **Default:** none **Allowed Values:** `TCP` ### `spec.target.port` The Service targetPort this Endpoint's Target Service uses for requests **Type:** `int32` **Required:** yes **Default:** none ### `spec.target.metadata` Metadata is a subset of metav1.ObjectMeta that is added to the Service. Currently, it supports setting labels and annotations on the projected Service. **Type:** `Object` **Required:** no **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.target.metadata.labels`](#spectargetmetadatalabels) | `map[string]string` | no | none | Labels to be set on the projected Service | | [`spec.target.metadata.annotations`](#spectargetmetadataannotations) | `map[string]string` | no | none | Annotations to be set on the projected Service | ### `spec.target.metadata.labels` A map of string keys and values that will be added to the projected Service's `metadata.labels` **Type:** `map[string]string` **Required:** no **Default:** none ### `spec.target.metadata.annotations` A map of string keys and values that will be added to the projected Service's `metadata.annotations` **Type:** `map[string]string` **Required:** no **Default:** none ## Status fields The `BoundEndpoint` resource includes status information that reflects the current state of the bound endpoint projection into the cluster. ### `status.hashedName` The hashed name used for the BoundEndpoint and its associated resources. **Type:** `string` **Example:** `ngrok-fdc71d00-ab87-5f84-b837-b315c947a52c` ### `status.endpoints` Array of endpoint information for the bound endpoints. **Type:** `[]Object` **Fields:** * `id` - The ngrok API endpoint ID * `uri` - (Optional) The ngrok API URI for the endpoint **Example:** ```yaml theme={null} endpoints: - id: ep_34ieQ8TaQcuWrEWPahJJ5p7sHDR uri: https://api.ngrok.com/endpoints/ep_34ieQ8TaQcuWrEWPahJJ5p7sHDR - id: ep_34iePjXiqBy9J7zS71qsEk1PxbJ ``` ### `status.endpointsSummary` Human-readable summary of the number of endpoints bound to this resource. **Type:** `string` **Example:** `2 endpoints` ### `status.targetServiceRef` Reference to the target Service that was created in the specified namespace. **Type:** `Object` **Fields:** * `name` - Name of the target Service * `namespace` - Namespace of the target Service **Example:** ```yaml theme={null} targetServiceRef: name: alex-bindings-cli-test namespace: default ``` ### `status.upstreamServiceRef` Reference to the upstream Service created by the Operator for routing traffic. **Type:** `Object` **Fields:** * `name` - Name of the upstream Service **Example:** ```yaml theme={null} upstreamServiceRef: name: ngrok-fdc71d00-ab87-5f84-b837-b315c947a52c ``` ### `status.conditions` Standard Kubernetes conditions that indicate the state of the bound endpoint. **Type:** `[]Condition` Each condition includes: * `type` - The condition type * `status` - `True`, `False`, or `Unknown` * `reason` - A programmatic identifier for the condition state * `message` - A human-readable description * `lastTransitionTime` - When the condition last changed * `observedGeneration` - The resource generation this condition applies to #### Condition types ##### `Ready` Indicates whether the bound endpoint is fully operational and ready for use. **Status Values:** * `True` - Bound endpoint is ready * `False` - Bound endpoint is not ready (see reason for details) **Reasons:** | Reason | Status | Meaning | | - | - | - | | `BoundEndpointReady` | `True` | Bound endpoint is successfully configured and ready | | `ServiceCreationFailed` | `False` | Failed to create the target or upstream service | **Example:** ```yaml theme={null} conditions: - type: Ready status: "True" reason: BoundEndpointReady message: "BoundEndpoint is ready" lastTransitionTime: "2025-10-29T01:20:43Z" observedGeneration: 1 ``` ##### `ServicesCreated` Indicates whether the target and upstream Services were successfully created. **Status Values:** * `True` - Services were successfully created * `False` - Failed to create services **Reasons:** | Reason | Status | Meaning | | - | - | - | | `ServicesCreated` | `True` | Target and upstream services created successfully | | `ServiceCreationFailed` | `False` | Failed to create services (for example, namespace doesn't exist) | **Example:** ```yaml theme={null} conditions: - type: ServicesCreated status: "True" reason: ServicesCreated message: "Target and Upstream services created" lastTransitionTime: "2025-10-29T01:20:40Z" observedGeneration: 1 ``` ##### `ConnectivityVerified` Indicates whether connectivity to the upstream service was successfully verified. **Status Values:** * `True` - Connectivity verified * `False` - Connectivity verification failed **Reasons:** | Reason | Status | Meaning | | - | - | - | | `ConnectivityVerified` | `True` | Successfully connected to upstream service | **Example:** ```yaml theme={null} conditions: - type: ConnectivityVerified status: "True" reason: ConnectivityVerified message: "Successfully connected to upstream service" lastTransitionTime: "2025-10-29T01:20:43Z" observedGeneration: 1 ``` ## Status examples ### Successfully Bound Endpoint ```yaml theme={null} status: hashedName: ngrok-fdc71d00-ab87-5f84-b837-b315c947a52c endpoints: - id: ep_34ieQ8TaQcuWrEWPahJJ5p7sHDR - id: ep_34iePjXiqBy9J7zS71qsEk1PxbJ endpointsSummary: 2 endpoints targetServiceRef: name: alex-bindings-cli-test namespace: default upstreamServiceRef: name: ngrok-fdc71d00-ab87-5f84-b837-b315c947a52c conditions: - type: ServicesCreated status: "True" reason: ServicesCreated message: "Target and Upstream services created" lastTransitionTime: "2025-10-29T01:20:40Z" observedGeneration: 1 - type: ConnectivityVerified status: "True" reason: ConnectivityVerified message: "Successfully connected to upstream service" lastTransitionTime: "2025-10-29T01:20:43Z" observedGeneration: 1 - type: Ready status: "True" reason: BoundEndpointReady message: "BoundEndpoint is ready" lastTransitionTime: "2025-10-29T01:20:43Z" observedGeneration: 1 ``` ### Bound endpoint with service creation error ```yaml theme={null} status: hashedName: ngrok-96c65d4f-1422-5503-b353-0c9e1fbc4c68 endpoints: - id: ep_34ieQajOqQJ9DcgzCroGWvlMak2 endpointsSummary: 1 endpoint conditions: - type: ServicesCreated status: "False" reason: ServiceCreationFailed message: 'HTTP 503: Failed to create Target Service: namespaces "does-not-exist" not found [ERR_NGROK_20003]' lastTransitionTime: "2025-10-29T01:20:40Z" observedGeneration: 1 - type: Ready status: "False" reason: ServiceCreationFailed message: 'HTTP 503: Failed to create Target Service: namespaces "does-not-exist" not found [ERR_NGROK_20003]' lastTransitionTime: "2025-10-29T01:20:40Z" observedGeneration: 1 ``` ## Checking BoundEndpoint status You can check the status of a bound endpoint using kubectl: ```bash theme={null} # Check if bound endpoint is ready kubectl get boundendpoint -n ngrok-operator -o jsonpath='{.status.conditions[?(@.type=="Ready")]}' # Watch for bound endpoint to become ready kubectl wait --for=condition=Ready boundendpoint/ -n ngrok-operator --timeout=60s # Get all bound endpoints with their ready status kubectl get boundendpoints -A -o custom-columns=\ NAME:.metadata.name,\ NAMESPACE:.metadata.namespace,\ ENDPOINTS:.status.endpointsSummary,\ TARGET:.status.targetServiceRef.name,\ READY:.status.conditions[?(@.type==\'Ready\')].status,\ REASON:.status.conditions[?(@.type==\'Ready\')].reason # Check services creation status kubectl get boundendpoint -n ngrok-operator -o jsonpath='{.status.conditions[?(@.type=="ServicesCreated")]}' # Check connectivity verification status kubectl get boundendpoint -n ngrok-operator -o jsonpath='{.status.conditions[?(@.type=="ConnectivityVerified")]}' ``` ## Example BoundEndpoint The following `BoundEndpoint` will bind the endpoint with URL `http://my-service.my-namespace:80` and project it as a `Service` `my-service` in the `my-namespace` namespace on port 80. ```yaml theme={null} apiVersion: bindings.k8s.ngrok.com/v1alpha1 kind: BoundEndpoint metadata: name: ngrok-22680758-eb09-576e-a7ac-dc3728458dfc namespace: ngrok-operator spec: endpointURL: http://my-service.my-namespace:80 port: 10001 scheme: http target: metadata: labels: my-label-key: my-label-val annotations: my-annotation-key: my-annotation-val namespace: my-namespace port: 80 protocol: TCP service: my-service ``` # CloudEndpoint Source: https://ngrok.com/docs/gateway/k8s/crds/cloudendpoint Reference documentation for the CloudEndpoint Kubernetes custom resource for creating persistent, always-on Cloud Endpoints. ## CloudEndpoint custom resource ### **apiVersion:** `ngrok.k8s.ngrok.com/v1alpha1` ### **kind:** `CloudEndpoint` The `CloudEndpoint` custom resource is used to create centrally managed endpoints in the cloud. Cloud Endpoints are persistent, always-on endpoints that live within the ngrok cloud. They exist permanently until they are explicitly deleted. Cloud Endpoints do not forward their traffic to an agent by default and instead only use their attached Traffic Policy to handle connections. Using the Traffic Policy configuration, you may configure your Cloud Endpoints to route to one or more other internal ngrok endpoints. For more information about Cloud Endpoints in ngrok, refer to the [Cloud Endpoints page](/docs/gateway/endpoints/cloud-endpoints/) ## CloudEndpoint structure and types The following outlines the high level structure and typings of a `CloudEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: namespace: spec: url: # required trafficPolicyName: # optional trafficPolicy: # optional policy: # required description: # optional, default: "Created by the ngrok-operator" metadata: # optional, default: "{"owned-by":"ngrok-operator"}" bindings: <[]string> # optional poolingEnabled: # optional, default: false ``` ## CloudEndpoint fields The following sections outline each field of the `CloudEndpoint` custom resource, whether they are required, what their default values are (if applicable), and a description of their purpose/constraints. ### `spec` `spec` defines the desired state of the `CloudEndpoint` **Type:** `Object` **Required:** yes **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.url`](#specurl) | `string` | yes | none | The unique URL for this `CloudEndpoint` that defines how the endpoint is accessed | | [`spec.trafficPolicyName`](#spectrafficpolicyname) | `string` | no | none | The name of an `NgrokTrafficPolicy` custom resource in the same namespace as the `CloudEndpoint` to use for Traffic Policy configuration | | [`spec.trafficPolicy`](#spectrafficpolicy) | `Object` | no | none | Allows for a Traffic Policy to be provided to the `CloudEndpoint` using inline configuration | | [`spec.description`](#specdescription) | `string` | no | `"Created by the ngrok-operator"` | Human-readable description for this `CloudEndpoint` to help identify/describe it | | [`spec.metadata`](#specmetadata) | `string` | no | `"{"owned-by":"ngrok-operator"}"` | String of arbitrary data associated with the object in the ngrok API/Dashboard | | [`spec.bindings`](#specbindings) | `[]string` (enum) | no | none | List of Binding IDs to associate with the endpoint | | [`spec.poolingEnabled`](#specpoolingenabled) | `bool` | no | `false` | Whether or not the `CloudEndpoint` should enable pooling with other endpoints sharing the same URL | ### `spec.url` The unique URL for this Cloud Endpoint. This URL defines how the endpoint is accessed. **Type:** `string` **Required:** yes **Default:** none The following formats are accepted: * **Domain** * When using the domain format you are only defining the domain. The scheme and port will be inferred. * example: `example.org` * **Origin** * When using the origin format you are defining the protocol, domain, and port. HTTP endpoints accept ports `80` or `443` with respective protocol. * examples: * `https://example.ngrok.app` * `https://example.ngrok.app:443` * `tcp://1.tcp.ngrok.io:12345` * `tls://example.ngrok.app` * **Scheme (shorthand)** * When using scheme you are defining the protocol and will receive back a randomly assigned ngrok address. * examples: * `https://` * `tcp://` * `tls://` * `http://` * **Empty** When empty your endpoint will default to be https and receive back a randomly assigned ngrok address. * **Internal** * When ending your URL with .internal, an internal endpoint will be created. Internal Endpoints cannot be accessed directly, but rather can only be accessed using the forward-internal Traffic Policy action. * example: `some.domain.internal` ### `spec.trafficPolicyName` Reference to an `NgrokTrafficPolicy` custom resource in the same namespace as the `CloudEndpoint` to use for Traffic Policy configuration. It is invalid to configure both `spec.trafficPolicyName` and `spec.trafficPolicy` at the same time. The referenced `NgrokTrafficPolicy` **must** be in the same namespace as the `CloudEndpoint`. **Type:** `string` **Required:** no **Default:** none ### `spec.trafficPolicy` Reference to an `NgrokTrafficPolicy` custom resource in the same namespace as the `CloudEndpoint` to use for Traffic Policy configuration. It is invalid to configure both `spec.trafficPolicyName` and `spec.trafficPolicy` at the same time. For more information about writing ngrok Traffic Policies, refer to the [Traffic Policy section](/docs/gateway/traffic-policy/). **Type:** `Object` **Required:** no **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.trafficPolicy.policy`](#spectrafficpolicypolicy) | `json.RawMessage` | yes | none | The inline Traffic Policy configuration to be used for this `CloudEndpoint` | ### `spec.trafficPolicy.policy` Allows you to supply a Traffic Policy for this `AgentEndpoint` with inline configuration. An example has been included below. For more information about writing ngrok Traffic Policies, refer to the [Traffic Policy section](/docs/gateway/traffic-policy/). **Type:** `json.RawMessage` **Required:** yes **Default:** none ### `spec.description` Human-readable description of this Cloud Endpoint that can be used to help identify/describe it. **Type:** `string` **Required:** no **Default:** `"Created by the ngrok-operator"` ### `spec.metadata` String of arbitrary data associated with the object in the ngrok API/Dashboard. **Type:** `string` **Required:** no **Default:** `"{"owned-by":"ngrok-operator"}"` ### `spec.bindings` List of Binding IDs to associate with the endpoint **Type:** `string` (enum) **Required:** no **Default:** none **Accepted Values:** `"public"`, `"internal"`, or `"kubernetes"` * A binding of `"internal"` will create an internal `AgentEndpoint` that is only accessible from other endpoints using the `forward-internal` Traffic Policy action. * Aside from setting `bindings: ["internal"]`, the `spec.url` of the `AgentEndpoint` must end in `.internal` * Setting `bindings: ["public"]` will not work for internal endpoints (endpoints that have a `spec.url` ending in `.internal`) * A binding of `"public"` will create a standard `AgentEndpoint` with its `spec.url` accessible to the public internet * Setting `bindings: ["internal"]` will not work for public endpoints * A binding of `"kubernetes"` will create a Kubernetes bound endpoint. This causes the endpoint to not be publicly accessible to the public internet and will be projected into one or more of your Kubernetes clusters that are running the ngrok Operator (depending on their endpoint selector configuration) as `Services`. * With a `"kubernetes"` binding, the `spec.url` becomes the `name.namespace` of the projected Service. * For example, with a `spec.url` of `http://my-service.my-namespace` then a Service with name `my-service` in the `my-namespace` namespace will be created by the ngrok Operator. Other services within the Kubernetes cluster can make requests to this endpoint. This enables many different use-cases such as being able to project a service in one Kubernetes cluster to another Kubernetes cluster so that services running in the other cluster can make requests to it. This has the benefit of not needing to expose the endpoint to the public internet behind a publicly accessible URL. ### `spec.poolingEnabled` Configures whether [pooling](/docs/gateway/endpoints/endpoint-pooling) is always enabled for this `CloudEndpoint`. When pooling is enabled, if two different endpoints share the same `spec.url` then requests will be balanced between them. This is not limited to endpoints within the same cluster, or endpoints of the same type. For example, a `CloudEndpoint` can be pooled with an Agent Endpoint created using the ngrok CLI if they both enable pooling and share the same URL. It is recommended to include a prefix/suffix in the URL to prevent unintended pooling. For example, if you are running a service `foo` in the namespace `bar` in two different Kubernetes clusters, you may want to give each one a slightly different `spec.url` that includes something such as a cluster identifier to prevent requests from being balanced between them. You will receive an error back from the ngrok server if you attempt to create two different endpoints using the same URL if they do not both enable pooling. In this scenario, the older endpoint wins and the newer one will fail to be created. **Type:** `bool` **Required:** no **Default:** `false` ## Status fields The `CloudEndpoint` resource includes status information that reflects the current state of the Cloud Endpoint in the ngrok system. ### `status.id` The ngrok API ID for this Cloud Endpoint. **Type:** `string` **Example:** `ep_34iZT0muBP3kpcAXxGjM4uFlgDK` ### `status.domain` **(Deprecated)** Domain information for the endpoint. This field is deprecated; use `status.domainRef` instead. **Type:** `Object` ### `status.domainRef` Reference to the Domain resource associated with this Cloud Endpoint. **Type:** `Object` **Fields:** * `name` - Name of the Domain resource * `namespace` - Namespace of the Domain resource **Example:** ```yaml theme={null} domainRef: name: example-domain-ngrok-app namespace: default ``` ### `status.conditions` Standard Kubernetes conditions that indicate the state of the Cloud Endpoint. **Type:** `[]Condition` Each condition includes: * `type` - The condition type * `status` - `True`, `False`, or `Unknown` * `reason` - A programmatic identifier for the condition state * `message` - A human-readable description * `lastTransitionTime` - When the condition last changed * `observedGeneration` - The resource generation this condition applies to #### Condition types ##### `Ready` Indicates whether the Cloud Endpoint is fully operational and ready to use. **Status Values:** * `True` - Cloud endpoint is active and ready * `False` - Cloud endpoint is not ready (see reason for details) **Reasons:** | Reason | Status | Meaning | | - | - | - | | `CloudEndpointActive` | `True` | Cloud endpoint is successfully created and active | | `CloudEndpointCreationFailed` | `False` | Failed to create the Cloud Endpoint in ngrok | | `Pending` | `False` | Cloud endpoint creation is pending (for example, waiting for domain) | | `Unknown` | `False` | Cloud endpoint state is unknown | | `DomainNotReady` | `False` | Associated domain is not ready | Additionally, the `Ready` condition may reflect reasons from the associated Domain resource when domain issues prevent endpoint creation. **Example:** ```yaml theme={null} conditions: - type: Ready status: "True" reason: CloudEndpointActive message: "Cloud endpoint is active" lastTransitionTime: "2025-10-29T00:39:16Z" observedGeneration: 1 ``` **Example (Pooling Error):** ```yaml theme={null} conditions: - type: Ready status: "False" reason: CloudEndpointCreationFailed message: | Failed to create Cloud Endpoint: HTTP 400: The provided Cloud Endpoint was invalid because pooling was set to disabled when another endpoint exists for this url. [ERR_NGROK_18017] Operation ID: op_34ic9T376GyGAXQ4sIRn7F0xUYP lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 2 ``` ##### `CloudEndpointCreated` Indicates whether the Cloud Endpoint was successfully created in the ngrok API. **Status Values:** * `True` - Cloud endpoint was successfully created * `False` - Failed to create Cloud Endpoint **Reasons:** | Reason | Status | Meaning | | - | - | - | | `CloudEndpointCreated` | `True` | Cloud endpoint successfully created | | `CloudEndpointCreationFailed` | `False` | API call to create Cloud Endpoint failed | **Example:** ```yaml theme={null} conditions: - type: CloudEndpointCreated status: "True" reason: CloudEndpointCreated message: "Cloud endpoint successfully created" lastTransitionTime: "2025-10-29T00:39:16Z" observedGeneration: 1 ``` ##### `DomainReady` Indicates whether the associated domain is ready for use. This condition propagates the state from the referenced Domain resource. **Status Values:** * `True` - Domain is ready * `False` - Domain is not ready **Reasons:** This condition propagates reasons from the Domain resource's `Ready` condition, including: * `DomainActive` - Domain is successfully reserved and ready * `ProvisioningError` - Domain has DNS or certificate provisioning errors * `DomainCreationFailed` - Domain failed to be created * `DomainInvalid` - Domain format or configuration is invalid **Example:** ```yaml theme={null} conditions: - type: DomainReady status: "True" reason: DomainActive message: "Domain ready for use" lastTransitionTime: "2025-10-29T00:39:25Z" observedGeneration: 1 ``` **Example (Domain DNS Error):** ```yaml theme={null} conditions: - type: DomainReady status: "False" reason: ProvisioningError message: 'DNS_ERROR Reserved domain "example.com" NS DNS records are not pointing at the ngrok infrastructure' lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 ``` ## Status examples ### Successfully created Cloud Endpoint ```yaml theme={null} status: id: ep_34iZT0muBP3kpcAXxGjM4uFlgDK domainRef: name: example-domain-ngrok-app namespace: default conditions: - type: DomainReady status: "True" reason: DomainActive message: "Domain ready for use" lastTransitionTime: "2025-10-29T00:39:25Z" observedGeneration: 1 - type: CloudEndpointCreated status: "True" reason: CloudEndpointCreated message: "Cloud endpoint successfully created" lastTransitionTime: "2025-10-29T00:39:16Z" observedGeneration: 1 - type: Ready status: "True" reason: CloudEndpointActive message: "Cloud endpoint is active" lastTransitionTime: "2025-10-29T00:39:16Z" observedGeneration: 1 ``` ### Cloud Endpoint with pooling error ```yaml theme={null} status: domainRef: name: example-domain-ngrok-app namespace: default conditions: - type: DomainReady status: "True" reason: DomainActive message: "Domain ready for use" lastTransitionTime: "2025-10-29T00:39:25Z" observedGeneration: 2 - type: CloudEndpointCreated status: "False" reason: CloudEndpointCreationFailed message: | Failed to create Cloud Endpoint: HTTP 400: The provided Cloud Endpoint was invalid because pooling was set to disabled when another endpoint exists for this url. [ERR_NGROK_18017] Operation ID: op_34ic9T376GyGAXQ4sIRn7F0xUYP lastTransitionTime: "2025-10-29T00:39:25Z" observedGeneration: 2 - type: Ready status: "False" reason: CloudEndpointCreationFailed message: | Failed to create Cloud Endpoint: HTTP 400: The provided Cloud Endpoint was invalid because pooling was set to disabled when another endpoint exists for this url. [ERR_NGROK_18017] Operation ID: op_34ic9T376GyGAXQ4sIRn7F0xUYP lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 2 ``` ### Cloud Endpoint with domain not ready ```yaml theme={null} status: domainRef: name: custom-domain-com namespace: default conditions: - type: DomainReady status: "False" reason: ProvisioningError message: 'DNS_ERROR Reserved domain "example.com" NS DNS records are not pointing at the ngrok infrastructure' lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 - type: CloudEndpointCreated status: "False" reason: Pending message: "Waiting for domain to be ready" lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 - type: Ready status: "False" reason: DomainNotReady message: "Cloud endpoint cannot be created because domain is not ready" lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 ``` ## Checking Cloud Endpoint status You can check the status of a Cloud Endpoint using kubectl: ```bash theme={null} # Check if Cloud Endpoint is ready kubectl get cloudendpoint example-cloud-endpoint -o jsonpath='{.status.conditions[?(@.type=="Ready")]}' # Watch for Cloud Endpoint to become ready kubectl wait --for=condition=Ready cloudendpoint/example-cloud-endpoint --timeout=60s # Get all Cloud Endpoints with their ready status kubectl get cloudendpoints -A -o custom-columns=\ NAME:.metadata.name,\ NAMESPACE:.metadata.namespace,\ DOMAIN:.status.domainRef.name,\ READY:.status.conditions[?(@.type==\'Ready\')].status,\ REASON:.status.conditions[?(@.type==\'Ready\')].reason # Check domain readiness for a Cloud Endpoint kubectl get cloudendpoint example-cloud-endpoint -o jsonpath='{.status.conditions[?(@.type=="DomainReady")]}' ``` ## Example CloudEndpoints ### CloudEndpoint with inline Traffic Policy The following `CloudEndpoint`: * Accepts traffic on the URL `https://example-cloud-endpoint.ngrok.io` * Adds an inline Traffic Policy that sends back a custom response ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-cloud-endpoint.ngrok.io trafficPolicy: policy: on_http_request: - name: example-rule actions: - type: custom-response config: body: Example response from Traffic Policy headers: content-type: text/plain status_code: 200 ``` ### CloudEndpoint with referenced Traffic Policy The following `CloudEndpoint`: * Accepts traffic on the URL `https://example-cloud-endpoint.ngrok.io` * Adds a reference to a Traffic Policy that sends back a custom response ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: static-response namespace: default spec: policy: on_http_request: - name: example-rule actions: - type: custom-response config: body: Example response from Traffic Policy headers: content-type: text/plain status_code: 200 --- apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-cloud-endpoint.ngrok.io trafficPolicyName: static-response ``` # Domain Source: https://ngrok.com/docs/gateway/k8s/crds/domain Reference documentation for the Domain Kubernetes custom resource for managing reserved hostnames for ngrok endpoints. ## Domain custom resource ### **apiVersion:** `ingress.k8s.ngrok.com/v1alpha1` ### **kind:** `Domain` Domains define the hostnames that should be reserved for your ngrok endpoints. They are automatically created by the controller based on the `Ingress`, `Gateway`, `CloudEndpoint` and `AgentEndpoint` custom resources you create. Standard ngrok subdomains will automatically be created and reserved for you. Custom domains will also be created and reserved, but will be up to you to configure the DNS records for them. See the [custom domain](/docs/gateway/k8s/guides/custom-domain) guide for more details. If you delete all the `Ingress`/`Gateway`/`CloudEndpoint`/`AgentEndpoint` objects for a particular host, as a safety precaution, the Operator does *NOT* delete the domains and thus does not unregister them. This ensures you don't lose domains while modifying or recreating ingress objects. You can still manually delete a domain CRD via `kubectl delete domain ` if you want to unregister it. ## Domain structure and types The following outlines the high level structure and typings of a `Domain` ```yaml theme={null} apiVersion: ingress.k8s.ngrok.com/v1alpha1 kind: Domain metadata: name: namespace: spec: description: # optional, default: "Created by the ngrok-operator" metadata: # optional, default: "{"owned-by":"ngrok-operator"}" domain: # required region: # required ``` ## Domain fields The following sections outline each field of the `Domain` custom resource, whether they are required, what their default values are (if applicable), and a description of their purpose/constraints. ### `spec` `spec` defines the desired state of the `Domain` **Type:** `Object` **Required:** yes **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.description`](#specdescription) | `string` | no | `"Created by the ngrok-operator"` | Human-readable description for this `Domain` to help identify/describe it | | [`spec.metadata`](#specmetadata) | `string` | no | `"{"owned-by":"ngrok-operator"}"` | String of arbitrary data associated with the object in the ngrok API/Dashboard | | [`spec.domain`](#specdomain) | `string` | yes | none | The domain name to reserve | | [`spec.region`](#specregion) | `string` (enum) | no | none | (Deprecated) The region in which to reserve the domain | ### `spec.description` Human-readable description of this domain that can be used to help identify/describe it. **Type:** `string` **Required:** no **Default:** `"Created by the ngrok-operator"` ### `spec.metadata` String of arbitrary data associated with the object in the ngrok API/Dashboard. **Type:** `string` **Required:** no **Default:** `"{"owned-by":"ngrok-operator"}"` ### `spec.domain` The domain name to reserve. example: `example-domain.ngrok.io` **Type:** `string` **Required:** no **Default:** none ### `spec.region` The region in which to reserve domains With the launch of the ngrok Global Network domains traffic is now handled globally. Note that agents may still connect to specific regions. **Warning:** `spec.region` is deprecated **Type:** `string (enum)` **Required:** no **Default:** `"global"` **Allowed Values:** `"au"`, `"eu"`, `"ap"`, `"us"`, `"jp"`, `"in"`, `"sa"`, `"global"` ## Status fields The `Domain` resource includes status information that reflects the current state of the domain in the ngrok system. ### `status.id` The ngrok API ID for this domain. **Type:** `string` **Example:** `rd_347Xxo1moBQ5AgBmI3a9CFgcCrk` ### `status.domain` The actual domain string that was reserved. **Type:** `string` **Example:** `example-domain.ngrok.app` ### `status.region` The ngrok region where the domain is available. **Type:** `string` **Example:** `global` ### `status.cnameTarget` The CNAME target for custom domains. This field is only populated for custom (non-ngrok) domains and indicates where you should point your DNS records. **Type:** `string` **Example:** `example.cname.ngrok.io` ### `status.conditions` Standard Kubernetes conditions that indicate the state of the domain. **Type:** `[]Condition` Each condition includes: * `type` - The condition type * `status` - `True`, `False`, or `Unknown` * `reason` - A programmatic identifier for the condition state * `message` - A human-readable description * `lastTransitionTime` - When the condition last changed * `observedGeneration` - The resource generation this condition applies to #### Condition types ##### `Ready` Indicates whether the domain is fully operational and ready to use for endpoints. **Status Values:** * `True` - Domain is active and ready * `False` - Domain is not ready (see reason for details) **Reasons:** | Reason | Status | Meaning | | - | - | - | | `DomainActive` | `True` | Domain is successfully reserved and ready to use | | `DomainInvalid` | `False` | Domain format or configuration is invalid | | `DomainCreationFailed` | `False` | Failed to reserve the domain in ngrok | | `ProvisioningError` | `False` | Error provisioning custom domain (DNS/certificate issues) | **Example:** ```yaml theme={null} conditions: - type: Ready status: "True" reason: DomainActive message: "Domain ready for use" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 ``` ##### `DomainCreated` Indicates whether the domain was successfully reserved in the ngrok API. **Status Values:** * `True` - Domain was successfully reserved * `False` - Failed to reserve domain **Reasons:** | Reason | Status | Meaning | | - | - | - | | `DomainCreated` | `True` | Domain successfully reserved | | `DomainCreationFailed` | `False` | API call to reserve domain failed | | `DomainInvalid` | `False` | Domain name or format is invalid | **Example:** ```yaml theme={null} conditions: - type: DomainCreated status: "True" reason: DomainCreated message: "Domain successfully reserved" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 ``` ##### `CertificateReady` Indicates whether the TLS certificate for the domain is ready. **Status Values:** * `True` - Certificate is ready or managed by ngrok * `False` - Certificate is not ready **Reasons:** | Reason | Status | Meaning | | - | - | - | | `NgrokManaged` | `True` | Certificate is automatically managed by ngrok | | `CertificateReady` | `True` | Custom certificate is provisioned and ready | | `ProvisioningError` | `False` | Error provisioning custom domain certificate | | `DomainCreationFailed` | `False` | Cannot provision certificate because domain creation failed | | `DomainInvalid` | `False` | Cannot provision certificate because domain is invalid | **Example:** ```yaml theme={null} conditions: - type: CertificateReady status: "True" reason: NgrokManaged message: "Certificate managed by ngrok" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 ``` ##### `DNSConfigured` Indicates whether DNS is properly configured for the domain. **Status Values:** * `True` - DNS is configured correctly * `False` - DNS configuration is incomplete or incorrect **Reasons:** | Reason | Status | Meaning | | - | - | - | | `NgrokManaged` | `True` | DNS is automatically managed by ngrok (ngrok subdomain) | | `DomainCreated` | `True` | DNS records have been configured for custom domain | | `ProvisioningError` | `False` | DNS records are not pointing to ngrok infrastructure | | `DomainCreationFailed` | `False` | Cannot configure DNS because domain creation failed | | `DomainInvalid` | `False` | Cannot configure DNS because domain is invalid | **Example:** ```yaml theme={null} conditions: - type: DNSConfigured status: "True" reason: NgrokManaged message: "DNS managed by ngrok" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 ``` **Example (Custom Domain DNS Error):** ```yaml theme={null} conditions: - type: DNSConfigured status: "False" reason: ProvisioningError message: "DNS_ERROR Reserved domain \"example.com\" NS DNS records are not pointing at the ngrok infrastructure" lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 ``` ##### `Progressing` Indicates whether the domain is actively being provisioned (typically for custom domains). **Status Values:** * `True` - Domain is currently being provisioned * `False` - Domain is not in a provisioning state **Reasons:** | Reason | Status | Meaning | | - | - | - | | `Provisioning` | `True` | Domain provisioning is in progress | **Example:** ```yaml theme={null} conditions: - type: Progressing status: "True" reason: Provisioning message: "Domain provisioning in progress" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 ``` ## Status examples ### Successfully reserved ngrok subdomain ```yaml theme={null} status: id: rd_347Xxo1moBQ5AgBmI3a9CFgcCrk domain: example-domain.ngrok.app conditions: - type: DomainCreated status: "True" reason: DomainCreated message: "Domain successfully reserved" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 - type: CertificateReady status: "True" reason: NgrokManaged message: "Certificate managed by ngrok" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 - type: DNSConfigured status: "True" reason: NgrokManaged message: "DNS managed by ngrok" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 - type: Ready status: "True" reason: DomainActive message: "Domain ready for use" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 ``` ### Custom domain with DNS error ```yaml theme={null} status: domain: "*.wildcard-test.example.com" cnameTarget: "example.cname.ngrok.io" conditions: - type: DomainCreated status: "True" reason: DomainCreated message: "Domain successfully reserved" lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 - type: DNSConfigured status: "False" reason: ProvisioningError message: 'DNS_ERROR Reserved domain "*.wildcard-test.example.com" NS DNS records are not pointing at the ngrok infrastructure' lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 - type: CertificateReady status: "False" reason: ProvisioningError message: "Cannot provision certificate until DNS is configured" lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 - type: Ready status: "False" reason: ProvisioningError message: "Domain not ready due to DNS configuration error" lastTransitionTime: "2025-10-29T00:39:24Z" observedGeneration: 1 ``` ## Checking domain status You can check the status of a domain using kubectl: ```bash theme={null} # Check if domain is ready kubectl get domain example-domain -o jsonpath='{.status.conditions[?(@.type=="Ready")]}' # Watch for domain to become ready kubectl wait --for=condition=Ready domain/example-domain --timeout=60s # Get all domains with their ready status kubectl get domains -A -o custom-columns=\ NAME:.metadata.name,\ DOMAIN:.status.domain,\ READY:.status.conditions[?(@.type==\'Ready\')].status,\ REASON:.status.conditions[?(@.type==\'Ready\')].reason ``` ## Example domain ```yaml theme={null} apiVersion: ingress.k8s.ngrok.com/v1alpha1 kind: Domain metadata: name: example-domain namespace: default spec: description: Created by kubernetes-ingress-controller domain: example-domain.ngrok.io metadata: '{"owned-by":"kubernetes-gateway-api"}' ``` # Kubernetes Operator Custom Resources (CRDs) Source: https://ngrok.com/docs/gateway/k8s/crds/index Reference documentation for all custom resource definitions available in the ngrok Kubernetes Operator for managing endpoints and configurations. Kubernetes has the concept of [Custom Resource Definitions](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/) (CRDs) which allow you to define your own custom resources. This document will cover the CRDs you might use to achieve your goals with the ngrok Kubernetes Operator. ## ngrok Kubernetes CRDs

CloudEndpoint

Centrally managed endpoints in the cloud that can be used to route traffic to Agent Endpoints.

AgentEndpoint

Public or internal Endpoints created by Secure Tunnel agents.

NgrokTrafficPolicy

Configuration language that offers you the flexibility to filter, match, manage, and orchestrate traffic to your endpoints.

BoundEndpoint

Project ngrok endpoints into one or more Kubernetes clusters as Services to enable secure cluster to cluster networking without exposing them to public traffic.

Domain

A Domain's primary responsibility is to enable you to create public endpoints with a hostname matching the domain.

KubernetesOperator

Use ngrok with your Kubernetes clusters.

IPPolicy

A policy to restrict access to an endpoint based on IP that can be used across endpoints.

## Resource status and conditions The ngrok Kubernetes Operator uses standard Kubernetes conditions to report the state of resources. Each resource includes a `status.conditions` array that provides visibility into the current state and any issues that may need attention. ### Common condition types The following table summarizes the condition types available across different CRD resources: | CRD | Condition Type | Purpose | | - | - | - | | **Domain** | `Ready` | Overall domain readiness | | | `DomainCreated` | Domain reservation status | | | `CertificateReady` | TLS certificate status | | | `DNSConfigured` | DNS configuration status | | | `Progressing` | Domain provisioning in progress | | **IPPolicy** | `Ready` | Overall policy readiness | | | `IPPolicyCreated` | Policy creation status | | | `RulesConfigured` | Rules configuration status | | **CloudEndpoint** | `Ready` | Overall endpoint readiness | | | `CloudEndpointCreated` | Endpoint creation status | | | `DomainReady` | Associated domain status | | **AgentEndpoint** | `Ready` | Overall endpoint readiness | | | `EndpointCreated` | Endpoint creation status | | | `TrafficPolicyApplied` | Traffic policy application status | | | `DomainReady` | Associated domain status | | **BoundEndpoint** | `Ready` | Overall bound endpoint readiness | | | `ServicesCreated` | Target and upstream services creation status | | | `ConnectivityVerified` | Upstream connectivity verification status | ### Checking resource status You can use kubectl to check the status of your resources: ```bash theme={null} # Check if a resource is ready kubectl get -o jsonpath='{.status.conditions[?(@.type=="Ready")]}' # Wait for a resource to become ready kubectl wait --for=condition=Ready / --timeout=60s # List all resources with ready status kubectl get -A -o custom-columns=\ NAME:.metadata.name,\ NAMESPACE:.metadata.namespace,\ READY:.status.conditions[?(@.type==\'Ready\')].status,\ REASON:.status.conditions[?(@.type==\'Ready\')].reason ``` ### Understanding condition fields Each condition includes the following fields: * **type**: The condition type (for example, `Ready`, `DomainCreated`) * **status**: `True`, `False`, or `Unknown` * **reason**: A programmatic identifier for the condition state * **message**: A human-readable description of the condition * **lastTransitionTime**: When the condition last changed state * **observedGeneration**: The resource generation this condition reflects For detailed information about the status fields and conditions for each CRD, refer to the individual CRD documentation pages linked above. # IPPolicy Source: https://ngrok.com/docs/gateway/k8s/crds/ippolicy Learn how to use IP Policies to control traffic through your K8s endpoints. ## IPPolicy custom resource ### **apiVersion:** `ingress.k8s.ngrok.com/v1alpha1` ### **kind:** `IPPolicy` IP Policies define rules that allow or deny traffic based on the source IP of connections to your ngrok endpoints. These policies can be attached to endpoints via Traffic Policy configurations to control access based on IP addresses and CIDR ranges. IP Policies consist of one or more rules, where each rule specifies: * An action (`allow` or `deny`) * A CIDR range to match against * An optional description ## IPPolicy structure and types The following outlines the high level structure and typings of an `IPPolicy` ```yaml theme={null} apiVersion: ingress.k8s.ngrok.com/v1alpha1 kind: IPPolicy metadata: name: namespace: spec: description: # optional, default: "" metadata: # optional, default: "{"owned-by":"kubernetes-ingress-controller"}" rules: <[]Object> # required - action: # required, enum: "allow" | "deny" cidr: # required description: # optional metadata: # optional ``` ## IPPolicy fields The following sections outline each field of the `IPPolicy` custom resource, whether they are required, what their default values are (if applicable), and a description of their purpose/constraints. ### `spec` `spec` defines the desired state of the `IPPolicy` **Type:** `Object` **Required:** yes **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.description`](#specdescription) | `string` | no | `""` | Human-readable description for this `IPPolicy` to help identify/describe it | | [`spec.metadata`](#specmetadata) | `string` | no | `"{"owned-by":"kubernetes-ingress-controller"}"` | String of arbitrary data associated with the object in the ngrok API/Dashboard | | [`spec.rules`](#specrules) | `[]Object` | yes | none | List of rules that define which IPs/CIDR ranges to allow or deny | ### `spec.description` Human-readable description of this IP Policy that can be used to help identify/describe it. **Type:** `string` **Required:** no **Default:** `""` ### `spec.metadata` String of arbitrary data associated with the object in the ngrok API/Dashboard. **Type:** `string` **Required:** no **Default:** `"{"owned-by":"kubernetes-ingress-controller"}"` ### `spec.rules` List of rules that define which IP addresses or CIDR ranges should be allowed or denied access. Each rule is evaluated independently, and the most specific matching rule applies. **Type:** `[]Object` **Required:** yes **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.rules[].action`](#specrulesaction) | `string` (enum) | yes | none | Whether to allow or deny traffic from the specified CIDR range | | [`spec.rules[].cidr`](#specrulescidr) | `string` | yes | none | IP address or CIDR range to match against | | [`spec.rules[].description`](#specrulesdescription) | `string` | no | `""` | Human-readable description for this rule | | [`spec.rules[].metadata`](#specrulesmetadata) | `string` | no | none | String of arbitrary data associated with the rule in the ngrok API/Dashboard | ### `spec.rules[].action` Specifies whether to allow or deny traffic from the IP addresses matching this rule's CIDR range. **Type:** `string` (enum) **Required:** yes **Default:** none **Allowed Values:** `"allow"`, `"deny"` ### `spec.rules[].cidr` IP address or CIDR range to match against the source IP of incoming connections. This must be a valid IPv4 or IPv6 CIDR notation. **Type:** `string` **Required:** yes **Default:** none **Examples:** * Single IP: `192.168.1.1/32` * Subnet: `10.0.0.0/24` * IPv6: `2001:db8::/32` ### `spec.rules[].description` Human-readable description of this rule that can be used to help identify/describe it. **Type:** `string` **Required:** no **Default:** `""` ### `spec.rules[].metadata` String of arbitrary data associated with this rule in the ngrok API/Dashboard. **Type:** `string` **Required:** no **Default:** none ## Status fields The `IPPolicy` resource includes status information that reflects the current state of the policy in the ngrok system. ### `status.id` The ngrok API ID for this IP Policy resource. **Type:** `string` **Example:** `ipp_34iZT0muBP3kpcAXxGjM4uFlgDK` ### `status.rules` Array of rules with their ngrok API IDs. Each rule status includes the rule ID, CIDR, and action that was configured. **Type:** `[]Object` **Example:** ```yaml theme={null} rules: - id: ipr_34iZT1AbCdEfGhIjKlMnOpQrStU cidr: 162.236.158.182/32 action: allow ``` ### `status.conditions` Standard Kubernetes conditions that indicate the state of the IP Policy. **Type:** `[]Condition` Each condition includes: * `type` - The condition type * `status` - `True`, `False`, or `Unknown` * `reason` - A programmatic identifier for the condition state * `message` - A human-readable description * `lastTransitionTime` - When the condition last changed * `observedGeneration` - The resource generation this condition applies to #### Condition types ##### `Ready` Indicates whether the IP Policy is fully operational and ready to use. **Status Values:** * `True` - IP Policy is active and ready * `False` - IP Policy is not ready (see reason for details) **Reasons:** | Reason | Status | Meaning | | - | - | - | | `IPPolicyActive` | `True` | IP Policy is successfully created and active | | `IPPolicyRulesConfigurationError` | `False` | One or more rules failed to configure | | `IPPolicyCreationFailed` | `False` | Failed to create the IP Policy in ngrok | | `IPPolicyInvalidCIDR` | `False` | One or more rules have invalid CIDR notation | **Example:** ```yaml theme={null} conditions: - type: Ready status: "True" reason: IPPolicyActive message: "IP Policy is active" lastTransitionTime: "2025-10-29T00:39:16Z" observedGeneration: 1 ``` ##### `IPPolicyCreated` Indicates whether the IP Policy was successfully created in the ngrok API. **Status Values:** * `True` - IP Policy was successfully created * `False` - Failed to create IP Policy **Reasons:** | Reason | Status | Meaning | | - | - | - | | `IPPolicyCreated` | `True` | IP Policy successfully created | | `IPPolicyCreationFailed` | `False` | API call to create IP Policy failed | **Example:** ```yaml theme={null} conditions: - type: IPPolicyCreated status: "True" reason: IPPolicyCreated message: "IP Policy successfully created" lastTransitionTime: "2025-10-29T00:39:16Z" observedGeneration: 1 ``` ##### `RulesConfigured` Indicates whether all rules have been successfully configured for the IP Policy. **Status Values:** * `True` - All rules are configured * `False` - One or more rules failed to configure **Reasons:** | Reason | Status | Meaning | | - | - | - | | `IPPolicyRulesConfigured` | `True` | All rules successfully configured | | `IPPolicyRulesConfigurationError` | `False` | One or more rules failed validation or creation | | `IPPolicyInvalidCIDR` | `False` | One or more rules have invalid CIDR notation | **Example:** ```yaml theme={null} conditions: - type: RulesConfigured status: "True" reason: IPPolicyRulesConfigured message: "All rules configured for IP Policy" lastTransitionTime: "2025-10-29T00:39:16Z" observedGeneration: 1 ``` ## Status examples ### Successfully created IP Policy ```yaml theme={null} status: id: ipp_34iZT0muBP3kpcAXxGjM4uFlgDK rules: - id: ipr_34iZT1AbCdEfGhIjKlMnOpQrStU cidr: 162.236.158.182/32 action: allow conditions: - type: IPPolicyCreated status: "True" reason: IPPolicyCreated message: "IP Policy successfully created" lastTransitionTime: "2025-10-29T00:39:16Z" observedGeneration: 1 - type: RulesConfigured status: "True" reason: IPPolicyRulesConfigured message: "All rules configured for IP Policy" lastTransitionTime: "2025-10-29T00:39:16Z" observedGeneration: 1 - type: Ready status: "True" reason: IPPolicyActive message: "IP Policy is active" lastTransitionTime: "2025-10-29T00:39:16Z" observedGeneration: 1 ``` ### IP Policy with Invalid CIDR ```yaml theme={null} status: conditions: - type: IPPolicyCreated status: "False" reason: IPPolicyCreationFailed message: "Failed to create IP Policy: invalid CIDR notation in rule" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 - type: RulesConfigured status: "False" reason: IPPolicyInvalidCIDR message: "Invalid CIDR range: 192.168.1.0/42 (prefix length must be between 0 and 32 for IPv4)" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 - type: Ready status: "False" reason: IPPolicyInvalidCIDR message: "IP Policy cannot be activated due to invalid CIDR in rules" lastTransitionTime: "2025-10-29T00:39:15Z" observedGeneration: 1 ``` ## Checking IP Policy status You can check the status of an IP Policy using kubectl: ```bash theme={null} # Check if IP Policy is ready kubectl get ippolicy whitelist-specific-ip -o jsonpath='{.status.conditions[?(@.type=="Ready")]}' # Watch for IP Policy to become ready kubectl wait --for=condition=Ready ippolicy/whitelist-specific-ip --timeout=60s # Get all IP Policies with their ready status kubectl get ippolicies -A -o custom-columns=\ NAME:.metadata.name,\ NAMESPACE:.metadata.namespace,\ READY:.status.conditions[?(@.type==\'Ready\')].status,\ REASON:.status.conditions[?(@.type==\'Ready\')].reason # View all rules and their IDs kubectl get ippolicy whitelist-specific-ip -o jsonpath='{.status.rules[*]}' ``` ## Example IPPolicies ### Allow specific IP address The following `IPPolicy` allows traffic only from a specific IP address: ```yaml theme={null} apiVersion: ingress.k8s.ngrok.com/v1alpha1 kind: IPPolicy metadata: name: whitelist-specific-ip namespace: default spec: description: IP Policy to whitelist specific IP address rules: - action: allow cidr: 162.236.158.182/32 description: Allow access from specific IP address ``` ### Allow subnet with multiple rules The following `IPPolicy` allows traffic from a corporate network subnet: ```yaml theme={null} apiVersion: ingress.k8s.ngrok.com/v1alpha1 kind: IPPolicy metadata: name: corporate-network-access namespace: production spec: description: Allow access from corporate network rules: - action: allow cidr: 10.0.0.0/16 description: Allow access from main corporate subnet - action: allow cidr: 172.16.0.0/12 description: Allow access from corporate VPN range ``` ### Deny specific IP addresses The following `IPPolicy` denies traffic from specific IP addresses: ```yaml theme={null} apiVersion: ingress.k8s.ngrok.com/v1alpha1 kind: IPPolicy metadata: name: blocklist namespace: default spec: description: Block traffic from malicious IPs rules: - action: deny cidr: 192.0.2.1/32 description: Blocked IP from abuse reports - action: deny cidr: 198.51.100.0/24 description: Blocked subnet from attack attempts ``` # KubernetesOperator Source: https://ngrok.com/docs/gateway/k8s/crds/kubernetesoperator Reference documentation for the KubernetesOperator custom resource that synchronizes ngrok operator state with the ngrok API. ## KubernetesOperator custom resource ### **apiVersion:** `ngrok.k8s.ngrok.com/v1alpha1` ### **kind:** `KubernetesOperator` The `KubernetesOperator` custom resource handles synchronizing the state of your ngrok Kubernetes Operator instance with the ngrok API. A `KubernetesOperator` resource is created automatically when you install the ngrok Operator with Helm and has configuration that matches your installation options from Helm so that you can identify it in the ngrok cloud. ## KubernetesOperator structure and types The following outlines the high level structure and typings of a `KubernetesOperator` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: KubernetesOperator metadata: name: namespace: spec: description: # optional, default: "Created by the ngrok-operator" metadata: # optional, default: "{"owned-by":"ngrok-operator"}" enabledFeatures: <[]string> # optional region: # required, default: "global" deployment: # optional name: # required namespace: # required version: # required binding: # optional endpointSelectors: <[]string> # required ingressEndpoint: # optional tlsSecretName: # required, default: "default-tls" drain: # optional policy: # optional, default: "Retain" ``` ## KubernetesOperator fields The following sections outline each field of the `KubernetesOperator` custom resource, whether they are required, what their default values are (if applicable), and a description of their purpose/constraints. ### `spec` `spec` defines the desired state of the `KubernetesOperator` **Type:** `Object` **Required:** yes **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.description`](#specdescription) | `string` | no | `"Created by the ngrok-operator"` | Human-readable description for this `KubernetesOperator` to help identify/describe it | | [`spec.metadata`](#specmetadata) | `string` | no | `"{"owned-by":"ngrok-operator"}"` | String of arbitrary data associated with the object in the ngrok API/Dashboard | | [`spec.enabledFeatures`](#specenabledfeatures) | `[]string` (enum) | no | none | List of the features that are enabled for this instance of the ngrok Kubernetes Operator | | [`spec.region`](#specregion) | `string` | yes | `"global"` | The ngrok region in which the ingress for this Operator is served | | [`spec.deployment`](#specdeployment) | `Object` | no | none | Deployment information for this instance of the ngrok Kubernetes Operator | | [`spec.binding`](#specbinding) | `Object` | no | none | Configuration of the binding feature for this instance of the ngrok Kubernetes Operator | | [`spec.drain`](#specdrain) | `Object` | no | none | Configuration of the drain behavior during operator uninstall | ### `spec.description` Human-readable description of this object that can be used to help identify/describe it. **Type:** `string` **Required:** no **Default:** `"Created by the ngrok-operator"` ### `spec.metadata` String of arbitrary data associated with the object in the ngrok API/Dashboard. **Type:** `string` **Required:** no **Default:** `"{"owned-by":"ngrok-operator"}"` ### `spec.enabledFeatures` List of the features that are enabled for this instance of the ngrok Kubernetes Operator **Type:** `[]string` **Required:** no **Default:** none **Allowed Values:** `"ingress"`, `"gateway"`, `"bindings"` ### `spec.region` The ngrok region in which the ingress for this Operator is served. **Type:** `string (enum)` **Required:** no **Default:** `"global"` **Allowed Values:** `"au"`, `"eu"`, `"ap"`, `"us"`, `"jp"`, `"in"`, `"sa"`, `"global"` ### `spec.deployment` Deployment information for this instance of the ngrok Kubernetes Operator. **Type:** `Object` **Required:** no **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.deployment.name`](#specdeploymentname) | `string` | yes | none | The name of the Kubernetes `Deployment` for this intance of the ngrok Kubernetes Operator | | [`spec.deployment.namespace`](#specdeploymentnamespace) | `string` | yes | none | The namespace of the Kubernetes `Deployment` for this intance of the ngrok Kubernetes Operator | | [`spec.deployment.version`](#specdeploymentversion) | `string` | yes | none | The version of the Kubernetes `Deployment` for this intance of the ngrok Kubernetes Operator | ### `spec.deployment.name` The name of the Kubernetes `Deployment` for this intance of the ngrok Kubernetes Operator **Type:** `string` **Required:** yes **Default:** none ### `spec.deployment.namespace` The namespace of the Kubernetes `Deployment` for this intance of the ngrok Kubernetes Operator **Type:** `string` **Required:** yes **Default:** none ### `spec.deployment.version` The version of the Kubernetes `Deployment` for this intance of the ngrok Kubernetes Operator **Type:** `string` **Required:** yes **Default:** none ### `spec.binding` Configuration of the binding feature for this intance of the ngrok Kubernetes Operator **Type:** `Object` **Required:** no **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.binding.endpointSelectors`](#specbindingendpointselectors) | `[]string` | yes | none | List of CEL expressions that determine which kubernetes-bound Endpoints will be created by the Operator | | [`spec.binding.ingressEndpoint`](#specbindingingressendpoint) | `string` | no | none | The public ingress endpoint for this Kubernetes Operator | | [`spec.binding.tlsSecretName`](#specbindingtlssecretname) | `string` | yes | none | The name of the Kubernetes Secret that contains the TLS private/public keys to use for the ngrok forwarding endpoint | ### `spec.binding.endpointSelectors` List of cel expression that determine which kubernetes-bound Endpoints will be created by the Operator **Type:** `[]string` **Required:** yes **Default:** none ### `spec.binding.ingressEndpoint` The public ingress endpoint for this Kubernetes Operator **Type:** `string` **Required:** no **Default:** none ### `spec.binding.tlsSecretName` The name of the Kubernetes Secret that contains the TLS private/public keys to use for the ngrok forwarding endpoint **Type:** `string` **Required:** yes **Default:** `"default-tls"` ### `spec.drain` Configuration of the drain behavior during operator uninstall. For more information, see [Uninstalling the Operator](/docs/gateway/k8s/installation/uninstall). **Type:** `Object` **Required:** no **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.drain.policy`](#specdrainpolicy) | `string` (enum) | no | `"Retain"` | Determines whether to delete ngrok API resources or just remove finalizers during drain | ### `spec.drain.policy` Determines how ngrok API resources are handled when the operator is uninstalled. **Type:** `string (enum)` **Required:** no **Default:** `"Retain"` **Allowed Values:** `"Delete"`, `"Retain"` * `Retain`: removes finalizers from custom resources but preserves ngrok API resources in your account * `Delete`: deletes each custom resource and waits for the operator to clean up the corresponding ngrok API resource ## Status fields The `KubernetesOperator` resource includes status information that reflects the current state of the operator and its registration with the ngrok API. ### `status.id` The unique identifier for this Kubernetes Operator in the ngrok API. **Type:** `string` ### `status.uri` The URI for this Kubernetes Operator in the ngrok API. **Type:** `string` ### `status.registrationStatus` The status of the registration of this Kubernetes Operator with the ngrok API. **Type:** `string (enum)` **Allowed Values:** `"registered"`, `"error"`, `"pending"` ### `status.registrationErrorCode` The ngrok error code if registration failed. **Type:** `string` **Pattern:** `ERR_NGROK_` ### `status.errorMessage` A free-form error message if the registration status is `error`. **Type:** `string` ### `status.enabledFeatures` String representation of the features enabled for this Kubernetes Operator. **Type:** `string` ### `status.bindingsIngressEndpoint` The URL that the operator uses to talk to the ngrok network when forwarding traffic for kubernetes-bound endpoints. **Type:** `string` ### `status.drainStatus` Indicates the current state of the drain process during uninstall. **Type:** `string (enum)` **Allowed Values:** `"pending"`, `"draining"`, `"completed"`, `"failed"` ### `status.drainMessage` Human-readable information about the current drain state. **Type:** `string` ### `status.drainProgress` Indicates how many resources have been drained versus total. Format: `X/Y` where X is the number processed (completed + failed) and Y is total. **Type:** `string` **Example:** `"5/10"` ### `status.drainErrors` List of the most recent errors encountered during drain. **Type:** `[]string` ## Status examples ### Registered Operator ```yaml theme={null} status: id: k8sop_2example123 uri: /kubernetes_operators/k8sop_2example123 registrationStatus: registered enabledFeatures: ingress,gateway,bindings ``` ### Operator draining during uninstall ```yaml theme={null} status: id: k8sop_2example123 uri: /kubernetes_operators/k8sop_2example123 registrationStatus: registered drainStatus: draining drainMessage: "Draining resources" drainProgress: "3/10" ``` ### Drain completed with errors ```yaml theme={null} status: id: k8sop_2example123 uri: /kubernetes_operators/k8sop_2example123 registrationStatus: registered drainStatus: failed drainMessage: "Drain completed with errors" drainProgress: "10/10" drainErrors: - "failed to delete CloudEndpoint my-endpoint: HTTP 500: internal server error" ``` ## Example KubernetesOperator ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: KubernetesOperator metadata: name: ngrok-operator namespace: ngrok-operator spec: description: Created by ngrok-operator metadata: '{"owned-by":"ngrok-operator"}' binding: endpointSelectors: - "true" ingressEndpoint: kubernetes-binding-ingress.ngrok.io:443 tlsSecretName: ngrok-operator-default-tls deployment: name: ngrok-operator namespace: ngrok-operator version: 0.15.0 enabledFeatures: - ingress - gateway - bindings region: global ``` # NgrokTrafficPolicy Source: https://ngrok.com/docs/gateway/k8s/crds/ngroktrafficpolicy Reference documentation for the NgrokTrafficPolicy Kubernetes custom resource for defining reusable Traffic Policy configurations. ## NgrokTrafficPolicy custom resource ### **apiVersion:** `ngrok.k8s.ngrok.com/v1alpha1` ### **kind:** `NgrokTrafficPolicy` The `NgrokTrafficPolicy` resource allows you to define your Traffic Policy configuration in a resource that can by used by reference on other resources such as `Ingress`, `Gateway`, `CloudEndpoint`, and `AgentEndpoint`. `Ingress` and `Gateway` have no direct method for configuring an ngrok Traffic Policy, so to use a Traffic Policy with those resources, you would create an `NgrokTrafficPolicy` resource and add the `k8s.ngrok.com/traffic-policy` annotation on your `Ingress`/`Gateway` to add the Traffic Policy configuration to it. `CloudEndpoint` and `AgentEndpoint` both give you the option to either define the Traffic Policy configuration inline, or as a reference to an `NgrokTrafficPolicy` resource, enabling the efficient reuse of common Traffic Policies such as enforcing ratelimiting/authentication across all of your endpoints without needing to redefine the Traffic Policy on each individual resource that needs to use it. For more information about writing ngrok Traffic Policies, refer to the [Traffic Policy section](/docs/gateway/traffic-policy/) ## NgrokTrafficPolicy structure and types The following outlines the high level structure and typings of a `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: namespace: spec: policy: # required ``` ## NgrokTrafficPolicy fields The following sections outline each field of the `NgrokTrafficPolicy` custom resource, whether they are required, what their default values are (if applicable), and a description of their purpose/constraints. ### `spec` `spec` defines the desired state of the `NgrokTrafficPolicy` **Type:** `Object` **Required:** yes **Default:** none **Fields:** | Field Name | Type | Required | Default | Description | | - | - | - | - | - | | [`spec.policy`](#specpolicy) | `json.RawMessage` | yes | none | Configuration for the Traffic Policy rules and actions | ### `spec.policy` Defines the phases, rules, actions, and expressions of the Traffic Policy configuration. **Type:** `json.RawMessage` **Required:** yes **Default:** none ## Example NgrokTrafficPolicy The following `NgrokTrafficPolicy` will return a static response when the request query parameter `?debug=true` is present. On its own, this `NgrokTrafficPolicy` will not do anything, and must be attached to an `Ingress`/`Gateway`/`CloudEndpoint`/`AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-ngroktrafficpolicy namespace: default spec: policy: on_http_request: - name: debug expressions: - req.url.query_params['debug'][0]=='true' actions: - type: custom-response config: body: Custom response from Traffic Policy headers: content-type: text/plain status_code: 200 ``` # Custom ngrok Annotations Source: https://ngrok.com/docs/gateway/k8s/guides/annotations Reference documentation for custom annotations used with the ngrok Kubernetes Operator to extend functionality of Ingress and Gateway resources. The following custom annotations can be used with the ngrok Kubernetes Operator to extend the functionality of various resources. ## `k8s.ngrok.com/traffic-policy` Allows you to supply an `NgrokTrafficPolicy` to an `Ingress` or `Gateway` resource. For more information about using Traffic Policies with `Ingresses` and Gateway API, see [the using Ingresses page](/docs/gateway/k8s/guides/using-ingresses) and [the using Gateway API page](/docs/gateway/k8s/guides/using-gwapi). **Usable On:** `Ingress`/`Gateway` **Example:** ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: custom-response-example namespace: default spec: policy: on_http_request: - name: custom-response-example expressions: - req.url.path.startsWith('/custom-response') actions: - type: custom-response config: status_code: 200 body: "Custom response from your Traffic Policy" headers: content-type: text/plain --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: "custom-response-example" name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-ingresses.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ## `k8s.ngrok.com/mapping-strategy` Allows you to control the way that the Operator translates `Ingress`, `Gateway` and `LoadBalancer` type `Services` into ngrok custom resources. By default, these resources will be translated into `CloudEndpoint` and `AgentEndpoint` custom resources, but you can opt to use the deprecated translation into `HTTPSEdge` resources before those are removed. For more information about how the Operator translates resources, see [the using Ingresses page](/docs/gateway/k8s/guides/using-ingresses) and [the using Gateway API page](/docs/gateway/k8s/guides/using-gwapi). **Usable On:** `Ingress`/`Gateway` **Allowed Values:** `"endpoints"`/`"edges"` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/mapping-strategy: "edges" name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-ingresses.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ## `k8s.ngrok.com/pooling-enabled` Allows you to configure whether pooling should be enabled/disabled when the Operator translates `Ingress` and `Gateway` resources into `CloudEndpoints`. By default, translated `CloudEndpoint` resources will have `poolingEnabled` set to `false`, but this annotation allows you to enable it. For more information about endpoint pooling, see [the pooling page](/docs/gateway/k8s/guides/pooling) **Usable On:** `Ingress`/`Gateway` **Allowed Values:** `"true"`/`"false"` **Example:** ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/pooling-enabled: "true" name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-ingresses.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` Due to how Kubernetes handles annotation values, the value of k8s.ngrok.com/pooling-enabled must be a string ("true"). Setting the value to true without surrounding it with quotes will not work. ## `k8s.ngrok.com/bindings` Allows you to specify the binding that should be used when the Operator translates `Ingress` and `Gateway` resources into `CloudEndpoints`. By default, a binding of `"public"` will be used, but this annotation allows you to create internal or Kubernetes bound `CloudEndpoints` instead. For more information about bindings, see [the bindings page](/docs/gateway/k8s/guides/bindings) **Usable On:** `Ingress`/`Gateway` **Allowed Values:** `"internal"`/`"public"`/`"kubernetes"` **Example:** ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress namespace: default annotations: k8s.ngrok.com/bindings: "internal" spec: ingressClassName: ngrok rules: - host: example-domain.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ## `k8s.ngrok.com/app-protocols` Allows you to specify the protocol of ports on your service. Accepts a value that is a json string which maps the name of the ports on your `Service` to `http` or `https`. This annotation is optional, and when not used or a named port is not included in the annotation's value then the ngrok Kubernetes Operator will default to using `http` for that port. **Usable On:** `Service` **Example:** The following example will mark the following service as having one port that is `https` and one port that is `http`. ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: example-service labels: app: example-app annotations: k8s.ngrok.com/app-protocols: '{"https-port":"https","http-port":"http"}' spec: ports: - name: https-port port: 443 protocol: TCP targetPort: 8443 - name: http-port port: 80 protocol: TCP targetPort: 8080 selector: app: example-app ``` # Binding Endpoints in the ngrok Kubernetes Operator Source: https://ngrok.com/docs/gateway/k8s/guides/bindings Learn about ngrok's endpoint bindings and how to use them in the ngrok Kubernetes Operator. ngrok supports the concept of endpoint bindings which allow you to control how your endpoints are accessed. This page gives a brief overview of each type of binding as well as how they are used with the ngrok Kubernetes Operator. With the Kubernetes Operator, bindings can be used on the `CloudEndpoint` and `AgentEndpoint` custom resources. ## Public binding Endpoints with a public binding have a publicly addressable URL that receives traffic from the internet via the ngrok cloud service's global points of presence. for example, These endpoints might use an ngrok subdomain or a custom domain. Public endpoint URLs must: * Use either a Domain or TCP Address * Use the public binding Example URLs: * [https://example.ngrok.app](https://example.ngrok.app) * [https://blog.example.com](https://blog.example.com) Example `CloudEndpoint` (the same `spec.bindings` configuration works for `AgentEndpoint` as well) ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-cloud-endpoint.ngrok.io bindings: - public description: Public Endpoint Created by the ngrok-operator trafficPolicy: policy: on_http_request: - actions: - type: forward-internal config: url: https://my-k8s-service.internal ``` The public binding and URL for the above `CloudEndpoint` make it accessible on the public internet at `https://example-cloud-endpoint.ngrok.io` [Learn more about Public Endpoints](/docs/gateway/endpoints/public-endpoints/). ## Internal binding Endpoints with an internal binding can only receive traffic forwarded to them from other Endpoints in your ngrok account via the forward-internal Traffic Policy action. Example URLs: * [https://example.internal](https://example.internal) * tcp\://ssh.internal:22 Example `AgentEndpoint` (the same `spec.bindings` configuration works for `CloudEndpoint` as well) ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: bindings: - internal description: Internal Endpoint Created by the ngrok-operator url: https://my-k8s-service.internal upstream: url: http://my-service.default:80 ``` The internal binding and URL on the above internal `AgentEndpoint` make it not accessible via the public internet. It can be accessed by using the `forward-internal` action from a Traffic Policy on other endpoints. This is not limited to the Kubernetes cluster that you installed the Operator in, and you could, for example, create a public endpoint using the ngrok dashboard/API/CLI that forwards to this endpoint. [Learn more about Internal Endpoints](/docs/gateway/endpoints/internal-endpoints/). ## Kubernetes binding The ngrok Operator does not create the target namespace for you. Before creating a Kubernetes-bound endpoint, ensure the namespace referenced in the URL (the `` in `://.`) already exists in the cluster — otherwise the `BoundEndpoint` will fail to project with [`ERR_NGROK_20003`](/docs/errors/err_ngrok_20003) and a `namespaces "" not found` message in its status. You can create the namespace with `kubectl create ns `. Endpoints with a kubernetes binding are private endpoints that are only available inside of Kubernetes clusters where you installed the ngrok Kubernetes Operator. Example URLs: * [http://service.namespace](http://service.namespace) * tcp\://db.controlplane:5432 Public endpoint URLs must: * Use the kubernetes binding * The `spec.url` must follow the format `://.` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint namespace: my-namespace spec: bindings: - kubernetes description: Kubernetes Bound Endpoint Created by the ngrok-operator url: http://my-bound-service.my-namespace upstream: url: http://my-service.my-namespace:80 ``` The kubernetes binding and URL for the above `AgentEndpoint` make it a Kubernetes bound endpoint. Endpoints with a binding of `kubernetes` can be made available as projected services in any cluster running the ngrok Operator. These endpoints are not accessible on the public internet, but can be used to enable services in your Kubernetes cluster to access the endpoint by making requests to a projected `Service` that is created for the endpoint. ### How Kubernetes bound endpoints work An endpoint with a Kubernetes binding will cause instances of the ngrok Kubernetes Operator to create a `BoundEndpoint` custom resource for the endpoint. The `BoundEndpoint` will cause the endpoint to be projected into the cluster as a `Service` resource. This enables many different use-cases such as enabling secure communication between different Kubernetes clusters using Kubernetes bound endpoints. For example, the following endpoint: ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint namespace: my-namespace spec: bindings: - kubernetes description: Kubernetes Bound Endpoint Created by the ngrok-operator url: http://my-bound-service.my-namespace upstream: url: http://my-service.my-namespace:80 ``` Will cause the following `BoundEndpoint` to be created by the Operator. You can also configure the same options on `CloudEndpoint` resources. ```yaml theme={null} apiVersion: bindings.k8s.ngrok.com/v1alpha1 kind: BoundEndpoint metadata: name: ngrok-22680758-eb09-576e-a7ac-dc3728458dfc namespace: ngrok-operator spec: endpointURL: http://my-bound-service.my-namespace:80 port: 10001 scheme: http target: metadata: {} namespace: my-namespace port: 80 protocol: TCP service: my-service ``` Which in-turn causes the Operator to project the following `Service` into your cluster ```yaml theme={null} apiVersion: v1 kind: Service metadata: labels: app.kubernetes.io/managed-by: ngrok-operator bindings.k8s.ngrok.com/endpoint-binding-name: ngrok-22680758-eb09-576e-a7ac-dc3728458dfc bindings.k8s.ngrok.com/endpoint-binding-namespace: ngrok-operator name: my-bound-service namespace: my-namespace spec: externalName: ngrok-22680758-eb09-576e-a7ac-dc3728458dfc.ngrok-operator.svc.cluster.local ports: - name: http port: 80 protocol: TCP targetPort: 80 sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10800 type: ExternalName ``` When anything in the cluster makes a request to the `my-bound-service.my-namespace` `Service` it will hit the above `AgentEndpoint` and ultimately be routed to the `my-service.my-namespace` `Service`. This can be used within a single cluster to use ngrok Traffic Policies on your endpoints for `Service` to `Service` networking to perform operations such as rate-limiting, header manipulation, etc. Furthermore, the true power in Kubernetes bindings is allowing you to project a `Service` in one cluster to one or more other clusters running the ngrok Kubernetes Operator. When doing this, you can configure networking between different Kubernetes clusters without needing to expose any publicly accessible URLs that would need to require authentication to prevent unwanted access. This is not simply limited to Kubernetes clusters. You could for example, create a Kubernetes bound endpoint using the ngrok CLI/SDK to project workloads running on another system such as a Virtual Machine into your Kubernetes clusters. ### Kubernetes pod identity in Traffic Policy When a connection arrives on a Kubernetes-bound endpoint, ngrok makes the originating pod's metadata available as Traffic Policy variables under `conn.k8s.pod.*`. This enables policies that make access control decisions based on the Kubernetes workload initiating the connection, such as restricting access to pods in a specific namespace or matching on pod annotations. Pod identity variables are only available on endpoints with a `kubernetes` binding. They are not populated on public or internal endpoints. For a full reference of the available variables, error handling, and usage examples, see [Restricting Access by Kubernetes Pod Identity](/docs/gateway/k8s/guides/how-to/pod-identity). ### Enable bindings for the Operator By default, the kubernetes bindings feature is not enabled for the ngrok Operator. Without enabling the feature, you can still create endpoints like any of the above examples for `internal`/`public`/`kubernetes` endpoints, but the Operator will not create any `BoundEndpoint` resources for endpoints in your ngrok account with a Kubernetes binding. To enable the feature and allow your Operator to create `BoundEndpoints` in response to endpoints with a kubernetes binding, use the helm value `bindings.enabled`. For example, if you installed the ngrok Operator with the default name/namespace, the following command will enable bindings. ```bash theme={null} helm upgrade ngrok-operator -n ngrok-operator ngrok/ngrok-operator --reuse-values --set bindings.enabled=true ``` See the [Operator Helm page](/docs/gateway/k8s/installation/helm) for more information about the `bindings` value and all the other configuration options such as configuring custom annotations/labels on the projected services created by Kubernetes bound endpoints. ### Restrict Kubernetes bound endpoints By default, the Operator will create a `BoundEndpoint` in response to all endpoints in your account with a Kubernetes binding type. You can modify your Operator to only process a subset of them with the `bindings.endpointSelectors` Helm value. `bindings.endpointSelectors` is a list of cel expressions used to filter which kubernetes-bound endpoints should be projected into this cluster. It defaults to `["true"]` which causes it to process all endpoints with a Kubernetes type binding. This can be modified to anything you like to control which endpoints the Operator should process. This strategy can enable you to easily only project endpoints into certain clusters running the ngrok Kubernetes Operator. ## Bindings with `Ingresses` By default, creating an `Ingress` will create a `CloudEndpoint` that has a `public` binding, meaning that its URL is accessible via the public internet, and an `AgentEndpoint` with an `internal` binding, meaning that its URL is only accessible using the `forward-internal` Traffic Policy action. You can control the binding on the `CloudEndpoint` that will be created by this `Ingress` using the `k8s.ngrok.com/bindings` annotation. For more information about bindings, see [the bindings page](/docs/gateway/k8s/guides/bindings) The following `Ingress` will create a `CloudEndpoint` with an `internal` binding. **Note that with the internal binding, the URL must end in .internal** ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress namespace: default annotations: k8s.ngrok.com/bindings: "internal" spec: ingressClassName: ngrok rules: - host: example-ingress.internal http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ## Bindings with Gateway API By default, the `CloudEndpoint` resources that are created by the Operator from Gateway API config have a `public` binding, meaning that its URL is accessible via the public internet, and any created `AgentEndpoint` resources will have an `internal` binding, meaning that its URL is only accessible using the `forward-internal` Traffic Policy action. You can control the binding on the `CloudEndpoint` that will be created by using the `k8s.ngrok.com/bindings` annotation. For more information about bindings, see [the bindings page](/docs/gateway/k8s/guides/bindings) The following configuration will create a `CloudEndpoint` with an `internal` binding. the annotation can be used on the `Gateway` but not `HTTPRoute`. **Note that with the internal binding, the URL must end in .internal** ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/bindings: "internal" spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.internal" port: 443 protocol: HTTPS --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.internal parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: / backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 ``` # Kubernetes Custom White-Label Domains Source: https://ngrok.com/docs/gateway/k8s/guides/custom-domain Learn how to use custom domains with the ngrok Kubernetes Operator. In the Kubernetes Ingress and Gateway API specs, both ingresses and routes can have multiple rules with different hostnames. While standard ngrok domains are available for use immediately after reservation, custom white-label domains may require extra steps to set up. This guide outlines your options for getting custom white-label domains working with the ngrok Kubernetes Operator. ## Managed by Kubernetes If you have [ExternalDNS](https://github.com/kubernetes-sigs/external-dns) installed and configured, you can create an Ingress object to fully automate the process of reserving a domain in ngrok, updating DNS records with your DNS provider to the ngrok-provided CNAME record, and receiving a TLS certificate. If you aren't using ExternalDNS or a similar tool, you must manually create records for each Ingress or Gateway you provision. ### Ingress If you create an ingress object (k8s ingress) or route object (Gateway API) with a hostname that isn't a standard ngrok domain, the Operator will attempt to create a custom white-label domain for you. This domain will be reserved and registered with ngrok but you must configure its DNS records before you can use it. This will be registered in the ngrok API and also show up as a domain CRD. For example, if you create an ingress object like the one shown below, the Operator will attempt to reserve and register the domain `foo.bar.com` with ngrok: ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: rules: - host: foo.bar.com http: paths: - path: / pathType: Prefix backend: service: name: service1 port: number: 80 ``` This will be registered in the ngrok API and also show up as a domain CRD. The Operator will then wait for the DNS records to be configured for this domain. Once the DNS records are configured, the Operator will configure the endpoint to route traffic to the ingress or route object. You should be able to see the domain CRD via kubectl: ```shell theme={null} kubectl get domains.ingress.k8s.ngrok.com ``` For custom domains, the domain resource contains the CNAME target value that needs to be created for DNS resolution and certificates to work properly. This value is added to the ingress or route object's `status.loadBalancer.ingress` field. `kubectl get ingress -o yaml example-ingress` ```yaml theme={null} Status: loadBalancer: ingress: - hostname: abc123.def456.ngrok-cname.com ``` ### Gateway Using a custom domain with the Gateway API is similar to the Ingress example above. If you create the following Gateway, the ngrok-operator will reserve the domain for you: ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway spec: gatewayClassName: ngrok listeners: - allowedRoutes: kinds: - group: gateway.networking.k8s.io kind: HTTPRoute namespaces: from: All hostname: foo.bar.com name: http port: 443 protocol: HTTPS ``` It will also populate the Gateway's `status.addresses` field: ```yaml theme={null} status: addresses: - type: Hostname value: abc123.def456.ngrok-cname.com ``` ## Wait for the TLS certificate to provision Once you've reserved a custom domain and pointed your CNAME at the value above, ngrok provisions a TLS certificate from an ACME provider such as Let's Encrypt. This typically completes within a few minutes after your CNAME is resolvable, but DNS propagation or a slow DNS challenge can push it longer. While the certificate is still being provisioned, ngrok terminates HTTPS with a default ngrok wildcard certificate that does **not** match your custom hostname. If you send traffic to your endpoint over HTTPS during this window, clients will see a certificate name-mismatch warning (for example, `NET::ERR_CERT_COMMON_NAME_INVALID` in Chrome). This is expected during provisioning and clears as soon as the certificate is issued. The Domain CRD surfaces provisioning state through its [status conditions](/docs/gateway/k8s/crds/domain#status-fields). Check `CertificateReady`, `DNSConfigured`, and `Progressing` to see whether the certificate is still being issued or blocked on a DNS configuration error: ```shell theme={null} kubectl get domain -o jsonpath='{.status.conditions}' ``` You can also watch for the domain to become ready: ```shell theme={null} kubectl wait --for=condition=Ready domain/ --timeout=10m ``` If `CertificateReady` stays `False` with reason `ProvisioningError`, recheck the CNAME records you configured against the `status.cnameTarget` value on the domain. For wildcard custom domains, both CNAME records described in [TLS certificate provisioning](/docs/gateway/domains/tls-certificates/#wildcard-domains) must exist before the certificate can be issued. ## Externally managed Domains can also be created in the dashboard, or via the ngrok API or Terraform provider. If created externally, the Operator will discover the domain is already registered and won't interact with it unless you modify or delete the CRD itself. Deleting the ingress objects that use that host won't result in the domain CRD being deleted. # How to create endpoints in the ngrok Kubernetes Operator Source: https://ngrok.com/docs/gateway/k8s/guides/endpoint-types Learn how to create endpoints that accept `http://`, `https://`, `tcp://`, and `tls://` requests using the ngrok Kubernetes Operator. The ngrok Kubernetes Operator offers `CloudEndpoint` and `AgentEndpoint` as custom resource types that let you create endpoints in ngrok. The [using CRDs guide](/docs/gateway/k8s/guides/using-crds) goes over the details and differences about how to configure each one. This page covers the different types of endpoints that can be created using either `CloudEndpoint` or `AgentEndpoint`. ## HTTP endpoints The following examples showcase how you can create endpoints that accept `http://` cleartext requests. The example showcases an upstream that accepts `http://` requests, but you can also use `https://` for the upstream. For `AgentEndpoints`, the `upstream.url` scheme does not need to match the scheme of the public URL. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: https-agent-endpoint namespace: default spec: url: http://example-http-domain.ngrok.app upstream: url: http://example-service.default:8080 bindings: - public ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: http-cloud-endpoint spec: url: http://example-http-domain.ngrok.app bindings: - public trafficPolicy: policy: on_http_request: - actions: - type: forward-internal config: url: http://example-service.internal:8080 ``` ## HTTPS endpoints The following examples showcase how you can create endpoints that accept `https://` encrypted requests. For HTTPS endpoints, ngrok will automatically terminate TLS connections for your HTTPS endpoints. If you would like to control TLS termination yourself, you can use the [terminate-tls Traffic Policy action](/docs/gateway/traffic-policy/actions/terminate-tls/). The example showcases an upstream that accepts `https://` requests, but you can also use `http://` for the upstream. For `AgentEndpoints`, the `upstream.url` scheme does not need to match the scheme of the public URL. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: https-agent-endpoint namespace: default spec: url: https://example-http-domain.ngrok.app upstream: url: https://example-service.default:8443 bindings: - public ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: https-cloud-endpoint spec: url: https://example-https-domain.ngrok.app bindings: - public trafficPolicy: policy: on_http_request: - actions: - type: forward-internal config: url: https://example-service.internal:8443 ``` ## TCP endpoints The following examples showcase how you can create endpoints that accept `tcp://` requests. TCP Endpoints can be used to forward raw TCP traffic. In order to create a TCP Endpoint, you must first [reserve a TCP address through the ngrok dashboard](https://dashboard.ngrok.com/tcp-addresses). This step is not necessary for internal endpoints. See [the bindings page](/docs/gateway/k8s/guides/bindings) for more information about public/internal/k8s endpoints. The example showcases an upstream that accepts `tcp://` requests, but you can also use `tls://` for the upstream. For `AgentEndpoints`, the `upstream.url` scheme does not need to match the scheme of the public URL. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: tcp-agent-endpoint namespace: default spec: url: tcp://example-http-domain.ngrok.app upstream: url: tcp://example-service.default:8000 bindings: - public ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: tcp-cloud-endpoint spec: url: tcp://example-tcp-domain.ngrok.app bindings: - public trafficPolicy: policy: on_http_request: - actions: - type: forward-internal config: url: tcp://example-service.internal:8000 bindings: - public ``` ## TLS endpoints The following examples showcase how you can create endpoints that accept `tls://` requests. TLS endpoints enable you to deliver any network service that runs over a TLS-based protocol. TLS endpoints make no assumptions about the wrapped protocol being transported. For TLS endpoints, ngrok will not terminate the TLS connection by default and it is up to you to handle TLS termination in your upstream service. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: tls-agent-endpoint namespace: default spec: url: tls://example-tls-domain.ngrok.app upstream: url: tls://example-service.default:9000 bindings: - public ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: tls-cloud-endpoint spec: url: tls://example-tls-domain.ngrok.app bindings: - public trafficPolicy: policy: on_http_request: - actions: - type: forward-internal config: url: tls://example-service.internal:9000 bindings: - public ``` # Exposing the Kubernetes API Server with the ngrok Operator Source: https://ngrok.com/docs/gateway/k8s/guides/expose-kubernetes-api Learn how to expose the Kubernetes API server externally using the ngrok Kubernetes Operator, covering each Kubernetes authentication strategy and how to update your kubeconfig. Exposing the Kubernetes API server through ngrok lets you reach your cluster's control plane from anywhere—no VPN, no bastion host, no firewall rules required. Common use cases include: * Running `kubectl` commands remotely without VPN access * Enabling CI/CD pipelines outside the cluster to deploy resources * Giving teammates temporary access to a development cluster * Accessing clusters running in air-gapped or private networks The ngrok Operator creates a persistent, secure tunnel from your cluster to ngrok's global network. However, the **type of ngrok endpoint** you need depends on which Kubernetes authentication strategy your cluster uses, since some auth mechanisms rely on TLS client certificates that require end-to-end TLS passthrough. ## Prerequisites * The [ngrok Kubernetes Operator installed](/docs/gateway/k8s/installation/helm) in your cluster * `kubectl` configured with access to the cluster you want to expose ## Kubernetes authentication strategies As of Kubernetes 1.33, the following authentication strategies are available. From a `kubectl` client perspective they correspond to specific fields in your kubeconfig, which determines which ngrok endpoint type to use. | Authentication Strategy | kubeconfig Indicator | ngrok Compatible? | Required Endpoint Type | | - | - | - | - | | Bearer Token (Service Account, Static) | `token` or `tokenFile` field | ✅ Yes | HTTPS | | OIDC Token (via `exec` plugin) | `exec` block (for example, `kubelogin`) | ✅ Yes | HTTPS | | Cloud Provider Token (EKS, GKE, AKS) | `exec` block (for example, `aws`, `gke-gcloud-auth-plugin`) | ✅ Yes | HTTPS | | X.509 Client Certificate | `client-certificate-data` + `client-key-data` fields | ✅ Yes | TLS (passthrough) | | Exec Plugin (certificate output) | `exec` block producing `clientCertificateData` | ✅ Yes | TLS (passthrough) | | Authenticating Proxy | External proxy infrastructure | ⚠️ Architecture dependent | N/A | | Anonymous | No credentials | ✅ Yes (add ngrok-level auth!) | HTTPS | > \*Kubernetes can delegate authentication to a reverse proxy that sits in front of the API server. Since the proxy itself handles authentication—-not the API server or `kubectl`—-ngrok's role is limited to transporting traffic. Whether this works depends on where the proxy sits in your architecture relative to the ngrok tunnel. This topology is outside the scope of this guide. ### Why endpoint type matters **HTTPS endpoints** terminate TLS on ngrok's network. The `Authorization: Bearer ` header passes through transparently to the Kubernetes API server, so any token-based strategy works. **TLS endpoints** pass the raw TLS stream through to the upstream without ngrok terminating it. This means the full TLS handshake—including the client certificate—travels end-to-end from `kubectl` to the Kubernetes API server. This is required for X.509 client certificate authentication. If you use HTTPS with client certificate auth, ngrok will terminate TLS before the client cert reaches the API server and authentication will fail. ## Identifying your authentication strategy Open your kubeconfig (default location: `~/.kube/config`) and find the `users` section for the relevant cluster. ```bash theme={null} kubectl config view --minify ``` ### Bearer token Your kubeconfig has a `token` or `tokenFile` field directly under `user`: ```yaml theme={null} users: - name: my-user user: token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... ``` or ```yaml theme={null} users: - name: my-user user: tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token ``` → Use the **[HTTPS endpoint setup](#scenario-1-bearer-token-or-exec-plugin-producing-a-token-https)**. ### Exec credential plugin Your kubeconfig has an `exec` block under `user`. This is used by cloud providers (EKS, GKE, AKS) and OIDC login tools: ```yaml theme={null} # Amazon EKS users: - name: my-eks-user user: exec: apiVersion: client.authentication.k8s.io/v1beta1 command: aws args: - eks - get-token - --cluster-name - my-cluster ``` ```yaml theme={null} # Google GKE users: - name: my-gke-user user: exec: apiVersion: client.authentication.k8s.io/v1beta1 command: gke-gcloud-auth-plugin ``` ```yaml theme={null} # OIDC (e.g. kubelogin) users: - name: my-oidc-user user: exec: apiVersion: client.authentication.k8s.io/v1beta1 command: kubectl-oidc-login args: - get-token - --oidc-issuer-url=https://accounts.google.com - --oidc-client-id=my-client-id ``` Most exec plugins (including all cloud provider implementations) produce a bearer token, not a client certificate. Check the plugin documentation to confirm. If the plugin returns a token: → Use the **[HTTPS endpoint setup](#scenario-1-bearer-token-or-exec-plugin-producing-a-token-https)**. If the plugin returns `clientCertificateData` and `clientKeyData`: → Use the **[TLS passthrough setup](#scenario-2-x509-client-certificate-or-exec-plugin-producing-a-certificate-tls-passthrough)**. ### X.509 client certificate Your kubeconfig has `client-certificate-data` and `client-key-data` fields (or their file-based equivalents) under `user`: ```yaml theme={null} users: - name: my-user user: client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t... client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ==... ``` or ```yaml theme={null} users: - name: my-user user: client-certificate: /path/to/client.crt client-key: /path/to/client.key ``` → Use the **[TLS passthrough setup](#scenario-2-x509-client-certificate-or-exec-plugin-producing-a-certificate-tls-passthrough)**. ## Scenario 1: Bearer token or exec plugin producing a token (HTTPS) Use this scenario when your kubeconfig uses `token`, `tokenFile`, or an `exec` block that produces a bearer token (the most common case for EKS, GKE, AKS, and OIDC). ### Apply the AgentEndpoint manifest Create an `AgentEndpoint` that exposes the in-cluster Kubernetes API service. Replace `my-k8s-api.ngrok.app` with your reserved ngrok domain. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: kube-api-access namespace: default spec: url: https://my-k8s-api.ngrok.app:443 upstream: url: https://kubernetes.default.svc:443 # Optionally, you can add a traffic policy to restrict access to your endpoint # Remove the traffic policy below if not needed, or update the IP to the IP # you get when running `curl https://ipv4.ngrok.com` trafficPolicy: # You can add an inline traffic policy here to restrict # access to your endpoint, or you can reference an existing # traffic policy like so: # # targetRef: # name: my-traffic-policy # inline: on_tcp_connect: - actions: - config: allow: - 1.2.3.4/32 type: restrict-ips ``` Apply it: ```bash theme={null} kubectl apply -f kubernetes-api-endpoint.yaml ``` ### Update your kubeconfig Find the name of the cluster entry you want to update: ```bash theme={null} kubectl config get-clusters ``` Replace the server with your ngrok endpoint(ex: `https://my-k8s-api.ngrok.app`) ```bash theme={null} kubectl config set-cluster --server=https://my-k8s-api.ngrok.app ``` Remove the previous certificate data as ngrok will now be terminating HTTPS for you: ```bash theme={null} kubectl config unset clusters..certificate-authority-data ``` Alternatively, edit `~/.kube/config` directly. The cluster entry should look like: ```yaml theme={null} clusters: - name: my-cluster cluster: server: https://my-k8s-api.ngrok.app ``` ### Verify access You should now be able to verify access by running any kubectl command. ```bash theme={null} kubectl cluster-info ``` ## Scenario 2: X.509 client certificate or exec plugin producing a certificate (TLS passthrough) Use this scenario when your kubeconfig uses `client-certificate-data` / `client-key-data` fields or an `exec` plugin that outputs a client certificate. The `tls://` endpoint type passes the raw TLS stream through ngrok without termination, so your client certificate reaches the Kubernetes API server directly. ### Apply the AgentEndpoint manifest Create an `AgentEndpoint` using the `tls://` scheme for the public URL and `tcp://` for the upstream. Replace `my-k8s-api.ngrok.app` with your desired domain. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: kubernetes-api namespace: default spec: url: tls://my-k8s-api.ngrok.app upstream: url: tcp://kubernetes.default.svc:443 # Optionally, you can add a traffic policy to restrict access to your endpoint # Remove the traffic policy below if not needed, or update the IP to the IP # you get when running `curl https://ipv4.ngrok.com` trafficPolicy: # You can add an inline traffic policy here to restrict # access to your endpoint, or you can reference an existing # traffic policy like so: # # targetRef: # name: my-traffic-policy # inline: on_tcp_connect: - actions: - config: allow: - 1.2.3.4/32 type: restrict-ips ``` Apply it: ```bash theme={null} kubectl apply -f kubernetes-api-endpoint.yaml ``` ### Update your kubeconfig With TLS passthrough, ngrok forwards the raw TLS handshake to the Kubernetes API server. The server still presents its original cluster certificate, so you keep `certificate-authority-data` pointing to the cluster CA. ```bash theme={null} # Set the new server URL (https:// — TLS is handled end-to-end by kube-apiserver) kubectl config set-cluster \ --server=https://my-k8s-api.ngrok.app ``` Alternatively, edit `~/.kube/config` directly. The cluster entry should look like: ```yaml theme={null} clusters: - name: my-cluster cluster: server: https://my-k8s-api.ngrok.app certificate-authority-data: # Keep as-is ``` The `user` section (with `client-certificate-data` and `client-key-data`) stays unchanged—these are passed through to the API server via the TLS handshake. Verify access: ```bash theme={null} kubectl cluster-info ``` ### Troubleshooting If you get an error message like: ```bash theme={null} Unable to connect to the server: tls: failed to verify certificate: x509: certificate is valid for kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster.local, localhost, not my-k8s-api.ngrok.app ``` then the certificate presented by your Kubernetes API server is only valid for those server names and you will either need to: 1. Create/Obtain a valid certificate and instruct your kubeapi server to use it. For example, you can use `--apiserver-cert-extra-sans` if using [kubeadm](https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/) OR 2. Disable the TLS server verification step, as shown below, which comes with security implications that you should consider. ```bash theme={null} # Remove the cluster CA — it cannot be used in conjunction with --insecure-skip-tls-verify kubectl config unset clusters..certificate-authority-data # Set the new server URL kubectl config set-cluster \ --server=https://my-k8s-api.ngrok.app \ --insecure-skip-tls-verify ``` Alternatively, edit `~/.kube/config` directly. The cluster entry should look like: ```yaml theme={null} clusters: - name: my-cluster cluster: insecure-skip-tls-verify: true server: https://my-k8s-api.ngrok.app ``` ## Security Recommendations Exposing the Kubernetes API server publicly increases your attack surface. Apply these controls: ### Restrict access by IP Use a Traffic Policy on your `AgentEndpoint` to allow only known IP ranges. This prevents unauthorized clients from even reaching the API server. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: kubernetes-api namespace: default spec: url: https://my-k8s-api.ngrok.app upstream: url: https://kubernetes.default.svc.cluster.local:443 bindings: - public trafficPolicy: inline: on_tcp_connect: - actions: - type: restrict-ips config: enforce: true allow: - 203.0.113.0/28 # Your office public IP - 198.51.100.10/32 # Specific CI/CD system IP ``` See the [restrict IPs guide](/docs/gateway/k8s/guides/how-to/restrict-ips) for more details. ### Kubernetes RBAC remains your primary authorization layer ngrok handles transport security—the existing Kubernetes RBAC rules still control what any authenticated user can do. Ensure that service accounts and user accounts exposed through the ngrok endpoint follow the principle of least privilege. ### Remove access when no longer needed When remote access is no longer needed, delete the `AgentEndpoint` resource to immediately close the tunnel: ```bash theme={null} kubectl -n default delete agentendpoint kubernetes-api ``` # Finalizers Created by the ngrok Kubernetes Operator Source: https://ngrok.com/docs/gateway/k8s/guides/finalizers Learn about how the ngrok Kubernetes Operator uses finalizers to ensure proper cleanup of ngrok resources before deletion. ## What are finalizers? Finalizers are **mechanisms in Kubernetes** that prevent a resource from being deleted until **certain cleanup operations** have been completed. They act as a safeguard to ensure that dependent external resources, such as resources in your ngrok account that the Operator creates/updates/deletes, are properly removed before the Kubernetes object disappears. When a resource with a finalizer is deleted, Kubernetes **does not immediately remove it**. Instead, the resource enters a **"deletion in progress"** state, allowing the Operator to perform necessary cleanup tasks before the resource is fully deleted. If you are using the ngrok Kubernetes Operator, and delete resources such as `CloudEndpoint`/`AgentEndpoint` and notice that they are not going away, this is likely the case. *** ## How the Operator uses finalizers The ngrok Kubernetes Operator **automatically attaches finalizers** to its **custom resources** to make sure that any associated resources in your ngrok account are properly cleaned up before deletion. ### Why does the Operator use finalizers? * **Prevents orphaned resources** * Ensures ngrok resources (for example, tunnels, endpoints) are deleted before removing the Kubernetes object. * **Ensures a clean teardown** * Avoids leaving behind unnecessary external configurations. * **Prevents inconsistencies** * Ensures that removing a Kubernetes resource does not leave ghost services in your ngrok account. *** ## How finalizers work 1. **Resource is Marked for Deletion**\ When a user deletes an ngrok **custom resource**, Kubernetes does **not** immediately remove it. Instead, it **sets a deletion timestamp** on the resource. 2. **The Operator detects the deletion request**\ The ngrok Operator sees that the resource has a **finalizer** and begins the **cleanup process**. 3. **Cleanup operations are performed** * **Contacts the ngrok API** to delete the associated **tunnel, endpoint, or cloud resource**. * **Verifies successful cleanup** before proceeding. 4. **Finalizer is removed**\ Once the ngrok resource is fully cleaned up, the Operator **removes the finalizer** from the Kubernetes object. 5. **Kubernetes deletes the resource**\ With the finalizer removed, Kubernetes **completes the deletion process**, and the resource is fully removed from the cluster. *** ## Checking finalizers on a resource To check if a resource has a finalizer, use: ```bash theme={null} kubectl get -o yaml ``` Example output: ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1 kind: CloudEndpoint metadata: name: my-endpoint finalizers: - ngrok.k8s.ngrok.com/finalizer ``` ## Manually removing finalizers If the Operator is not responding or failing to clean up resources properly, you may need to manually remove the finalizer on a resource. This may happen if you are uninstalling the Operator with `helm uninstall` and the Operator pods are removed before they have a chance to clean up the finalizers on all the resources that were marked for deletion. When uninstalling the Operator, the [pre-delete cleanup hook](/docs/gateway/k8s/installation/uninstall#how-the-uninstall-process-works) handles this automatically, but if it was disabled or failed, you may need to intervene manually. This will bypass cleanup operations, potentially leaving behind orphaned resources in your ngrok account. Check your ngrok dashboard after performing this to ensure that the resource is removed from your account so that you are not billed for it. ```bash theme={null} kubectl patch --type=json -p '[{"op": "remove", "path": "/metadata/finalizers"}]' ``` ## Removing finalizers from all ngrok resources If you need to bulk-remove finalizers from all ngrok custom resources (for example, after an uninstall where the cleanup job did not complete), you can use the following script: ```bash theme={null} for crd in $(kubectl get crds -o json | jq -r '.items[].metadata.name | select(contains("ngrok.com"))'); do kubectl get "$crd" --all-namespaces -o json | jq -r '.items[] | select(.metadata.finalizers) | "\(.metadata.namespace) \(.metadata.name)"' | \ while read ns name; do kubectl patch "$crd" "$name" -n "$ns" --type=json \ -p '[{"op": "remove", "path": "/metadata/finalizers"}]' done done ``` This iterates through every ngrok CRD type, finds all instances across all namespaces, and removes their finalizers. After running this, any resources stuck in `Terminating` state should complete their deletion. *** ## See also * To learn more about how the automated cleanup process handles finalizers during uninstall, see [Uninstalling the Operator](/docs/gateway/k8s/installation/uninstall). * See [Troubleshooting](/docs/gateway/k8s/guides/troubleshooting#resources-not-getting-deleted) for help with resources that won't delete. # Securing Endpoints with Basic Authentication Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/basic-auth Learn how to secure your Kubernetes endpoints with basic authentication using the ngrok Kubernetes Operator and Traffic Policies. Basic Authentication (Basic Auth) is a simple but effective way to restrict access to your services by requiring a valid username and password before allowing requests to reach your upstream service. When Basic Authentication is enabled: 🔐 Requests without valid credentials are automatically rejected with a 401 Unauthorized response.
🔐 Only authenticated users can access your protected services.
⚡ Authentication is enforced at the gateway level, reducing security overhead for your backend.
## 🔍 What are the benefits of basic authentication? HTTP Basic Authentication is a simple yet effective way to restrict access to sensitive services without requiring complex identity providers or OAuth-based authentication systems. Key Benefits: * Securing development or staging environments from public access. * Adding a lightweight authentication layer when no other auth system is available. * Protecting internal admin panels, dashboards, or API endpoints. * Protect temporary or short-lived services without setting up an OAuth provider. If you need more secure authentication methods for production traffic, consider the [OAuth](/docs/gateway/k8s/guides/how-to/oauth), [OIDC](/docs/gateway/k8s/guides/how-to/oidc), and [JWT Validation](/docs/gateway/k8s/guides/how-to/jwt-validation) guides. ## Basic authentication examples The following examples show how to set up an endpoint that accepts requests that contain basic auth credentials for either `user:password1` or `admin:password2`. All other requests will be denied with a `401` response code. Check out the [basic auth Traffic Policy action](/docs/gateway/traffic-policy/actions/basic-auth/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: basic-auth config: realm: sample-realm credentials: - user:password1 - admin:password2 enforce: true ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: basic-auth config: realm: sample-realm credentials: - user:password1 - admin:password2 enforce: true - type: custom-response config: status_code: 200 headers: content-type: text/plain body: Successfully Authenticated! ``` 💡 `Ingress` resources do not natively support Basic Authentication, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: basic-auth config: realm: sample-realm credentials: - user:password1 - admin:password2 enforce: true ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` 💡 Gateway API resources do not natively support Basic Authentication, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: basic-auth config: realm: sample-realm credentials: - user:password1 - admin:password2 enforce: true ``` ### 2. Use the `NgrokTrafficPolicy` on a `Gateway` The following example showcases supplying the `NgrokTrafficPolicy` on a `Gateway` resource. All requests to the `Gateway` will run the Traffic Policy. If you prefer, `NgrokTrafficPolicy` can also be used on the route level by using an `externalRef` filter on an `HTTPRoute`. See the [using Gateway API guide](/docs/gateway/k8s/guides/using-gwapi) for examples. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: example-tp spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` # Circuit Breaking for System Reliability Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/circuit-breaking Configure circuit breaking in Kubernetes to protect your services from overload and maintain system reliability with ngrok. Circuit breaking is a resilience pattern that helps maintain system reliability by temporarily rejecting requests when error rates and request volumes exceed predefined thresholds. This prevents overloaded services from spiraling into failure and allows them time to recover. When circuit breaking is enabled: 🚨 Requests are automatically rejected if too many errors occur within a given time window.
⏳ Traffic pauses for a set period to allow the upstream service to recover.
✅ The system automatically re-evaluates health and resumes traffic once conditions improve.
## 🔍 What are the benefits of circuit breaking? Modern applications often rely on multiple microservices, APIs, or external dependencies. If one service becomes slow or fails, it can cause cascading failures across the entire system. Key Benefits: * Isolating failing services so they don't bring down the entire system. * Reducing unnecessary load on struggling services. * Ensuring graceful degradation instead of full service failure. * If a downstream service becomes overloaded, circuit breaking can prevent other services from also failing. * Prevents long wait times for users by cutting off slow or failing requests. * Ensures high-priority services remain available even when others are experiencing high failure rates. ## Circuit breaking examples The following examples show how to set up an endpoint that allows only 1 request every 60 seconds and trips the circuit breaker for 2 minutes. Check out the [circuit breaking Traffic Policy action](/docs/gateway/traffic-policy/actions/circuit-breaker/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: circuit-breaker config: error_threshold: 0 volume_threshold: 1 window_duration: 60s tripped_duration: 2m enforce: true ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: circuit-breaker config: error_threshold: 0 volume_threshold: 1 window_duration: 60s tripped_duration: 2m enforce: true ``` 💡 `Ingress` resources do not natively support circuit breaking, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: circuit-breaker config: error_threshold: 0 volume_threshold: 1 window_duration: 60s tripped_duration: 2m enforce: true ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` 💡 Gateway API resources do not natively support circuit breaking, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: circuit-breaker config: error_threshold: 0 volume_threshold: 1 window_duration: 60s tripped_duration: 2m enforce: true ``` ### 2. Use the `NgrokTrafficPolicy` on a `Gateway` The following example showcases supplying the `NgrokTrafficPolicy` on a `Gateway` resource. All requests to the `Gateway` will run the Traffic Policy. If you prefer, `NgrokTrafficPolicy` can also be used on the route level by using an `externalRef` filter on an `HTTPRoute`. See the [using Gateway API guide](/docs/gateway/k8s/guides/using-gwapi) for examples. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: example-tp spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` # Improving Performance with Response Compression Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/compress-response Enable response compression in Kubernetes to improve performance and reduce bandwidth usage for your ngrok endpoints. Response compression reduces the size of HTTP response bodies sent from your upstream service, improving page load speeds, reducing bandwidth usage, and enhancing user experience. When response compression is enabled: 📉 Data transfer sizes are reduced, lowering bandwidth consumption.
⚡ Pages and APIs load faster, especially on slower networks.
🚀 Performance is improved without requiring changes to the upstream service.
## 🔍 What are the benefits of response compression? Every millisecond counts when serving web applications and APIs. Compression is a simple yet effective way to improve performance without modifying backend code. Response compression offers the following benefits: * Faster Web & API Performance. Users receive responses quicker, reducing page load times. * Compress JSON and XML payloads for faster API calls. * Lower Bandwidth Usage. Ideal for high-traffic APIs, mobile users, and global applications. * Better Client Experience. Reduces latency, especially in areas with slower network connections. * Reduces bandwidth usage for users on slow or metered connections. * No Backend Changes Needed. Compression is handled at the Traffic Policy level, requiring zero code modifications. ## Response compression examples When an HTTP request includes an Accept-Encoding header, HTTP responses are automatically compressed, and a Content-Encoding header is added to the response. If the response is already compressed by your upstream service, no additional compression will be applied. The following examples configure the endpoint to have ngrok compress the response using one of the specified algorithms (for example, gzip). The response will include the `content-encoding: gzip` header, and the body will be compressed accordingly. The Traffic Policy includes an expression to only run the compression on paths that start with `/api/` Check out the [compress response Traffic Policy action](/docs/gateway/traffic-policy/actions/compress-response/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_response: - expressions: - req.url.path.startsWith('/api/') actions: - type: compress-response config: algorithms: - gzip - br - deflate - compress ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_response: - expressions: - req.url.path.startsWith('/api/') actions: - type: compress-response config: algorithms: - gzip - br - deflate - compress ``` 💡 `Ingress` resources do not natively support response compression, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_response: - expressions: - req.url.path.startsWith('/api/') actions: - type: compress-response config: algorithms: - gzip - br - deflate - compress ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` 💡 Gateway API resources do not natively support response compression, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_response: - expressions: - req.url.path.startsWith('/api/') actions: - type: compress-response config: algorithms: - gzip - br - deflate - compress ``` ### 2. Use the `NgrokTrafficPolicy` on a `Gateway` The following example showcases supplying the `NgrokTrafficPolicy` on a `Gateway` resource. All requests to the `Gateway` will run the Traffic Policy. If you prefer, `NgrokTrafficPolicy` can also be used on the route level by using an `externalRef` filter on an `HTTPRoute`. See the [using Gateway API guide](/docs/gateway/k8s/guides/using-gwapi) for examples. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: example-tp spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` # Protecting Your Endpoints by Denying Unwanted Traffic Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/deny-requests Configure request denial rules in Kubernetes to block unwanted or malicious traffic before it reaches your upstream services. Whenever your endpoints are exposed to the internet, they will inevitably receive unwanted or malicious traffic. This can include bot activity, unauthorized access attempts, abusive requests, or even potential security threats. By denying requests at the edge, you can: 🚫 Block unauthorized access attempts before they reach your upstream service.
⚡ Reduce load on backend services by filtering unnecessary traffic.
🔐 Improve security by rejecting requests based on predefined rules.
## 🔍 What are the benefits of denying requests? Publicly exposed services are constantly targeted by automated scanners, bots, and unwanted traffic. Even if these requests are not inherently malicious, they can consume resources, increase latency, and degrade performance. Key Benefits: * **Reduce Attack Surface:** Stop unauthorized access attempts before they hit your backend. * **Harden API Security:** Restrict access to only legitimate users and trusted sources. * **Improve Performance:** Free up server resources by dropping unwanted requests early. * **Protect Against Malicious Bots & Scanners:** Prevent automated scripts and scrapers from overloading your API. * Deny requests based on geolocation rules to comply with regulations. * Prevent excessive requests from bad actors or misconfigured clients. ## Denying requests examples The following examples set up an endpoint that will deny all traffic unless the client IP is from the EU. See the [IP Intelligence](/docs/gateway/traffic-policy/variables/ip-intel) page for more ways you can check properties of the connecting IP address. Check out the [deny request Traffic Policy action](/docs/gateway/traffic-policy/actions/deny/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - expressions: - conn.client_ip.geo.location.is_eu == false actions: - type: deny config: status_code: 403 ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - expressions: - conn.client_ip.geo.location.is_eu == false actions: - type: deny config: status_code: 403 ``` 💡 `Ingress` resources do not natively support request denial configuration, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - expressions: - conn.client_ip.geo.location.is_eu == false actions: - type: deny config: status_code: 403 ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` 💡 Gateway API resources do not natively support request denial configuration, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - expressions: - conn.client_ip.geo.location.is_eu == false actions: - type: deny config: status_code: 403 ``` ### 2. Use the `NgrokTrafficPolicy` on a `Gateway` The following example showcases supplying the `NgrokTrafficPolicy` on a `Gateway` resource. All requests to the `Gateway` will run the Traffic Policy. If you prefer, `NgrokTrafficPolicy` can also be used on the route level by using an `externalRef` filter on an `HTTPRoute`. See the [using Gateway API guide](/docs/gateway/k8s/guides/using-gwapi) for examples. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: example-tp spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` # Securing Your Endpoints with JWT Validation Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/jwt-validation Configure JWT validation in Kubernetes to secure your endpoints by verifying authentication tokens at the network edge. By validating JWTs at the network edge, you can: 🔒 Ensure only authenticated users can access your services.
⚡ Reduce backend load by rejecting unauthorized requests early.
🛡 Protect APIs from unauthorized access without modifying your upstream services.
## 🔍 What are the benefits of validating JWTs? JWTs are commonly used in OAuth 2.0, OpenID Connect (OIDC), and API authentication flows. However, simply including a JWT in a request does not improve security. It must be verified to ensure: * The token is properly signed by a trusted provider. * The token has not expired. * The token contains the correct claims (for example, roles, permissions). By performing JWT validation at the network edge, you can offload authentication tasks from your backend and prevent unauthorized access before requests reach your service. Key Benefits: * **Enforcing Authentication:** Ensure only authenticated users can access protected resources. * **API Security & Access Control:** Validate JWT claims to enforce role-based access (RBAC) or tenant restrictions. * **Single Sign-On (SSO) Support:** Accept tokens from OAuth, OpenID Connect (OIDC), or enterprise identity providers. * **Reducing Backend Load:** Offload JWT validation to the network edge, reducing unnecessary authentication checks. ## JWT validation examples The following Traffic Policy configuration is an example configuration of the jwt-validation action. For a more real-world example, check out the [Auth0](/docs/integrations/jwt-validation/auth0) guide. A useful tool for working with JWTs is provided at [jwt.io](https://jwt.io/). Check out the [JWT validation Traffic Policy action](/docs/gateway/traffic-policy/actions/jwt-validation/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: jwt-validation config: issuer: allow_list: - value: https://example.com/issuer audience: allow_list: - value: urn:example:api http: tokens: - type: access_token method: header name: Authorization prefix: "Bearer " - type: it+jwt method: body name: _id_token jws: allowed_algorithms: - RS256 - ES256 keys: sources: additional_jkus: - https://example.com/issuer/jku ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: jwt-validation config: issuer: allow_list: - value: https://example.com/issuer audience: allow_list: - value: urn:example:api http: tokens: - type: access_token method: header name: Authorization prefix: "Bearer " - type: it+jwt method: body name: _id_token jws: allowed_algorithms: - RS256 - ES256 keys: sources: additional_jkus: - https://example.com/issuer/jku ``` 💡 `Ingress` resources do not natively support JWT validation, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: jwt-validation config: issuer: allow_list: - value: https://example.com/issuer audience: allow_list: - value: urn:example:api http: tokens: - type: access_token method: header name: Authorization prefix: "Bearer " - type: it+jwt method: body name: _id_token jws: allowed_algorithms: - RS256 - ES256 keys: sources: additional_jkus: - https://example.com/issuer/jku ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` 💡 Gateway API resources do not natively support JWT validation, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: jwt-validation config: issuer: allow_list: - value: https://example.com/issuer audience: allow_list: - value: urn:example:api http: tokens: - type: access_token method: header name: Authorization prefix: "Bearer " - type: it+jwt method: body name: _id_token jws: allowed_algorithms: - RS256 - ES256 keys: sources: additional_jkus: - https://example.com/issuer/jku ``` ### 2. Use the `NgrokTrafficPolicy` on a `Gateway` The following example showcases supplying the `NgrokTrafficPolicy` on a `Gateway` resource. All requests to the `Gateway` will run the Traffic Policy. If you prefer, `NgrokTrafficPolicy` can also be used on the route level by using an `externalRef` filter on an `HTTPRoute`. See the [using Gateway API guide](/docs/gateway/k8s/guides/using-gwapi) for examples. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: example-tp spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` # Customizing Requests with Header Manipulation Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/manipulate-headers Learn how to add, modify, or remove HTTP headers in Kubernetes to enhance security, routing, and service compatibility. Header manipulation is a core feature of API Gateways that allows you to modify HTTP headers on incoming requests and outgoing responses. This enables better security, routing, debugging, and interoperability between services. By adding, modifying, or removing headers, you can: 🛠 Enrich requests with additional metadata before they reach your backend.
🔒 Enhance security by stripping sensitive headers from responses.
🚀 Enable service compatibility by adjusting headers for different upstream APIs.
## 🔍 What are the benefits of modifying headers? Headers contain critical metadata that helps control authentication, caching, routing, security, and debugging. Modifying headers at the gateway level ensures consistency without requiring upstream service changes. Key Benefits: * **Modify Requests Before They Reach Your Services:** Adjust headers at the network edge before requests hit your backend. * **Ensure API Compatibility:** Add or modify headers to meet the expectations of different backends. * **Improve Security & Privacy:** Strip unnecessary or sensitive headers from responses. * **Simplify Authentication & Tracing:** Add authentication tokens or tracking headers for better monitoring. * **Reduce Backend Overhead:** Offload header transformations from your application code. * **Enhancing Security:** Remove headers like Server to hide backend details from attackers. * **Injecting Authentication Headers:** Add Authorization headers for SSO, API tokens, or identity providers. * **Adding Debugging & Tracing Information:** Inject X-Request-ID or Trace-Id headers for observability and monitoring. ## Header manipulation examples ### Adding request headers The following examples will add the `X-Request-Header: my-custom-value` header to requests before they are forwarded to the upstream. Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: actions: - type: add-headers config: headers: X-Request-Header: my-custom-value ``` Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: actions: - type: add-headers config: headers: X-Request-Header: my-custom-value ``` 💡 `Ingress` resources do not have native configuration for header manipulation, but they can be extended using a Traffic Policy. Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: actions: - type: add-headers config: headers: X-Request-Header: my-custom-value ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports header manipulation, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with the `RequestHeaderModifier` filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: / backendRefs: - group: "" kind: Service name: example-service port: 8080 weight: 1 filters: - type: RequestHeaderModifier requestHeaderModifier: add: - name: X-Request-Header value: "my-custom-value" ``` ### Adding response headers The following examples will add the `X-Response-Header: my-custom-value` header to responses before they are sent to the clients. Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_response: actions: - type: add-headers config: headers: X-Response-Header: my-custom-value ``` Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_response: actions: - type: add-headers config: headers: X-Response-Header: my-custom-value ``` 💡 `Ingress` resources do not have native configuration for header manipulation, but they can be extended using a Traffic Policy. Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_response: actions: - type: add-headers config: headers: X-Response-Header: my-custom-value ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports header manipulation, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with the `ResponseHeaderModifier` filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: / backendRefs: - group: "" kind: Service name: example-service port: 8080 weight: 1 filters: - type: ResponseHeaderModifier responseHeaderModifier: add: - name: X-Response-Header value: "my-custom-value" ``` ### Removing request headers The following examples will remove the `X-Trace-Id` header from requests before they are forwarded to the upstream if the header is present. Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: actions: - type: remove-headers config: headers: - X-Trace-Id ``` Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: actions: - type: remove-headers config: headers: - X-Trace-Id ``` 💡 `Ingress` resources do not have native configuration for header manipulation, but they can be extended using a Traffic Policy. Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: actions: - type: remove-headers config: headers: - X-Trace-Id ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports header manipulation, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with the `RequestHeaderModifier` filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: / backendRefs: - group: "" kind: Service name: example-service port: 8080 weight: 1 filters: - type: RequestHeaderModifier remove: - X-Trace-Id ``` ### Removing response headers The following examples will remove the `Server` header from responses before they are sent to the clients if the header is present. Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_response: actions: - type: remove-headers config: headers: - Server ``` Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_response: actions: - type: remove-headers config: headers: - Server ``` 💡 `Ingress` resources do not have native configuration for header manipulation, but they can be extended using a Traffic Policy. Check out the [add headers](/docs/gateway/traffic-policy/actions/add-headers/) and [remove headers](/docs/gateway/traffic-policy/actions/remove-headers/) Traffic Policy action pages for more details about how they function and the parameters they accept. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_response: actions: - type: remove-headers config: headers: - Server ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports header manipulation, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with the `ResponseHeaderModifier` filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: / backendRefs: - group: "" kind: Service name: example-service port: 8080 weight: 1 filters: - type: ResponseHeaderModifier responseHeaderModifier: remove: - Server ``` # Securing Your Endpoints with OAuth Authentication Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/oauth Configure OAuth authentication in Kubernetes to secure your endpoints by requiring valid OAuth tokens before forwarding requests. OAuth is a widely adopted authentication and authorization mechanism that enables users to securely access resources without exposing their credentials. It is commonly used for single sign-on (SSO), API security, and third-party integrations. By enforcing OAuth authentication at the network edge, you can: 🔐 Ensure only authorized users can access your services.
⚡ Offload authentication from your application, simplifying backend logic.
🛡 Prevent unauthorized access by requiring valid OAuth tokens before forwarding requests.
## 🔍 What are the benefits of using OAuth for authentication? OAuth allows applications to delegate authentication to a trusted identity provider (IdP), reducing the need for applications to handle passwords directly. This enhances security, user experience, and compliance. Key Benefits: * **Secure Authentication:** Prevent unauthorized access with token-based authentication. * **Seamless Single Sign-On (SSO):** Enable SSO across multiple services without requiring users to reauthenticate. * **Supports Major Identity Providers:** Works with Google, Microsoft, GitHub, Okta, and other OAuth providers. * **Improves Security & Compliance:** Helps meet security best practices like OAuth 2.0 and OpenID Connect (OIDC). * **Reduces Backend Complexity:** Authentication happens before requests reach your application, eliminating the need for custom auth logic. ## OAuth examples The following example showcases how you might set up a required Google login with a custom provider that requests the `userinfo.profile` and `userinfo.email` scopes. Check out the [OAuth Traffic Policy action](/docs/gateway/traffic-policy/actions/oauth/) page for more details about how it functions and the parameters it accepts. Consult the list of [supported providers](/docs/gateway/traffic-policy/actions/oauth/#supported-providers) for step-by-step integration guides for each one. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: oauth config: provider: google client_id: "{your app's oauth client id}" client_secret: "{your app's oauth client secret}" scopes: - https://www.googleapis.com/auth/userinfo.profile - https://www.googleapis.com/auth/userinfo.email ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: oauth config: provider: google client_id: "{your app's oauth client id}" client_secret: "{your app's oauth client secret}" scopes: - https://www.googleapis.com/auth/userinfo.profile - https://www.googleapis.com/auth/userinfo.email ``` 💡 `Ingress` resources do not natively support OAuth enforcement, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: oauth config: provider: google client_id: "{your app's oauth client id}" client_secret: "{your app's oauth client secret}" scopes: - https://www.googleapis.com/auth/userinfo.profile - https://www.googleapis.com/auth/userinfo.email ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` 💡 Gateway API resources do not natively support OAuth enforcement, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: oauth config: provider: google client_id: "{your app's oauth client id}" client_secret: "{your app's oauth client secret}" scopes: - https://www.googleapis.com/auth/userinfo.profile - https://www.googleapis.com/auth/userinfo.email ``` ### 2. Use the `NgrokTrafficPolicy` on a `Gateway` The following example showcases supplying the `NgrokTrafficPolicy` on a `Gateway` resource. All requests to the `Gateway` will run the Traffic Policy. If you prefer, `NgrokTrafficPolicy` can also be used on the route level by using an `externalRef` filter on an `HTTPRoute`. See the [using Gateway API guide](/docs/gateway/k8s/guides/using-gwapi) for examples. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: example-tp spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` # Authenticating Users with OpenID Connect (OIDC) Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/oidc Configure OpenID Connect authentication in Kubernetes to secure your endpoints using trusted identity providers. OpenID Connect (OIDC) is an identity verification protocol that allows users to securely sign in to applications using trusted identity providers (IdPs) such as Google, Microsoft, Okta, and Auth0. OIDC is built on top of OAuth 2.0, adding an authentication layer that enables applications to verify user identities while also obtaining authorization tokens. By enforcing OIDC authentication at the network edge, you can: 🔐 Ensure only authenticated users can access your services.
⚡ Offload authentication to a trusted provider, simplifying backend logic.
🛡 Enhance security by requiring identity verification before forwarding requests.
## 🔍 What are the benefits of using OIDC for authentication? OIDC provides secure, standardized authentication that integrates seamlessly with modern identity providers. It enables single sign-on (SSO), multi-factor authentication (MFA), and user identity verification. Key Benefits: * **User Authentication & Identity Verification:** Verify who is accessing your service, not just what permissions they have. * **Seamless Single Sign-On (SSO):** Allow users to sign in once and access multiple applications. * **Supports Major Identity Providers:** Works with Google, Microsoft, Okta, Auth0, and other IdPs. * **Enhances Security:** Enforces secure login flows, multi-factor authentication (MFA), and token-based verification. * **Reduces Backend Complexity:** Authentication happens before requests reach your application, eliminating the need for custom auth logic. ## OIDC examples The following examples show how to provide your app with an authentication step. When you create your own OIDC app, you must specify a 'Callback URL' or 'Redirect URL' to the OIDC provider. When using ngrok's OIDC action, that Callback URL is always: ```bash theme={null} https://idp.ngrok.com/oauth2/callback ``` Check out the [OIDC Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc/) page for more details about how it functions and the parameters it accepts. Consult the list of [supported providers](/docs/gateway/traffic-policy/actions/oidc/#supported-providers) for step-by-step integration guides for each. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: openid-connect config: issuer_url: "{your issuer url}" client_id: "{your app's oauth client id}" client_secret: "{your app's oauth client secret}" scopes: - openid - profile - email ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: openid-connect config: issuer_url: "{your issuer url}" client_id: "{your app's oauth client id}" client_secret: "{your app's oauth client secret}" scopes: - openid - profile - email ``` 💡 `Ingress` resources do not natively support OIDC authentication, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: openid-connect config: issuer_url: "{your issuer url}" client_id: "{your app's oauth client id}" client_secret: "{your app's oauth client secret}" scopes: - openid - profile - email ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` 💡 Gateway API resources do not natively support OIDC authentication, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: openid-connect config: issuer_url: "{your issuer url}" client_id: "{your app's oauth client id}" client_secret: "{your app's oauth client secret}" scopes: - openid - profile - email ``` ### 2. Use the `NgrokTrafficPolicy` on a `Gateway` The following example showcases supplying the `NgrokTrafficPolicy` on a `Gateway` resource. All requests to the `Gateway` will run the Traffic Policy. If you prefer, `NgrokTrafficPolicy` can also be used on the route level by using an `externalRef` filter on an `HTTPRoute`. See the [using Gateway API guide](/docs/gateway/k8s/guides/using-gwapi) for examples. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: example-tp spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` # Restricting Access by Kubernetes Pod Identity Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/pod-identity Use Kubernetes pod identity variables to secure Endpoints. Configure Traffic Policies on Kubernetes-bound endpoints to allow or deny connections based on the identity of the originating pod. When a connection arrives on a Kubernetes-bound endpoint, ngrok makes pod metadata available as Traffic Policy variables under `conn.k8s.pod.*`. This lets you write policies that make access control decisions based on which Kubernetes workload is initiating the connection, not just IP address or network location. Use cases include: * Restricting a Kubernetes-bound endpoint so that only pods in a specific namespace can connect. * Building allowlists of approved pod names for sensitive internal services. * Enforcing tenant isolation in multi-tenant clusters by matching on namespace or pod annotations. ## Requirements * ngrok Kubernetes Operator version **0.22.1 or later**. See [Updates & Upgrades](/docs/gateway/k8s/installation/update) for instructions on upgrading. ## Available pod identity variables The following variables are available in Traffic Policy expressions on connections to Kubernetes-bound endpoints. | Variable | Description | Size limit | | - | - | - | | `conn.k8s.pod.id` | The unique identifier (UID) of the originating pod. | 36 bytes | | `conn.k8s.pod.metadata.name` | The name of the originating pod. | 255 bytes | | `conn.k8s.pod.metadata.namespace` | The namespace the originating pod belongs to. | 63 bytes | | `conn.k8s.pod.metadata.annotations` | A map of pod annotations prefixed with `k8s.ngrok.com/`. See [Annotations](#annotations) below. | 1024 bytes | | `conn.k8s.pod.metadata.error_code` | An error code set when pod identity could not be resolved. See [Handling errors](#handling-errors) below. | N/A | | `conn.k8s.pod.metadata.error_message` | A human-readable error message providing additional detail when `conn.k8s.pod.metadata.error_code` is set. | N/A | These variables are only populated for connections to endpoints with a `kubernetes` binding. They are not available on public or internal endpoints. ### Annotations Only annotations prefixed with `k8s.ngrok.com/` are included in `conn.k8s.pod.metadata.annotations`. Annotations without this prefix are not surfaced. The combined size of all included annotations is subject to a 1024-byte cumulative limit. If this limit is exceeded, `conn.k8s.pod.metadata.error_code` will be set to `ERR_NGROK_28000` and a truncated annotation map being returned. To use an annotation in a Traffic Policy expression, add the `k8s.ngrok.com/` prefix to it in your pod spec: ```yaml theme={null} metadata: annotations: k8s.ngrok.com/tenant-id: "tenant-abc" k8s.ngrok.com/environment: "production" ``` You can then reference the annotation in a Traffic Policy expression: ``` conn.k8s.pod.metadata.annotations["k8s.ngrok.com/tenant-id"] == "tenant-abc" ``` ## Handling errors Pod identity is resolved at connection time. In some circumstances, identity information may be unavailable; for example, immediately after a pod first starts, or if the originating pod cannot be uniquely identified due to network configuration. When this happens, the `conn.k8s.pod.*` metadata variables will not be set, and the error variables will be populated instead. | Error code | Meaning | | - | - | | [`ERR_NGROK_28000`](/docs/errors/err_ngrok_28000) | The combined size of one or more pod identity variables exceeded the allowed limit. | | [`ERR_NGROK_28001`](/docs/errors/err_ngrok_28001) | Pod identity metadata could not be found for this connection. | Because pod identity may not always be available, **your Traffic Policy should explicitly handle the case where identity is missing**. The two common approaches are fail-closed (deny the connection if identity is unavailable) and fail-open (allow the connection to proceed). Which you choose depends on your security requirements. ### Fail-closed (recommended for security-sensitive endpoints) Deny the connection if pod identity cannot be resolved. ```yaml theme={null} on_tcp_connect: - expressions: - conn.k8s.pod.metadata.error_code != "" actions: - type: deny - expressions: - conn.k8s.pod.metadata.namespace != "my-namespace" actions: - type: deny - actions: - type: forward-internal config: url: http://my-service.my-namespace.internal ``` ### Fail-open Allow the connection to proceed even if pod identity is unavailable, but still enforce the policy when identity is present. ```yaml theme={null} on_tcp_connect: - expressions: - conn.k8s.pod.metadata.error_code == "" && conn.k8s.pod.metadata.namespace != "my-namespace" actions: - type: deny - actions: - type: forward-internal config: url: http://my-service.my-namespace.internal ``` ## Known limitations * **Pods using `hostNetwork: true`** share the node's IP address and cannot be uniquely identified. Pod identity is not supported for these pods. * **Some CNI configurations** may prevent reliable pod identity resolution. If you observe unexpected `ERR_NGROK_28001` errors, verify that your CNI preserves pod source IPs. * Pod identity is evaluated once when the connection is established. If a pod's metadata changes after connection, the policy continues to use the identity that was captured at connect time. ## Pod identity examples The following examples demonstrate common pod identity access control patterns. ### Deny connections from outside a namespace Only allow connections originating from pods in the `payments` namespace. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_tcp_connect: - expressions: - conn.k8s.pod.metadata.error_code != "" actions: - type: deny - expressions: - conn.k8s.pod.metadata.namespace != "payments" actions: - type: deny - actions: - type: forward-internal config: url: http://my-service.my-namespace.internal ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: http://example-service.my-namespace bindings: - kubernetes trafficPolicy: policy: on_tcp_connect: - expressions: - conn.k8s.pod.metadata.error_code != "" actions: - type: deny - expressions: - conn.k8s.pod.metadata.namespace != "payments" actions: - type: deny - actions: - type: forward-internal config: url: http://my-service.my-namespace.internal ``` ### Allow only specific pods by name Allowlist two specific pods by name and deny all others. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: http://example-service.my-namespace bindings: - kubernetes trafficPolicy: policy: on_tcp_connect: - expressions: - conn.k8s.pod.metadata.error_code != "" actions: - type: deny - expressions: - !(conn.k8s.pod.metadata.name in ["worker-a", "worker-b"]) actions: - type: deny - actions: - type: forward-internal config: url: http://my-service.my-namespace.internal ``` ### Enforce access using a pod annotation Only allow connections from pods that have the `k8s.ngrok.com/environment: production` annotation. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: http://example-service.my-namespace bindings: - kubernetes trafficPolicy: policy: on_tcp_connect: - expressions: - conn.k8s.pod.metadata.error_code != "" actions: - type: deny - expressions: - conn.k8s.pod.metadata.annotations["k8s.ngrok.com/environment"] != "production" actions: - type: deny - actions: - type: forward-internal config: url: http://my-service.my-namespace.internal ``` ### Multi-tenant isolation Deny any connection whose pod namespace does not match the endpoint's own namespace. This is a common pattern for multi-tenant clusters where each tenant runs in a dedicated namespace. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: webapp-tenant1 namespace: tenant1 spec: url: http://webapp-tenant1.tenant1 bindings: - kubernetes trafficPolicy: policy: on_tcp_connect: - expressions: - conn.k8s.pod.metadata.error_code != "" actions: - type: deny - expressions: - conn.k8s.pod.metadata.namespace != "tenant1" actions: - type: deny - actions: - type: forward-internal config: url: http://webapp.tenant1.internal ``` # Protecting Services with Rate Limiting Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/rate-limiting Configure rate limiting in Kubernetes to protect your services from excessive traffic and ensure fair usage across clients. Rate limiting is a critical feature that prevents your services from being overwhelmed by excessive network traffic. By enforcing rate limits, you can control how many requests are allowed over a given period, helping to: ⚡ Maintain API performance by preventing excessive load.
🔐 Protect against abuse and malicious traffic.
🌍 Ensure fair usage across clients and users.
## 🔍 What are the benefits of rate limiting? Without rate limiting, a single misconfigured client or a spike in traffic could easily overload your backend. Implementing rate limiting at the network edge helps prevent: * API overuse and abuse (for example, DDoS attacks, aggressive polling). * Performance degradation for other users. * Unexpected infrastructure costs due to excessive traffic. Key Benefits: * **Prevents Service Overload:** Ensures API stability even under high traffic. * **Protects Against Automated Abuse:** Stops bots and scrapers from overwhelming services. * **Ensures Fair Usage:** Distributes API resources evenly among users. * **Reduces Infrastructure Costs:** Avoids unnecessary scaling due to excessive requests. * **Improves Security & Compliance:** Helps meet API quotas and prevent brute-force attacks. ## Rate limiting examples The following examples demonstrate how to rate limit all incoming requests by the `Host` header. Check out the [rate limit Traffic Policy action](/docs/gateway/traffic-policy/actions/rate-limit/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: rate-limit config: name: Only allow 30 requests per minute algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - req.headers['host'] ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: rate-limit config: name: Only allow 30 requests per minute algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - req.headers['host'] ``` 💡 `Ingress` resources do not natively support rate limiting, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: rate-limit config: name: Only allow 30 requests per minute algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - req.headers['host'] ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` 💡 Gateway API resources do not natively support rate limiting, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: rate-limit config: name: Only allow 30 requests per minute algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - req.headers['host'] ``` ### 2. Use the `NgrokTrafficPolicy` on a `Gateway` The following example showcases supplying the `NgrokTrafficPolicy` on a `Gateway` resource. All requests to the `Gateway` will run the Traffic Policy. If you prefer, `NgrokTrafficPolicy` can also be used on the route level by using an `externalRef` filter on an `HTTPRoute`. See the [using Gateway API guide](/docs/gateway/k8s/guides/using-gwapi) for examples. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: example-tp spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` # Managing Traffic with Redirects Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/redirects Configure URL redirects in Kubernetes to route users to the correct destination and enforce HTTPS connections. Redirects allow you to seamlessly route users from one URL to another, ensuring they always reach the correct destination. They are commonly used for: 🔄 Preserving SEO rankings when URLs change.
🔐 Enforcing secure HTTPS connections.
🌍 Directing traffic to localized versions of a site.
## 🔍 What are the benefits of redirects? Redirects help optimize user experience and security by ensuring that visitors always land on the correct and most relevant page. Key Benefits: * **Ensure Seamless URL Changes:** Automatically direct users to new pages when URLs change. * **Enforce HTTPS:** Redirect HTTP traffic to secure HTTPS connections. * **Improve SEO & Preserve Link Equity:** Avoid broken links that impact search engine rankings. * **Handle Legacy URLs:** Maintain compatibility with old URLs or deprecated endpoints. * **Enhance User Experience:** Automatically direct visitors to localized or mobile-friendly versions of your site. ## Redirect full path The following examples showcase how to create an endpoint that redirects requests. The hostname, scheme, and port are kept the same, but the entire path of the request is changed. * Requests to `https://example-hostname.ngrok.io/example` will be redirected to `https://example-hostname.ngrok.io/redirect` * Requests to `https://example-hostname.ngrok.io/example/foo` will be redirected to `https://example-hostname.ngrok.io/redirect` Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: redirect config: from: /.* to: /redirect ``` Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: redirect config: from: /.* to: /redirect ``` 💡 Ingress resources do not have native redirect capabilities, but they can be extended using a Traffic Policy. Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more information about how to customize the parameters. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: redirect config: from: /.* to: /redirect ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports redirect configuration, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with a RequestRedirect Filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: /example filters: - type: RequestRedirect requestRedirect: path: type: ReplaceFullPath replaceFullPath: "/redirect" ``` ## Redirect path prefix The following examples showcase how to create an endpoint that redirects requests. The hostname, scheme, and port are kept the same, but only the prefix is changed. * Requests to `https://example-hostname.ngrok.io/example` will be redirected to `https://example-hostname.ngrok.io/redirect` * Requests to `https://example-hostname.ngrok.io/example/foo` will be redirected to `https://example-hostname.ngrok.io/redirect/foo` Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: redirect config: from: /example(.*) to: /redirect$1 ``` Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: redirect config: from: /example(.*) to: /redirect$1 ``` 💡 Ingress resources do not have native redirect capabilities, but they can be extended using a Traffic Policy. Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more information about how to customize the parameters. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: redirect config: from: /example(.*) to: /redirect$1 ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports redirect configuration, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with a RequestRedirect Filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: /example filters: - type: RequestRedirect requestRedirect: path: type: ReplacePrefixMatch replacePrefixMatch: "/redirect" ``` ## Redirect port The following examples showcase how to create an endpoint that redirects requests. The hostname, scheme, and path are kept the same, but only the port is changed. * Requests to `http://example-hostname.ngrok.io:8080` will be redirected to `http://example-hostname.ngrok.io:9090` Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: http://example-hostname.ngrok.io:8080 upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: redirect config: from: :8080 to: :9090 ``` Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: http://example-hostname.ngrok.io:8080 trafficPolicy: policy: on_http_request: - actions: - type: redirect config: from: :8080 to: :9090 ``` 💡 Ingress resources do not have native redirect capabilities, but they can be extended using a Traffic Policy. Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more information about how to customize the parameters. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: redirect config: from: :8080 to: :9090 ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports redirect configuration, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 8080 protocol: HTTP ``` ### 2. Create an `HTTPRoute` with a RequestRedirect Filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: /example filters: - type: RequestRedirect requestRedirect: port: 9090 ``` ## Redirect hostname The following examples showcase how to create an endpoint that redirects requests. The scheme, port, and path are kept the same, but only the hostname is changed. * Requests to `https://example-hostname.ngrok.io` will be redirected to `https://other-example-hostname.ngrok.io` Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: redirect config: from: example-hostname.ngrok.io to: other-example-hostname.ngrok.io ``` Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: redirect config: from: example-hostname.ngrok.io to: other-example-hostname.ngrok.io ``` 💡 Ingress resources do not have native redirect capabilities, but they can be extended using a Traffic Policy. Check out the [redirect Traffic Policy action](/docs/gateway/traffic-policy/actions/redirect/) page for more information about how to customize the parameters. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: redirect config: from: example-hostname.ngrok.io to: other-example-hostname.ngrok.io ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports redirect configuration, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with a RequestRedirect Filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: /example filters: - type: RequestRedirect requestRedirect: hostname: other-example-hostname.ngrok.io ``` # Controlling Traffic Flow with Request Routing Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/request-routing Configure request routing in Kubernetes to direct incoming traffic to the appropriate backend services based on rules. Request routing is the core capability of an API Gateway, enabling it to direct incoming traffic to the appropriate backend service. By defining routing rules, you can control how requests are handled, which services they reach, and under what conditions. Proper request routing ensures: 🚀 Optimized performance by directing traffic efficiently.
🔄 Seamless service rollouts by routing requests based on versioning, headers, or user location.
🛡 Enhanced reliability with failover and fallback mechanisms.
## 🔍 What are the benefits of request routing? Modern applications often consist of multiple microservices that serve different functions. A well-configured API Gateway can intelligently route traffic based on: * Path-based rules (/api/v1/orders → Order Service, /api/v1/users → User Service). * Header-based conditions (User-Agent: Mobile → Mobile Backend, User-Agent: Desktop → Web Backend). * Query parameters & cookies (A/B testing, canary deployments). Key Benefits: * **Efficiently Direct Requests:** Ensure each request reaches the correct service, region, or version. * **Enable Canary & Blue-Green Deployments:** Route a subset of traffic to new versions before full rollout. * **Improve Scalability:** Load balance requests across multiple backend instances. * **Enhance Security & Compliance:** Route traffic based on user identity, location, or authentication level. * **Optimize Multi-Region Deployments:** Direct users to the nearest geographically distributed service. ## Using CloudEndpoints with AgentEndpoints `AgentEndpoint` resources allow you to configure an upstream that should receive the traffic sent to the `AgentEndpoint`, but you can only specify a single upstream. `CloudEndpoint` resources do not have configuration for an upstream, but you can use Traffic Policy configuration with the [forward-internal](/docs/gateway/traffic-policy/actions/forward-internal/) action to forward to other endpoints. The most common pattern is to create an internal `AgentEndpoint` for your upstream services. An internal endpoint is used so that your upstream service is not directly accessible to the public internet, but other ngrok endpoints can route to it using the `forward-internal` action. Then, a `CloudEndpoint` resource is created for the hostname that you would like to use. The `CloudEndpoint` is then given Traffic Policy configuration to forward to your `AgentEndpoints`. This makes it simple to have a central resource for a given hostname that defines Traffic Policy actions for things like rate-limiting and authentication that you may like to enforce for the entire hostname while having all of the routing rules for the hostname located in the same location. The following example showcases how you can create two different internal `AgentEndpoint` resources for two different upstream services, and then use a `CloudEndpoint` to accept traffic for an example hostname and route to the desired `AgentEndpoint`. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: foo-service namespace: default spec: url: http://foo-service.internal:80 upstream: url: http://foo-service.example-namespace:8080 bindings: - internal ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: bar-service namespace: default spec: url: http://bar-service.internal:80 upstream: url: http://bar-service.example-namespace:8080 bindings: - internal ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io bindings: - public trafficPolicy: policy: on_http_request: - expressions: - "req.url.path.startsWith('/foo-service')" actions: - type: forward-internal config: url: http://foo-service.internal:80 - expressions: - "req.url.path.startsWith('/bar-service')" actions: - type: forward-internal config: url: http://bar-service.internal:80 ``` `CloudEndpoint` and `AgentEndpoint` resources offer a lot of flexibility in determining when to run Traffic Policy actions. Check out the [Traffic Policy expressions](/docs/gateway/traffic-policy/how-it-works#expressions) page for more information about the expressions you can write. You can also include Traffic Policy configuration on your internal AgentEndpoint resources that will run after the CloudEndpoint forwards the request to them ## Exact path matching The following examples showcase how you can match on an exact path. The request will not be routed unless it has the exact path `/example`, so `/example/foo` will not be routed. ### 1. Create an `AgentEndpoint` for your upstream `Service` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: http://example-service.internal:80 upstream: url: http://example-service.example-namespace:8080 bindings: - internal ``` ### 2. Create a `CloudEndpoint` that routes to the `AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io bindings: - public trafficPolicy: policy: on_http_request: - expressions: - "req.url.path == '/example'" actions: - type: forward-internal config: url: http://example-service.internal:80 ``` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: /example pathType: Exact backend: service: name: example-service port: number: 80 ``` Check out the [Gateway API HTTPRoute matching docs](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches) for more information on matching requests. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with Exact Path Matching ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: Exact value: /example backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 ``` ## Path prefix matching The following examples showcase how you can match using a path prefix. Any request that has a path starting with `/example` will be routed, so `/example/foo` will be routed as well. ### 1. Create an `AgentEndpoint` for your upstream `Service` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: http://example-service.internal:80 upstream: url: http://example-service.example-namespace:8080 bindings: - internal ``` ### 2. Create a `CloudEndpoint` that routes to the `AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io bindings: - public trafficPolicy: policy: on_http_request: - expressions: - "req.url.path.startsWith('/example')" actions: - type: forward-internal config: url: http://example-service.internal:80 ``` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: /example pathType: Prefix backend: service: name: example-service port: number: 80 ``` Check out the [Gateway API HTTPRoute matching docs](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches) for more information on matching requests. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with Prefix Path Matching ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: /example backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 ``` ## Request method matching The following examples showcase how to create an endpoint that only routes requests with the `POST` method. ### 1. Create an `AgentEndpoint` for your upstream `Service` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: http://example-service.internal:80 upstream: url: http://example-service.example-namespace:8080 bindings: - internal ``` ### 2. Create a `CloudEndpoint` that routes to the `AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io bindings: - public trafficPolicy: policy: on_http_request: - expressions: - "req.method == 'POST'" actions: - type: forward-internal config: url: http://example-service.internal:80 ``` ❌ `Ingress` resources do not natively support request method matching, so this option requires using Gateway API or the `CloudEndpoint`/`AgentEndpoint` resources directly. Check out the [Gateway API HTTPRoute matching docs](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches) for more information on matching requests. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with Request Method Matching ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - method: "POST" backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 ``` ## Exact header matching The following examples showcase how to create an endpoint that only routes requests if the header `x-request-header` is present with value `foo`. ### 1. Create an `AgentEndpoint` for your upstream `Service` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: http://example-service.internal:80 upstream: url: http://example-service.example-namespace:8080 bindings: - internal ``` ### 2. Create a `CloudEndpoint` that routes to the `AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io bindings: - public trafficPolicy: policy: on_http_request: - expressions: - "req.url.path.startsWith('/')" - "req.headers.exists_one(x, x == 'x-request-header') && req.headers['x-request-header'].join(',') == 'foo'" actions: - type: forward-internal config: url: http://example-service.internal:80 ``` ❌ `Ingress` resources do not natively support header matching, so this option requires using Gateway API or the `CloudEndpoint`/`AgentEndpoint` resources directly. Check out the [Gateway API HTTPRoute matching docs](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches) for more information on matching requests. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with Exact Header Matching ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - headers: - type: Exact name: x-request-header value: foo backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 ``` ## Regex header matching The following examples showcase how to create an endpoint that only routes requests if the header `x-request-header` is present and the value matches the regex `x-value-.*` ### 1. Create an `AgentEndpoint` for your upstream `Service` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: http://example-service.internal:80 upstream: url: http://example-service.example-namespace:8080 bindings: - internal ``` ### 2. Create a `CloudEndpoint` that routes to the `AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io bindings: - public trafficPolicy: policy: on_http_request: - expressions: - "req.headers.exists_one(x, x == 'x-request-header') && req.headers['x-request-header'].join(',').matches('x-value-.*')" actions: - type: forward-internal config: url: http://example-service.internal:80 ``` ❌ `Ingress` resources do not natively support header matching, so this option requires using Gateway API or the `CloudEndpoint`/`AgentEndpoint` resources directly. Check out the [Gateway API HTTPRoute matching docs](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches) for more information on matching requests. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with Regex Header Matching ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - headers: - type: RegularExpression name: x-request-header value: x-value-.* backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 ``` ## Exact query parameter matching The following examples showcase how to create an endpoint that only routes requests if the query parameter `my-query-param` is present with value `foo`. ### 1. Create an `AgentEndpoint` for your upstream `Service` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: http://example-service.internal:80 upstream: url: http://example-service.example-namespace:8080 bindings: - internal ``` ### 2. Create a `CloudEndpoint` that routes to the `AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io bindings: - public trafficPolicy: policy: on_http_request: - expressions: - "req.url.query_params.exists_one(x, x == 'my-query-param') && req.url.query_params['my-query-param'].join(',') == 'foo'" actions: - type: forward-internal config: url: http://example-service.internal:80 ``` ❌ `Ingress` resources do not natively support header matching, so this option requires using Gateway API or the `CloudEndpoint`/`AgentEndpoint` resources directly. Check out the [Gateway API HTTPRoute matching docs](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches) for more information on matching requests. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with Exact Query Parameter Matching ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - queryParams: - type: Exact name: my-query-param value: foo backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 ``` ## Regex query parameter matching The following examples showcase how to create an endpoint that only routes requests if the query parameter `my-query-param` is present and its value matches the regex `value-.*`. ### 1. Create an `AgentEndpoint` for your upstream `Service` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: http://example-service.internal:80 upstream: url: http://example-service.example-namespace:8080 bindings: - internal ``` ### 2. Create a `CloudEndpoint` that routes to the `AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io bindings: - public trafficPolicy: policy: on_http_request: - expressions: - "req.url.query_params.exists_one(x, x == 'my-query-param') && req.url.query_params['my-query-param'].join(',').matches('value-.*')" actions: - type: forward-internal config: url: http://example-service.internal:80 ``` ❌ `Ingress` resources do not natively support query parameter matching, so this option requires using Gateway API or the `CloudEndpoint`/`AgentEndpoint` resources directly. Check out the [Gateway API HTTPRoute matching docs](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches) for more information on matching requests. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with Regex Query Parameter Matching ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - queryParams: - type: RegularExpression name: my-query-param value: value-.* backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 ``` # Controlling Access with IP Restrictions Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/restrict-ips Configure IP restrictions in Kubernetes to allow or deny access based on source IP addresses using allow lists or deny lists. Instead of denying requests based on request properties like headers or paths, you may need to restrict access based on the request's source IP address. By defining allow lists (permitting only specific IP ranges) or deny lists (blocking unwanted IPs), you can: 🛡 Enhance security by blocking malicious IPs or untrusted networks.
🚀 Limit access to internal or private resources.
⚡ Prevent API abuse by restricting access to known users or organizations.
## 🔍 What are the benefits of restricting IPs? IP-based restrictions provide a simple yet effective security mechanism that doesn't require authentication tokens or additional request validation. Key Benefits: * **Block Malicious or Unauthorized Traffic:** Stop unwanted requests before they reach your backend. * **Restrict Access to Internal Users:** Ensure only approved teams or offices can reach private APIs. * **Reduce Attack Surface:** Prevent access from high-risk geographies or known malicious networks. * **Enforce Compliance Policies:** Meet regulatory requirements by restricting access to specific locations. * **Lightweight and Efficient:** No need for complex authentication mechanisms—filter requests at the network edge. ## IP restriction examples The following examples restrict access to specific IP addresses using the restrict-ips action. Check out the [IP restriction policy action](/docs/gateway/traffic-policy/actions/restrict-ips/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_tcp_connect: - actions: - type: restrict-ips config: enforce: true allow: - 1.1.1.1/32 deny: - e680:5791:be4c:5739:d959:7b94:6d54:d4b4/128 ``` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_tcp_connect: - actions: - type: restrict-ips config: enforce: true allow: - 1.1.1.1/32 deny: - e680:5791:be4c:5739:d959:7b94:6d54:d4b4/128 ``` 💡 `Ingress` resources do not natively support IP-based restrictions, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_tcp_connect: - actions: - type: restrict-ips config: enforce: true allow: - 1.1.1.1/32 deny: - e680:5791:be4c:5739:d959:7b94:6d54:d4b4/128 ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` 💡 Gateway API resources do not natively support IP-based restrictions, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_tcp_connect: - actions: - type: restrict-ips config: enforce: true allow: - 1.1.1.1/32 deny: - e680:5791:be4c:5739:d959:7b94:6d54:d4b4/128 ``` ### 2. Use the `NgrokTrafficPolicy` on a `Gateway` The following example showcases supplying the `NgrokTrafficPolicy` on a `Gateway` resource. All requests to the `Gateway` will run the Traffic Policy. If you prefer, `NgrokTrafficPolicy` can also be used on the route level by using an `externalRef` filter on an `HTTPRoute`. See the [using Gateway API guide](/docs/gateway/k8s/guides/using-gwapi) for examples. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: example-tp spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` # Customizing Request Routing with URL Rewriting Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/rewrite-url Configure URL rewriting in Kubernetes to modify request URLs before forwarding them to your upstream services. URL rewriting allows you to modify request URLs before forwarding them to your upstream service. This includes changing the hostname, path, or query parameters to ensure that requests are correctly formatted before reaching their destination. By rewriting URLs at the network edge, you can: 🔄 Standardize API request formats across different backends.
🚀 Ensure compatibility between clients and services.
⚡ Optimize backend routing without requiring changes to application code.
## 🔍 What are the benefits of URL rewriting? URL rewriting is an essential feature for API gateways, load balancers, and proxy servers, allowing for seamless request transformation before traffic reaches the backend. Key Benefits: * **Enable Backward Compatibility:** Allow older clients to use new API paths without breaking compatibility. * **Simplify Microservices Routing:** Adjust URLs dynamically when forwarding requests to multiple backend services. * **Support Multi-Tenant Architectures:** Rewrite hostnames or paths based on tenant information. * **Improve API Gateway Interoperability:** Ensure requests are formatted correctly for third-party APIs and microservices. * **Path Normalization:** Standardize incoming requests (`/v1/users` → `/api/users`). * **Hostname Rewriting:** Change hostnames (`old.example.com` → `new.example.com`). * **Backend Service Path Adjustments:** Route requests to different paths without requiring frontend changes. ## Rewrite full path The following examples showcase how to create an endpoint that rewrites requests. The hostname, scheme, and port are kept the same, but the entire path of the request is changed. * Requests to `https://example-hostname.ngrok.io/example` will be rewritten to `https://example-hostname.ngrok.io/rewrite` * Requests to `https://example-hostname.ngrok.io/example/foo` will be rewritten to `https://example-hostname.ngrok.io/rewrite` Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: url-rewrite config: from: /.* to: /rewrite ``` Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: url-rewrite config: from: /.* to: /rewrite ``` 💡 Ingress resources do not have native URL rewriting capabilities, but they can be extended using a Traffic Policy. Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: url-rewrite config: from: /.* to: /rewrite ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports URL rewriting, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with a URLRewrite Filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: /example backendRefs: - group: "" kind: Service name: example-service port: 443 weight: 1 filters: - type: URLRewrite urlRewrite: path: type: ReplaceFullPath replaceFullPath: "/rewrite" ``` ## Rewrite path prefix The following examples showcase how to create an endpoint that rewrites requests. The hostname, scheme, and port are kept the same, but only the prefix is changed. * Requests to `https://example-hostname.ngrok.io/example` will be rewritten to `https://example-hostname.ngrok.io/rewrite` * Requests to `https://example-hostname.ngrok.io/example/foo` will be rewritten to `https://example-hostname.ngrok.io/rewrite/foo` Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: url-rewrite config: from: /example(.*) to: /rewrite$1 ``` Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: url-rewrite config: from: /example(.*) to: /rewrite$1 ``` 💡 Ingress resources do not have native URL rewriting capabilities, but they can be extended using a Traffic Policy. Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: url-rewrite config: from: /example(.*) to: /rewrite$1 ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports URL rewriting, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with a URLRewrite Filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: /example filters: - type: URLRewrite urlRewrite: path: type: ReplacePrefixMatch replacePrefixMatch: "/rewrite" ``` ## Rewrite port The following examples showcase how to create an endpoint that rewrites requests. The hostname, scheme, and path are kept the same, but only the port is changed. * Requests to `https://example-hostname.ngrok.io:8080` will be rewritten to `https://example-hostname.ngrok.io:9000` Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: http://example-hostname.ngrok.io:8080 upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: url-rewrite config: from: :8080 to: :9090 ``` Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: http://example-hostname.ngrok.io:8080 trafficPolicy: policy: on_http_request: - actions: - type: url-rewrite config: from: :8080 to: :9090 ``` 💡 Ingress resources do not have native URL rewriting capabilities, but they can be extended using a Traffic Policy. Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: url-rewrite config: from: :8080 to: :9090 ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports URL rewriting, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 8080 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with a URLRewrite Filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: /example filters: - type: URLRewrite urlRewrite: port: 9090 ``` ## Rewrite hostname The following examples showcase how to create an endpoint that rewrites requests. The scheme, port, and path are kept the same, but only the hostname is changed. * Requests to `https://example-hostname.ngrok.io` will be rewritten to `https://other-example-hostname.ngrok.io` Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: url-rewrite config: from: example-hostname.ngrok.io to: other-example-hostname.ngrok.io ``` Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: url-rewrite config: from: example-hostname.ngrok.io to: other-example-hostname.ngrok.io ``` 💡 Ingress resources do not have native URL rewriting capabilities, but they can be extended using a Traffic Policy. Check out the [URL rewrite Traffic Policy action](/docs/gateway/traffic-policy/actions/url-rewrite/) page for more details about how it functions and the parameters it accepts. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: url-rewrite config: from: example-hostname.ngrok.io to: other-example-hostname.ngrok.io ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports URL rewriting, and this feature is fully supported by the ngrok Kubernetes Operator. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 2. Create an `HTTPRoute` with a URLRewrite Filter ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: /example filters: - type: URLRewrite urlRewrite: hostname: other-example-hostname.ngrok.io ``` # Serving Static Responses with Custom Response Policies Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/static-response Configure static responses in Kubernetes to return predefined responses directly from the gateway without backend calls. There are scenarios where you may want to issue static responses directly from the API Gateway rather than forwarding requests to an upstream service. This allows you to handle specific request patterns efficiently without consuming backend resources. By defining custom response rules, you can: 🛠 Return predefined error messages, such as a common 404 Not Found page.
⚡ Reduce backend load by handling certain requests at the gateway level.
🚀 Improve response times for static responses, such as maintenance messages.
## 🔍 What are the benefits of static responses? Instead of routing every request to your upstream services, certain requests can be handled at the gateway layer, improving efficiency and reducing unnecessary backend calls. Key Benefits: * **Reduce Backend Load:** Offload simple responses without hitting your backend. * **Improve Performance:** Serve common responses instantly at the edge. * **Standardize Error Handling:** Return the same response across multiple applications. * **Enhance Availability:** Provide a fallback response if your backend is unavailable. * **Handle Maintenance Windows Gracefully:** Display a scheduled maintenance message without modifying backend applications. ## Static response examples The following examples create an endpoint that returns a custom HTML maintenance page back for all requests to your endpoint. Check out the [custom response Traffic Policy action](/docs/gateway/traffic-policy/actions/custom-response/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_http_request: - actions: - type: custom-response config: status_code: 503 body:

Service Unavailable

Our servers are currently down for maintenance. Please check back later.

headers: content-type: text/html ```
```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_http_request: - actions: - type: custom-response config: status_code: 503 body:

Service Unavailable

Our servers are currently down for maintenance. Please check back later.

headers: content-type: text/html ```
💡 `Ingress` resources do not natively support serving custom responses, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: custom-response config: status_code: 503 body:

Service Unavailable

Our servers are currently down for maintenance. Please check back later.

headers: content-type: text/html ``` ### 2. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ```
💡 Gateway API resources do not natively support serving custom responses, but they can be extended using a Traffic Policy. ### 1. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_http_request: - actions: - type: custom-response config: status_code: 503 body:

Service Unavailable

Our servers are currently down for maintenance. Please check back later.

headers: content-type: text/html ``` ### 2. Use the `NgrokTrafficPolicy` on a `Gateway` The following example showcases supplying the `NgrokTrafficPolicy` on a `Gateway` resource. All requests to the `Gateway` will run the Traffic Policy. If you prefer, `NgrokTrafficPolicy` can also be used on the route level by using an `externalRef` filter on an `HTTPRoute`. See the [using Gateway API guide](/docs/gateway/k8s/guides/using-gwapi) for examples. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: example-tp spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ```
# TCP Routing Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/tcp-routing Configure TCP routing in Kubernetes to handle raw network traffic for non-HTTP protocols like databases and game servers. TCP routing allows an API Gateway to handle raw network traffic at the transport layer, enabling it to serve as an entry point for non-HTTP protocols such as databases, game servers, messaging brokers, or custom protocols. Proper TCP routing makes it possible to: 🔁 Consolidate entry points for multiple TCP-based services.
📡 Expose internal services to the internet with controlled access.
🔌 Handle any binary or custom protocol without deep inspection.
## 🔍 What are the benefits of TCP routing? Unlike HTTP and TLS routing, TCP routing operates at Layer 4 of the OSI model. The API Gateway uses the destination port or IP to determine the target backend—ideal for workloads that don't use HTTP. With TCP routing, the gateway can route connections using: * Port-based rules (for example, port 5432 → PostgreSQL service, port 4222 → NATS server). * Static or dynamic IP/port mappings to backend services. * Connection-level metadata, such as source IP or client certificate, if applicable. Key Benefits: * Protocol Agnostic Routing: Support non-HTTP services or custom TCP protocols. * High Performance: Route traffic with minimal latency and overhead. * Secure Exposure of Internal Services: Control who can access backend services on the transport layer. ## TCP routing examples The following examples showcase how you can route TCP traffic to your upstream services. You must first create a [TCP Address](/docs/gateway/domains/tcp-addresses/). When you create a TCP address, a random hostname and port will be assigned to you, for example, `1.tcp.ngrok.io:12345`. See the [TCP Endpoints page](/docs/gateway/endpoints/tcp/) for more details on how ngrok handles TCP endpoints. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: tcp://1.tcp.ngrok.io:12345 upstream: url: tcp://example-service.example-namespace:8443 ``` ### 1. Create an `AgentEndpoint` for your upstream `Service` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: tcp://example-service.internal:8443 upstream: url: tcp://example-service.example-namespace:8443 ``` ### 2. Create a `CloudEndpoint` that routes to the `AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: tcp://1.tcp.ngrok.io:12345 trafficPolicy: policy: on_http_request: - actions: - type: forward-internal config: url: tcp://example-service.internal:8443 ``` For TLS and TCP Routes, you must put the domain in the `Gateway.Spec.Addresses` field as the ngrok Operator needs to know what domains to use when creating endpoints for you and Gateway API forbids using the hostname field on Gateway listeners for TCP/TLS protocols. Since the `Gateway.Spec.Addresses` are expected to bind to all listeners, you may want to create separate Gateways for TLS and TCP endpoints to prevent unwanted conflicts and overlap. For these reasons, TCP and TLS routing is often more easily configurable using the `AgentEndpoint` resources directly instead of Gateway API configuration. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok addresses: - type: Hostname value: 1.tcp.ngrok.io listeners: - name: p12345 port: 12345 protocol: TCP ``` ### 2. Create a `TCPRoute` ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1alpha2 kind: TCPRoute metadata: name: example-tlsroute namespace: default spec: parentRefs: - name: example-gateway sectionName: p12345 rules: - backendRefs: - name: example-service port: 9000 ``` # Managing TLS Termination for Secure Connections Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/terminate-tls Configure TLS termination in Kubernetes to use custom certificates, enforce TLS versions, and enable mutual TLS. When using Ingress and Gateway API resources with the ngrok Kubernetes Operator, HTTPS endpoints are created by default, and ngrok automatically terminates TLS for secure traffic handling. By customizing TLS termination settings, you can: 🔐 Use your own TLS certificates instead of ngrok-managed certificates.
⚡ Enforce specific TLS versions for enhanced security.
🔄 Configure mutual TLS (mTLS) for bidirectional authentication between clients and your services.
## 🔍 What are the benefits of customizing TLS termination? TLS termination is essential for securing data in transit. Customizing TLS settings allows you to: * Control certificate management, choosing between automatically provisioned or manually managed TLS certificates. * Enforce strong encryption standards by restricting which TLS versions are accepted. * Enable mutual TLS (mTLS) for client authentication, adding an extra layer of security. Key Benefits: * **Enhance Security:** Enforce TLS policies that comply with industry best practices. * **Use Custom Certificates:** Deploy your own TLS certificates to meet security or compliance requirements. * **Improve Client Authentication:** Implement mutual TLS (mTLS) to verify both server and client identities. * **Ensure Compatibility:** Control TLS versions for secure connections. * **Meet Compliance Standards:** Enforce safety and security policies for HIPAA, PCI DSS, SOC 2, and other security frameworks. * **Multi-tenant Environments:** Serve different certificates per domain in multi-tenant environments. ## TLS termination examples The following examples showcase how you can create an endpoint that: * Terminates TLS using a custom certificate * Enforces mutual TLS with the clients * Allows a max TLS version of 1.3 * Requires a minimum TLS version of 1.3 ### 1. Generate certificates ```bash theme={null} # 1. Generate CA private key (ca.key) openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:2048 # 2. Generate CA certificate (ca.crt) openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=ExampleCA" # 3. Generate server private key (server.key) openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:2048 # 4. Generate server certificate signing request (CSR) (server.csr) openssl req -new -key server.key -out server.csr -subj "/CN=terminate-tls-example.ngrok.app" # 5. Generate server certificate (server.crt) openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 -sha256 # 6. Generate client private key (client.key) openssl genpkey -algorithm RSA -out client.key -pkeyopt rsa_keygen_bits:2048 # 7. Generate client certificate signing request (CSR) (client.csr) openssl req -new -key client.key -out client.csr -subj "/CN=ExampleClient" # 8. Generate client certificate (client.crt) with Client Authentication openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256 -extfile <(printf "extendedKeyUsage = clientAuth") ``` These commands will generate the following files: * CA Files: * `ca.key`: CA private key * `ca.crt`: CA certificate * Server Files: * `server.key`: Server private key * `server.csr`: Server certificate signing request * `server.crt`: Server certificate * Client Files: * `client.key`: Client private key * `client.csr`: Client certificate signing request * `client.crt`: Client certificate (with proper clientAuth extension for mTLS) ### 2. Create an endpoint with custom TLS termination Check out the [terminate TLS Traffic Policy action](/docs/gateway/traffic-policy/actions/terminate-tls/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: http://my-service.my-namespace:8080 trafficPolicy: inline: on_tcp_connect: - actions: - type: terminate-tls config: min_version: "1.3" max_version: "1.3" server_private_key: |- -----BEGIN PRIVATE KEY----- ... private key ... -----END PRIVATE KEY----- server_certificate: |- -----BEGIN CERTIFICATE----- ... certificate ... -----END CERTIFICATE----- mutual_tls_certificate_authorities: - |- -----BEGIN CERTIFICATE----- ... certificate ... -----END CERTIFICATE----- ``` Check out the [terminate TLS Traffic Policy action](/docs/gateway/traffic-policy/actions/terminate-tls/) page for more details about how it functions and the parameters it accepts. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: https://example-hostname.ngrok.io trafficPolicy: policy: on_tcp_connect: - actions: - type: terminate-tls config: min_version: "1.3" max_version: "1.3" server_private_key: |- -----BEGIN PRIVATE KEY----- ... private key ... -----END PRIVATE KEY----- server_certificate: |- -----BEGIN CERTIFICATE----- ... certificate ... -----END CERTIFICATE----- mutual_tls_certificate_authorities: - |- -----BEGIN CERTIFICATE----- ... certificate ... -----END CERTIFICATE----- ``` 💡 `Ingress` resources do not natively support advanced TLS termination options, but they can be extended using a Traffic Policy. Check out the [terminate TLS Traffic Policy action](/docs/gateway/traffic-policy/actions/terminate-tls/) page for more details about how it functions and the parameters it accepts. ### 3. Create an `NgrokTrafficPolicy` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: example-tp namespace: default spec: policy: on_tcp_connect: - actions: - type: terminate-tls config: min_version: "1.3" max_version: "1.3" server_private_key: |- -----BEGIN PRIVATE KEY----- ... private key ... -----END PRIVATE KEY----- server_certificate: |- -----BEGIN CERTIFICATE----- ... certificate ... -----END CERTIFICATE----- mutual_tls_certificate_authorities: - |- -----BEGIN CERTIFICATE----- ... certificate ... -----END CERTIFICATE----- ``` ### 4. Use the `NgrokTrafficPolicy` on an `Ingress` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: example-tp name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-hostname.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ✅ Gateway API natively supports TLS termination, and this feature is fully supported by the ngrok Kubernetes Operator. ### 3. Create a `Secret` to terminate TLS Create a Kubernetes `Secret` to configure TLS termination. ```bash theme={null} kubectl create secret tls server-cert-secret \ --cert=server.crt \ --key=server.key ``` ### 4. Create a `ConfigMap` to validate client certificates Create a Kubernetes `ConfigMap` containing the CA so that client certificates can be validated. ```bash theme={null} kubectl create configmap ca-cert-config \ --from-file=ca.crt ``` ### 5. Create a `Gateway` that terminates TLS using the generated certificates ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS tls: mode: Terminate certificateRefs: - kind: Secret name: server-cert-secret frontendValidation: caCertificateRefs: - kind: ConfigMap name: ca-cert-config options: "k8s.ngrok.com/terminate-tls.min_version": "1.2" "k8s.ngrok.com/terminate-tls.max_version": "1.3" "k8s.ngrok.com/terminate-tls.mutual_tls_verification_strategy": "require-and-verify" ``` ### 6. Generate an `HTTPRoute` to route to the upstream ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: / backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 ``` ### Use the client certificates while making requests Now that mutual TLS is enforced, you will need to include the client certificate when making requests ```bash theme={null} curl --cert client.crt --key client.key --cacert ca.crt https:// ``` # TLS Routing Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/tls-routing Configure TLS routing in Kubernetes to handle encrypted traffic using SNI-based routing with optional TLS termination. Transport Layer Security (TLS) routing enables your API Gateway to handle encrypted traffic based on the Server Name Indication (SNI) without needing to terminate the connection. By default, TLS traffic is routed without termination, preserving full end-to-end encryption. However, TLS termination can be enabled if your use case requires it. Proper TLS routing enables: 🔒 End-to-end encryption by default, with optional termination.
🌐 SNI-based routing to differentiate between secure services.
🏗 Support for multi-tenant architectures using a single IP and port.
## 🔍 What are the benefits of TLS routing? TLS routing is ideal for environments where secure communication must be preserved from client to backend. With SNI-based routing, the gateway can route requests without accessing the payload, maintaining user privacy and compliance. A well-configured API Gateway can route TLS traffic using: * SNI-based rules (for example, api.example.com → API service, auth.example.com → Auth service). * TLS passthrough (default): Maintain end-to-end encryption without terminating TLS at the gateway. * TLS termination (optional): Handle TLS termination at the gateway if you don't want your upstream services to terminate TLS. ## TLS routing examples The following examples showcase how you can route TLS traffic to your upstream services. See the [TLS Endpoints page](/docs/gateway/endpoints/tls/) for more details on how ngrok handles TLS endpoints. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: tls://example-hostname.ngrok.io:8443 upstream: url: tls://example-service.example-namespace:8443 ``` ### 1. Create an `AgentEndpoint` for your upstream `Service` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-service namespace: default spec: url: tls://example-service.internal:8443 upstream: url: tls://example-service.example-namespace:8443 ``` ### 2. Create a `CloudEndpoint` that routes to the `AgentEndpoint` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: url: tls://example-hostname.ngrok.io:8443 trafficPolicy: policy: on_http_request: - actions: - type: forward-internal config: url: tls://example-service.internal:8443 ``` For TLS and TCP Routes, you must put the domain in the `Gateway.Spec.Addresses` field as the ngrok Operator needs to know what domains to use when creating endpoints for you and Gateway API forbids using the hostname field on Gateway listeners for TCP/TLS protocols. Since the `Gateway.Spec.Addresses` are expected to bind to all listeners, you may want to create separate Gateways for TLS and TCP endpoints to prevent unwanted conflicts and overlap. For these reasons, TCP and TLS routing is often more easily configurable using the `AgentEndpoint` resources directly instead of Gateway API configuration. ### 1. Create a `Gateway` If you already have a `Gateway` you can use that instead ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: gatewayClassName: ngrok addresses: - type: Hostname value: example-hostname.ngrok.io listeners: - name: p9000 port: 9000 protocol: TLS ``` ### 2. Create a `TLSRoute` ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1alpha2 kind: TLSRoute metadata: name: example-tlsroute namespace: default spec: parentRefs: - name: example-gateway sectionName: p9000 rules: - backendRefs: - name: example-service port: 9000 ``` # Securing Upstream Connections with TLS & Mutual TLS (mTLS) Source: https://ngrok.com/docs/gateway/k8s/guides/how-to/upstream-tls Configure upstream TLS and mutual TLS in Kubernetes to secure connections between ngrok and your backend services. If your upstream service requires encrypted TLS connections, you need to ensure that requests forwarded from the API Gateway are properly secured using TLS. Additionally, if your upstream service requires mutual TLS (mTLS), you can configure client certificates to authenticate both the API Gateway and the upstream service. By configuring Upstream TLS and mTLS, you can: 🔐 Ensure end-to-end encryption between ngrok and your backend services.
⚡ Authenticate API Gateway requests using client certificates.
🚀 Enforce stricter security policies for upstream service communication.
## 🔍 What are the benefits of upstream TLS & mTLS? Securing upstream connections is essential for protecting sensitive data in transit, ensuring that API requests remain confidential and tamper-proof as they move between services. Key Benefits: * **End-to-End Encryption:** Protect sensitive data from client to backend using strong encryption. * **Client Certificate Authentication:** Ensure only trusted API gateways can connect to your backend. * **Prevent Unauthorized Access:** Block unauthorized requests by requiring valid client certificates. * **Ensure Regulatory Compliance:** Meet security standards like HIPAA, PCI DSS, and SOC 2. * **Enhance Service-to-Service Security:** Secure Kubernetes microservices using mTLS authentication. ## Upstream TLS examples The following examples showcase how you can configure client certificates to be used by the ngrok Kubernetes Operator when it connects to upstream services. ### 1. Generate certificates ```bash theme={null} # 1. Generate CA private key (ca.key) openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:2048 # 2. Generate CA certificate (ca.crt) openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=ExampleCA" # 3. Generate server private key (server.key) openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:2048 # 4. Generate server certificate signing request (CSR) (server.csr) openssl req -new -key server.key -out server.csr -subj "/CN=terminate-tls-example.ngrok.app" # 5. Generate server certificate (server.crt) openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 -sha256 # 6. Generate client private key (client.key) openssl genpkey -algorithm RSA -out client.key -pkeyopt rsa_keygen_bits:2048 # 7. Generate client certificate signing request (CSR) (client.csr) openssl req -new -key client.key -out client.csr -subj "/CN=ExampleClient" # 8. Generate client certificate (client.crt) with Client Authentication openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256 -extfile <(printf "extendedKeyUsage = clientAuth") ``` These commands will generate the following files: * CA Files: * `ca.key`: CA private key * `ca.crt`: CA certificate * Server Files: * `server.key`: Server private key * `server.csr`: Server certificate signing request * `server.crt`: Server certificate * Client Files: * `client.key`: Client private key * `client.csr`: Client certificate signing request * `client.crt`: Client certificate (with proper clientAuth extension for mTLS) ### 2. Configure your upstream If you're testing this with an upstream service that you have. It will need to make use of the following files that were generated. * `server.crt`: Used to terminate TLS * `server.key`: Used to terminate TLS * `ca.crt`: Used to validate client certificates ### 3. Create a Kubernetes Secret for the client certificate ```bash theme={null} kubectl create secret tls client-cert-secret \ --cert=client.crt \ --key=client.key ``` The following examples showcase using the client certificate when connecting to the upstream. ### 4. Create an `AgentEndpoint` that uses the client certificate ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example-agent-endpoint spec: url: https://example-hostname.ngrok.io upstream: url: https://my-service.my-namespace:8443 clientCertificateRefs: - name: client-cert-secret namespace: default ``` ❌ `Ingress` resources do not natively support advanced upstream TLS options, so this option requires using Gateway API or the `AgentEndpoint` resources directly. You can still use TLS upstream services with `Ingress` resources although you will not be able to configure mutual TLS between the Operator and the upstream service. If your service expects HTTPS instead of HTTP, then you can annotate the `Service` like the following example to let the ngrok Kubernetes Operator know which ports are HTTPS and which are HTTP ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: example-service labels: app: example-app annotations: k8s.ngrok.com/app-protocols: '{"https-port":"https","http-port":"http"}' spec: ports: - name: https-port port: 443 protocol: TCP targetPort: 8443 - name: http-port port: 80 protocol: TCP targetPort: 8080 selector: app: example-app ``` ✅ Gateway API natively supports upstream TLS configuration, and this feature is fully supported by the ngrok Kubernetes Operator. ### 4. Create a `Gateway` that uses the client certificate `Secret` ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default spec: backendTLS: clientCertificateRef: name: client-cert-secret gatewayClassName: ngrok listeners: - name: example hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### 5. Create an `HTTPRoute` that routes to the upstream ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: / backendRefs: - group: "" kind: Service name: example-service port: 443 weight: 1 ``` ### 6. Annotate the service In order for the Operator to understand what protocol each port of the `Service` expects, add the following annotation to your upstream `Service`. ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: example-service labels: app: example-app annotations: k8s.ngrok.com/app-protocols: '{"https-port":"https","http-port":"http"}' spec: ports: - name: https-port port: 443 protocol: TCP targetPort: 8443 - name: http-port port: 80 protocol: TCP targetPort: 8080 selector: app: example-app ``` # Running a Local Kubernetes Cluster Source: https://ngrok.com/docs/gateway/k8s/guides/local-cluster Learn how to set up a local Kubernetes cluster for development and testing with the ngrok Kubernetes Operator. You can install the ngrok Kubernetes Operator in a locally running Kubernetes cluster rather than paying for one from a cloud provider. This is useful for: * Quickly getting started trying out the ngrok Operator * Local development & testing * Connecting containerized workloads on your local machine to your ngrok infrastructure * Faster iteration cycles without cloud latency Running Kubernetes inside containers has the benefits of being quick and simple to setup compared to a bare-metal installation and keeps the Kubernetes environment more isolated from the dependencies and other applications on your computer. This is not a recommendation for how you should productionize your Kubernetes setup, but rather a guide on getting an easy to use local environment setup for development and testing. The following sections outline various options for running Kubernetes inside Docker containers on your local machine, but you can run the ngrok Kubernetes Operator in any Kubernetes cluster you like. ## Choosing a container runtime Before setting up a local Kubernetes cluster, you need a container runtime. The container runtime is responsible for running containers, managing their lifecycle, and handling networking. If you don't already have a container runtime such as Docker installed, choose one from the options below. | Runtime | OS Support | Architecture Support | Description | | - | - | - | - | | [Docker Engine](https://docs.docker.com/engine/install/) | Linux, ⚠️ Windows (WSL2 required) | x86\_64, ARM64 | Standard CLI-based runtime. Great for users who need a lightweight container runtime without a GUI. | | [Docker Desktop](https://www.docker.com/products/docker-desktop/) | Linux, macOS, Windows | x86\_64, ARM64 (Apple Silicon) | GUI-based runtime. Great for developers who want an easy-to-use experience with a dashboard. | | [Containerd](https://github.com/containerd/containerd) | Linux, Windows | x86\_64, ARM64, s390x, ppc64le | Minimalist, Kubernetes-native runtime. Great for Kubernetes users who need a lightweight runtime without Docker dependencies. | | [Colima](https://github.com/abiosoft/colima) | Linux, macOS | x86\_64, ARM64 (Apple Silicon) | macOS-native alternative to Docker Desktop. Great for Mac users looking for a lightweight and fast Docker-compatible runtime. | ## Choosing a cluster provider Select one of the following options for running Kubernetes inside containers on your local machine. | Provider | Benefits | Things to consider | | - | - | - | | [k3d](https://k3d.io/stable/) | Lightweight (runs on [K3s](https://k3s.io/)), fast cluster creation, low memory/CPU usage | Heavily focused on local development, making it more feature-limited | | [Minikube](https://minikube.sigs.k8s.io/docs/) | Official Kubernetes project, easy to use, [supports multiple drivers](https://minikube.sigs.k8s.io/docs/drivers/) | Compared to others may be slower and consume more CPU/memory | | [kind](https://kind.sigs.k8s.io/) | Multi-node clusters in Docker, great for testing, CI/CD | Focused on testing, other options may be better for local development | | [Microk8s](https://microk8s.io/) | Lightweight, full Kubernetes experience, easy to install | Config not automatically added to Kubeconfig, requires manual addition | | [Docker Desktop](https://www.docker.com/products/docker-desktop/) | Simple interface, Kubernetes support | GUI-based, may be slower than other options | ### Basic commands #### Create a k3d cluster ```bash theme={null} k3d cluster create my-cluster ``` When creating a k3d cluster, the config is automatically added to your Kubeconfig file so you are ready to go immediately. #### List running clusters ```bash theme={null} k3d cluster list ``` #### Delete a k3d cluster ```bash theme={null} k3d cluster delete my-cluster ``` #### Create a Minikube cluster ```bash theme={null} minikube start ``` When creating a Minikube cluster, the config is automatically added to your Kubeconfig file so you are ready to go immediately. #### List running clusters ```bash theme={null} minikube profile list ``` #### Delete a Minikube cluster ```bash theme={null} minikube delete ``` #### Create a kind cluster ```bash theme={null} kind create cluster --name my-cluster ``` When creating a kind cluster, the config is automatically added to your Kubeconfig file so you are ready to go immediately. #### List running clusters ```bash theme={null} kind get clusters ``` #### Delete a kind cluster ```bash theme={null} kind delete cluster --name my-cluster ``` #### Create a Microk8s cluster ```bash theme={null} microk8s start ``` When creating a Microk8s cluster, the config is **NOT** automatically added to your Kubeconfig file, so you can use the following command to add it. ```bash theme={null} microk8s config > $KUBECONFIG ``` #### List running clusters ```bash theme={null} microk8s stop ``` #### Delete a Microk8s cluster ```bash theme={null} microk8s reset ``` [Docker Desktop](https://www.docker.com/products/docker-desktop/) offers a simple to use interface that includes support for running Kubernetes in docker with [its Kubernetes feature](https://docs.docker.com/desktop/features/k8s/) ## Managing Kubernetes contexts When working with multiple Kubernetes clusters (for example, Minikube, k3d, kind, MicroK8s, cloud clusters), it is important to manage **kubeconfig contexts** efficiently. A **context** in Kubernetes determines: * **Which cluster** `kubectl` is communicating with. * **Which user credentials** to use for authentication. * **Which namespace** is the default for commands. Kubernetes stores this information in the **kubeconfig file** (default location: `~/.kube/config`). The `$KUBECONFIG` environment variable is used by `kubectl` to know where your configuration file is. If you would like to store it somewhere else, or use different files for different clusters, you can modify the `$KUBECONFIG` variable. ### Viewing the current context To check which Kubernetes cluster you are currently using: ```bash theme={null} kubectl config current-context ``` This returns the active context, which tells kubectl which cluster and credentials are being used. ### Listing all contexts To see all available Kubernetes contexts in your kubeconfig: ```bash theme={null} kubectl config get-contexts ``` This will display a list of available contexts, including: * Cluster Name (for example, minikube, kind-my-cluster, k3d-my-cluster). * User Credentials. * Current Context (marked with \*). ### Switching between contexts To switch from one Kubernetes cluster to another: ```bash theme={null} kubectl config use-context my-cluster ``` ### Setting a default namespace for a context By default, kubectl uses the default namespace. You can change the default namespace for a specific context: ```bash theme={null} kubectl config set-context --current --namespace=my-namespace ``` Or, specify a namespace when switching to a different context: ```bash theme={null} kubectl config set-context my-cluster --namespace=custom-namespace ``` ### Deleting a context If you no longer need a specific context, you can remove it from your kubeconfig: ```bash theme={null} kubectl config delete-context ``` This does not delete the cluster itself, only its entry from your kubeconfig. ### Merging multiple kubeconfig files If you have multiple kubeconfig files (for example, from cloud providers and local clusters), you can merge them: ```bash theme={null} export KUBECONFIG=~/.kube/config:~/.kube/k3d-config:~/.kube/microk8s-config kubectl config view --merge --flatten > ~/.kube/config-merged mv ~/.kube/config-merged ~/.kube/config ``` This consolidates multiple different cluster credentials into a single kubeconfig file. # Projecting Local Services into Remote Clusters Source: https://ngrok.com/docs/gateway/k8s/guides/local-projection Learn how to use Kubernetes bindings to instantly test local services against remote staging or production-like clusters without slow CI/CD loops. Kubernetes bindings let you "project" a service running on your local machine into a remote Kubernetes cluster, making it available to other pods and services. This approach eliminates the slow Kubernetes dev loop in favor of instant feedback by routing traffic from your remote cluster directly to your local machine. ## How it works A Kubernetes-bound endpoint is a private endpoint only accessible inside clusters where you've installed the ngrok Kubernetes Operator. When you create one, the Operator automatically creates a `Service` resource in your cluster that routes traffic to your endpoint. This means: * Your local service appears as a normal Kubernetes `Service` in the remote cluster * Other pods can make requests to it using standard service DNS * No public exposure—traffic stays private through ngrok's network * No port forwarding or VPN configuration required ## Prerequisites * An ngrok account with an [API key](https://dashboard.ngrok.com/api-keys) and [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) * The [ngrok CLI](/docs/gateway/agent/cli/) installed on your local machine * A local service running on a port you want to expose * Access to a remote Kubernetes cluster ## 1. Set up the remote cluster Install the ngrok Kubernetes Operator with bindings enabled: ```bash theme={null} helm install ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --create-namespace \ --set bindings.enabled=true \ --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN ``` Create a namespace for your projected services: ```bash theme={null} kubectl create ns staging ``` ### Restrict which endpoints can be projected (optional) You can limit which Kubernetes-bound endpoints appear in your cluster using endpoint selectors. For example, to only allow endpoints matching `*.staging`: ```bash theme={null} helm upgrade ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --reuse-values \ --set 'bindings.endpointSelectors={endpoint.url.contains(".staging")}' ``` See [Restrict Kubernetes bound endpoints](/docs/gateway/k8s/guides/bindings/#restrict-kubernetes-bound-endpoints) for more details. ## 2. Project your local service With your local service running (for example, on port `8080`), create a Kubernetes-bound endpoint. The ngrok CLI works best if you're *not* running the service in a [local K8s cluster](/docs/gateway/k8s/guides/local-cluster), like [k3d](https://k3d.io/stable/) or [kind](https://kind.sigs.k8s.io/)—for that, use the ngrok Kubernetes Operator on the local cluster as well. ```bash theme={null} ngrok http 8080 --url http://my-service.staging --binding kubernetes ``` The URL format is `http://.`, which determines where the `Service` resource gets created in your cluster. If you want to project a service using the Operator instead (useful for projecting services between clusters), create an `AgentEndpoint`: ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: my-service-projection namespace: staging spec: url: http://my-service.staging bindings: - kubernetes upstream: url: http://localhost:8080 ``` Once started, the Operator creates a `Service` named `my-service` in the `staging` namespace. ## 3. Test the connection Verify the projected service is accessible from within your *remote* cluster: ```bash theme={null} kubectl run -i --tty --rm debug \ --namespace staging \ --restart=Never \ --image=curlimages/curl \ -- curl http://my-service.staging ``` You should see the response from your local service, and the request will appear in your ngrok agent output. ## 4. Redirect existing traffic to your local service If other services in your cluster already communicate with a service you want to replace, you can redirect their traffic to an endpoint running on your local machine. For example, if a `frontend` deployment sends requests to a `backend` service configured via an environment variable: 1. Start your Kubernetes-bound endpoint with the same service name: ```bash theme={null} ngrok http 8080 --url http://backend.staging --binding kubernetes ``` 2. Update the frontend deployment to point to your projected service: ```bash theme={null} kubectl set env deployment/frontend BACKEND_URL=http://backend.staging ``` 3. Restart the frontend pods: ```bash theme={null} kubectl rollout restart deployment/frontend -n staging ``` Now all requests from `frontend` to `BACKEND_URL` route to your local machine. When you make changes locally using your IDE, they'll be instantly reflected in the remote cluster, and services in staging interact direct with your local version. Test and iterate until you're ready to create a pull request and push more formally to your CI/CD pipeline, then revert the environment variable, perform another rollout, and stop your ngrok agent to clean everything up. ## What's next * [Bindings](/docs/gateway/k8s/guides/bindings/): Learn about all binding types and how they work with the Operator * [Kubernetes endpoints](/docs/gateway/endpoints/kubernetes-endpoints/): Deep dive into URL formats and service creation * [CloudEndpoint CRD](/docs/gateway/k8s/crds/cloudendpoint/): Create persistent Kubernetes-bound endpoints declaratively # ngrok Kubernetes Operator Managed Resources Source: https://ngrok.com/docs/gateway/k8s/guides/managed-resources Learn about the resources that the ngrok Kubernetes Operator manages. The ngrok Kubernetes Operator has many different custom resource definitions that are installed along with it. Many of these are resources that you can create on your own, others are primarily created and managed by the Operator. ## Managed labels The ngrok Operator uses the following labels to mark which resources it created and manages versus resources that you create and manage manually. The values are determined by your installation name and namespace. ```yaml theme={null} k8s.ngrok.com/controller-name: ngrok-operator-manager k8s.ngrok.com/controller-namespace: ngrok-operator ``` Any resource with these labels will have its lifecycle controlled by the ngrok Operator. They should never be used on resources you create and manage yourself. When the Operator translates `Ingress` and Gateway API configuration resources into `AgentEndpoint` and `CloudEndpoint` resources for you, it marks those as managed resources using the above labels. Adding these labels to resources you create and manage yourself can cause the Operator to delete them since it thinks that the resource was created from an `Ingress`/etc. that no longer exists. You can use these labels to your advantage to list the resources that the Operator manages. For example, to get all `AgentEndpoint` resources that are created and managed by the Operator: ```bash theme={null} kubectl get aep --all-namespaces -l k8s.ngrok.com/controller-name=ngrok-operator-manager,k8s.ngrok.com/controller-namespace=ngrok-operator ``` # Running Multiple ngrok Operator Installations Source: https://ngrok.com/docs/gateway/k8s/guides/multiple-installs How to run multiple instances of the ngrok Kubernetes Operator in a single cluster using namespace-scoped installations. By default, the `ngrok-operator` watches for resources such as `Ingress` objects and ngrok custom resources across all namespaces. For more advanced setups, you can install the operator multiple times within a single cluster, with each installation scoped to its own namespace. Common reasons to run multiple installations include: * **Separate ngrok accounts** for different environments (dev, staging, prod) within one cluster * **Team isolation** where each team manages their own operator and resources in their own namespace ## Prerequisites: Install CRDs separately CRDs are cluster-scoped resources. If multiple operator installations each try to manage the same CRDs, you'll get Helm ownership conflicts. You **must** install the CRDs separately first using the `ngrok-crds` chart and set `installCRDs=false` on every operator installation. ```bash theme={null} helm repo add ngrok https://charts.ngrok.com helm repo update helm install ngrok-crds ngrok/ngrok-crds ``` If one operator release includes the CRDs, all operators depend on them. Uninstalling that specific release would remove the CRDs and break every other operator. Always install CRDs separately when using multiple operators. If you already have an existing operator installation with bundled CRDs, see [Migrating CRDs to a separate chart](#migrating-crds-to-a-separate-chart) below. ## Namespace-scoped installations Each operator installation must be scoped to watch a specific namespace using `ingress.watchNamespace`. This ensures each operator only reconciles `Ingress` objects and custom resources within its own namespace, including any derived resources like `CloudEndpoint` and `AgentEndpoint` that the operator creates from `Ingress` objects. ```bash theme={null} helm install ngrok-operator ngrok/ngrok-operator \ --namespace=team-a \ --create-namespace \ --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN \ --set installCRDs=false \ --set ingress.watchNamespace=team-a helm install ngrok-operator ngrok/ngrok-operator \ --namespace=team-b \ --create-namespace \ --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN \ --set installCRDs=false \ --set ingress.watchNamespace=team-b ``` Now you can create `Ingress` objects or custom resources like `CloudEndpoint` in either namespace, and only the operator watching that namespace will reconcile them. Resources created in a namespace without a corresponding operator will not be reconciled. You **must** use `ingress.watchNamespace` to scope each operator to its own namespace. Using only `ingress.ingressClass.name` to separate operators is **not sufficient**. The operator creates derived custom resources (such as `CloudEndpoint` and `AgentEndpoint`) from `Ingress` objects. While `ingress.watchNamespace` limits the operator's watch scope for both `Ingress` objects and these derived resources, there is no mechanism to filter derived resource events by the `ingressClass` of the parent `Ingress` that caused them to be created. Without namespace separation, multiple operators would attempt to reconcile each other's derived resources. Each operator already defaults to the `ngrok` ingress class, so namespace scoping alone is sufficient to separate multiple installations. You can optionally set `ingress.ingressClass.name` to use a custom class name, but this is not required for most setups. ## Migrating CRDs to a separate chart If you already have an operator installation with bundled CRDs, you can migrate them to the `ngrok-crds` chart without downtime. When Helm installs resources, it applies ownership annotations to track which release manages them: ```yaml theme={null} metadata: annotations: meta.helm.sh/release-name: ngrok-operator meta.helm.sh/release-namespace: ngrok-operator ``` Trying to install the `ngrok-crds` chart while these annotations point to a different release will fail. To migrate safely, follow these steps: **Step 1:** Annotate all ngrok CRDs with `helm.sh/resource-policy=keep`. This prevents the old operator release from deleting the CRDs when you update it, since the CRDs are still tracked in the old release's manifest: ```bash theme={null} for crd in $(kubectl get crds -o name | grep ngrok); do kubectl annotate $crd helm.sh/resource-policy=keep --overwrite done ``` Verify the annotation is present on all CRDs: ```bash theme={null} kubectl get crds -o name | grep ngrok | xargs -I{} kubectl get {} -o jsonpath='{.metadata.name}: {.metadata.annotations.helm\.sh/resource-policy}{"\n"}' ``` This migration relies on Helm honoring the `helm.sh/resource-policy=keep` annotation on live CRD objects. Test this procedure in a non-production cluster before running it in production. **Step 2:** Re-annotate the CRDs to be owned by the `ngrok-crds` release. Replace `CRDS_NAMESPACE` with the namespace where you plan to install the `ngrok-crds` release (for example, `default`): ```bash theme={null} for crd in $(kubectl get crds -o name | grep ngrok); do kubectl annotate $crd \ meta.helm.sh/release-name=ngrok-crds \ meta.helm.sh/release-namespace=CRDS_NAMESPACE \ --overwrite done ``` **Step 3:** Install the `ngrok-crds` chart into the same namespace you used above (this will be a no-op since the CRDs already exist): ```bash theme={null} helm install ngrok-crds ngrok/ngrok-crds --namespace=CRDS_NAMESPACE ``` **Step 4:** Update your existing operator to stop managing CRDs: ```bash theme={null} helm upgrade ngrok-operator ngrok/ngrok-operator \ --namespace=ngrok-operator \ --reuse-values \ --set installCRDs=false ``` **Step 5:** Remove the `resource-policy` annotation so that future `helm uninstall ngrok-crds` can properly delete the CRDs when you're ready: ```bash theme={null} for crd in $(kubectl get crds -o name | grep ngrok); do kubectl annotate $crd helm.sh/resource-policy- --overwrite done ``` You can now install additional operators with `installCRDs=false`. ### Installing without separating CRDs You can install a second operator with `installCRDs=false` without first migrating CRDs to a separate chart. However, this means one operator release owns the CRDs that all operators depend on. If that specific release is uninstalled, it will remove the CRDs and break every other operator. ## Uninstalling one of multiple operators For detailed uninstall procedures, see [Uninstalling the Operator](/docs/gateway/k8s/installation/uninstall#multiple-installations). Key points: * Uninstall operators that have `installCRDs=false` first * Uninstall the CRDs last (via `helm uninstall ngrok-crds`) only after all operators are removed * When using `drainPolicy=Delete`, the cleanup job respects the operator's `ingress.watchNamespace` and only deletes resources within that scope # Pooling Endpoints in the ngrok Kubernetes Operator Source: https://ngrok.com/docs/gateway/k8s/guides/pooling Learn how to load balance traffic between endpoints using the ngrok Kubernetes Operator. Endpoint pooling is the concept of creating two or more endpoints that share the same URL and have pooling enabled (if you don't enable pooling for an endpoint it cannot have the same URL as another endpoint). The [endpoint pooling page](/docs/gateway/endpoints/endpoint-pooling/) goes over concepts and details of pooling in more depth. This guide focuses on how to use and configure endpoint pooling while using the ngrok Kubernetes Operator. Note that while endpoint pooling is supported by the ngrok Kubernetes Operator, endpoint pools are not specific to the environment in which they were created. This means that you can pool endpoints created with the Kubernetes Operator with endpoints started using the CLI, etc. This is a powerful and important concept to keep in mind to avoid unwanted pooling. Pooling must be enabled by all endpoints that share the same URL. Any attempt to create a pooled endpoint for a URL already in-use by a non-pooled endpoint will fail. Similarly, any endpoint attempting to create an endpoint that does not enable pooling and uses a URL that is already in-use by one or more endpoints with pooling enabled will fail. ## Endpoint Pooling with `AgentEndpoints` Within the Kubernetes Operator, `AgentEndpoint` resources are currently **Always Pooled**. This means that if you create the same `AgentEndpoint` in two different Kubernetes clusters, or with a URL shared by any other type of endpoint that has pooling enabled, then they will all be part of the same pool. When the Kubernetes Operator creates `AgentEndpoint` resources for you by translating `Ingress` and Gateway API resources, it will prefix the generated `AgentEndpoint` URL with a special hash to avoid unintended pooling. ## Endpoint Pooling with `CloudEndpoints` `CloudEndpoint` resources default to having pooling disabled, but you can enable it with the `spec.poolingEnabled: true` field such as with the following example. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: static-response namespace: default spec: policy: on_http_request: - name: example-rule actions: - type: custom-response config: body: Example response from Traffic Policy headers: content-type: text/plain status_code: 200 --- apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: example-cloud-endpoint spec: poolingEnabled: true url: https://example-cloud-endpoint.ngrok.io trafficPolicyName: static-response ``` ## Endpoint Pooling with `Ingresses` If you are using `Ingress` resources with the Operator, you can enable endpoint pooling for an `Ingress` using the `k8s.ngrok.com/pooling-enabled: "true"` annotation. The following will create an `Ingress` that creates a `CloudEndpoint` with pooling enabled. Any other endpoint created with the same URL and pooling enabled will be pooled with the `CloudEndpoint` that this `Ingress` creates. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/pooling-enabled: "true" name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-ingresses.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` Since the Operator collapses `Ingress` resources that share the same hostname, you must make sure that all `Ingress` resources that use a given hostname use the same pooling enabled or disabled configuration. See the [using Ingresses guide](/docs/gateway/k8s/guides/using-ingresses) for more information. ## Endpoint Pooling with Gateway API If you are using Gateway API for configuration, you can enable pooling using the using the `k8s.ngrok.com/pooling-enabled: "true"` annotation on `Gateway` resources. The following will create a `CloudEndpoint` with pooling enabled. Any other endpoint created with the same URL and pooling enabled will be pooled with the `CloudEndpoint`. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/pooling-enabled: "true" spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: / backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 ``` # Troubleshooting the ngrok Kubernetes Operator Source: https://ngrok.com/docs/gateway/k8s/guides/troubleshooting Learn how to troubleshoot common issues with the ngrok Kubernetes Operator using kubectl describe, events, and logs. ## Check resource status ngrok custom resources include status conditions that report their current state. Use `kubectl describe` to check conditions and events on a resource: ```bash theme={null} kubectl describe cloudendpoint -n ``` The `Conditions` section in the output shows whether the resource is `Ready` and any errors encountered. For more details on the conditions available for each resource type, see the [CRD reference](/docs/gateway/k8s/crds). ## Check Kubernetes events If something is not right with the Operator you can check the events for the Operator namespace. ```bash theme={null} kubectl get events -n ngrok-operator --sort-by='.lastTimestamp' ``` Example Output: ```bash theme={null} LAST SEEN TYPE REASON OBJECT MESSAGE 3m24s Normal Scheduled pod/ngrok-operator-manager-657c5b69d7-f7vvg Successfully assigned ngrok-operator/ngrok-operator-manager-657c5b69d7-f7vvg to k3d-ngrok-operator-server-0 3m25s Normal ScalingReplicaSet deployment/ngrok-operator-manager Scaled up replica set ngrok-operator-manager-657c5b69d7 to 1 3m25s Normal SuccessfulCreate replicaset/ngrok-operator-manager-657c5b69d7 Created pod: ngrok-operator-manager-657c5b69d7-f7vvg 3m24s Normal Started pod/ngrok-operator-manager-657c5b69d7-f7vvg Started container ngrok-operator 3m24s Normal Created pod/ngrok-operator-manager-657c5b69d7-f7vvg Created container ngrok-operator 3m24s Normal Pulled pod/ngrok-operator-manager-657c5b69d7-f7vvg Container image "docker.io/ngrok/ngrok-operator:0.15.0" already present on machine 3m15s Normal ScalingReplicaSet deployment/ngrok-operator-manager Scaled down replica set ngrok-operator-manager-74b76d8f6d to 0 from 1 3m15s Normal SuccessfulDelete replicaset/ngrok-operator-manager-74b76d8f6d Deleted pod: ngrok-operator-manager-74b76d8f6d-2679x 3m15s Normal Killing pod/ngrok-operator-manager-74b76d8f6d-2679x Stopping container ngrok-operator 2m55s Normal Updating boundendpoint/ngrok-22680758-eb09-576e-a7ac-dc3728458dfc Updating BoundEndpoint/ngrok-22680758-eb09-576e-a7ac-dc3728458dfc 2m55s Normal Updated boundendpoint/ngrok-012050e0-8f06-560c-953e-c9d675d41305 Updated BoundEndpoint/ngrok-012050e0-8f06-560c-953e-c9d675d41305 2m55s Normal Updated boundendpoint/ngrok-22680758-eb09-576e-a7ac-dc3728458dfc Updated BoundEndpoint/ngrok-22680758-eb09-576e-a7ac-dc3728458dfc 2m55s Normal Status boundendpoint/ngrok-22680758-eb09-576e-a7ac-dc3728458dfc Successfully reconciled status 2m55s Normal Updated boundendpoint/ngrok-22680758-eb09-576e-a7ac-dc3728458dfc Updated Services 2m55s Normal Updated service/ngrok-22680758-eb09-576e-a7ac-dc3728458dfc Updated Upstream Service 2m55s Normal Updating boundendpoint/ngrok-012050e0-8f06-560c-953e-c9d675d41305 Updating BoundEndpoint/ngrok-012050e0-8f06-560c-953e-c9d675d41305 2m55s Normal LeaderElection lease/ngrok-operator-leader ngrok-operator-manager-657c5b69d7-f7vvg_72fb437b-38ad-444c-a9d8-d37349f2f677 became leader 2m55s Normal Status boundendpoint/ngrok-012050e0-8f06-560c-953e-c9d675d41305 Successfully reconciled status 2m55s Normal Updated boundendpoint/ngrok-012050e0-8f06-560c-953e-c9d675d41305 Updated Services 2m55s Normal Updated service/ngrok-012050e0-8f06-560c-953e-c9d675d41305 Updated Upstream Service 2m55s Normal Updating kubernetesoperator/ngrok-operator Updating KubernetesOperator/ngrok-operator 2m55s Normal Status kubernetesoperator/ngrok-operator Successfully reconciled status 2m55s Normal Updated kubernetesoperator/ngrok-operator Updated KubernetesOperator/ngrok-operator ``` Kubernetes events usually expire after an hour, so you might want to restart the Operator to re-create the events while troubleshooting if the events happened more than an hour ago. ## Check Operator logs The operator runs two main deployments: `ngrok-operator-manager` (handles `CloudEndpoint`, `Domain`, `Ingress`, Gateway API, and other cluster-level resources) and `ngrok-operator-agent` (handles `AgentEndpoint` resources). To check for errors in either: ```bash theme={null} kubectl logs -n ngrok-operator deploy/ngrok-operator-manager | grep -i error kubectl logs -n ngrok-operator deploy/ngrok-operator-agent | grep -i error ``` Replace the namespace if you installed the Operator somewhere other than the default location. ## Resources not getting deleted If you marked an ngrok custom resource for deletion but it is not getting deleted, there may be unresolved finalizers on it. You can refer to the [finalizers page](/docs/gateway/k8s/guides/finalizers) for more information about how the Operator uses finalizers and how to clean up resources with dangling finalizers. If this is happening during or after an uninstall, see the [uninstall troubleshooting guide](/docs/gateway/k8s/installation/uninstall#troubleshooting) for specific steps to resolve stuck resources and namespaces. ## Cloud Endpoints not getting created from Ingress or Gateway API If you're applying `Ingress` and/or Gateway API resources and do not see any `CloudEndpoint` or `AgentEndpoint` custom resources created by the Operator, here are a few steps to identify the reason. ### Ingress uses ngrok IngressClass? The ngrok `IngressClass` is required to use `Ingress` resources. Check that your `Ingress` uses the ngrok `IngressClass` like the following example. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-ingress.ngrok.io http: paths: - backend: service: name: example-service port: number: 80 path: / pathType: Prefix ``` ### Gateway API uses the ngrok GatewayClass? The ngrok `GatewayClass` is required to use Gateway API resources. If you do not already have it installed, you can create it with: ```bash theme={null} kubectl apply -f -< Request mirror filters are not yet supported with the ngrok Kubernetes Operator. ## How does the ngrok Operator use Gateway API? The ngrok Kubernetes Operator consumes Gateway API resources and translates them into the native ngrok `CloudEndpoint` and `AgentEndpoint` custom resources. Each hostname from the listeners on the `Gateway` resources you configure will cause the Operator to create a `CloudEndpoint` for the listener's hostname. Each unique upstream service you reference using `HTTPRoute` resources will cause the Operator to create an `AgentEndpoint` with an `internal` binding so that it is not accessible directly on the internet. The Operator then generates ngrok [Traffic Policy](/docs/gateway/traffic-policy/) configuration on those `CloudEndpoint` to perform all the routing and actions specified in your Gateway API configuration and route to the internal `AgentEndpoint` resources that handle forwarding traffic to your upstream services. When you create/update/delete Gateway API resources, the Operator will automatically handle creating/updating/deleting the `CloudEndpoint` and `AgentEndpoint` resources as needed. ## Using `NgrokTrafficPolicy` with Gateway API resources The Operator will consume `Gateway` and `HTTPRoute` resources as necessary and build endpoints to provide the desired routing behavior. The Gateway API filters on the `HTTPRoute` resources can be used to configure commonly used features such as redirecting requests and manipulating headers, but there may still be other use-cases and features that are not natively supported by the Gateway API that you would like to use. This is where you can take advantage of ngrok's Traffic Policy system to extend the functionality of your Gateway API resources. ### Using `NgrokTrafficPolicy` on `Gateways` You can supply an `NgrokTrafficPolicy` custom resource on your `Gateway` resources using an annotation. When configured this way, the rules from your Traffic Policy will run for all requests matching any of the hostnames from the listeners on your `Gateway`. The Traffic Policy may "terminate" the request by sending back a response or forwarding the request to another endpoint using the `forward-internal` action, but if this happens then the request will not be processed further and will not be routed using any configuration from any matching `HTTPRoutes`. This is most commonly done to enforce policy across an entire `Gateway` such as enforcing authentication or manipulating headers where you do not care which `HTTPRoute` the request matches. The following example showcases using a `Gateway` with an `NgrokTrafficPolicy` supplied via annotation. The `NgrokTrafficPolicy` must be in the same namespace as the `Gateway` ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: remove-header namespace: default spec: policy: on_http_request: - name: Remove Bad Header actions: - type: remove-headers config: headers: - "X-Removed-Header" --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: remove-header spec: gatewayClassName: ngrok listeners: - name: example-hostname hostname: "example-hostname.ngrok.io" port: 443 protocol: HTTPS ``` ### Use `NgrokTrafficPolicy` on `HTTPRoutes` You can also use `NgrokTrafficPolicy` on `HTTPRoute` resources as an external ref filter if you have Traffic Policies that should only be run for certain routes. The following example showcases using an `HTTPRoute` with an `NgrokTrafficPolicy` filter. They can also be used at the backend filter level which is useful if you have more than one upstream service in a group and you would like to run different Traffic Policy configurations depending on which backend was selected. The `NgrokTrafficPolicy` must be in the same namespace as the `Gateway`. While redundant, this example showcases both methods. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: basic-auth namespace: default spec: policy: on_http_request: - actions: - type: basic-auth config: realm: sample-realm credentials: - user:password1 - admin:password2 enforce: true - type: custom-response config: status_code: 200 headers: content-type: text/plain body: Successfully Authenticated! --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: hostnames: - example-hostname.ngrok.io parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: example-gateway namespace: default rules: - matches: - path: type: PathPrefix value: / backendRefs: - group: "" kind: Service name: example-service port: 80 weight: 1 filters: - type: ExtensionRef extensionRef: group: "ngrok.k8s.ngrok.com" kind: "NgrokTrafficPolicy" name: basic-auth filters: - type: ExtensionRef extensionRef: group: "ngrok.k8s.ngrok.com" kind: "NgrokTrafficPolicy" name: basic-auth ``` ## Gateway API HTTPS upstreams If you have an `HTTPRoute` that routes to a Service that expects HTTPS instead of HTTP, you can specify this using the `k8s.ngrok.com/app-protocols` annotation on your `Service`. For example, the following `Service` uses the annotation to let the Operator know which ports use HTTPS ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: example-service labels: app: example-app annotations: k8s.ngrok.com/app-protocols: '{"https-port":"https","http-port":"http"}' spec: ports: - name: https-port port: 443 protocol: TCP targetPort: 8443 - name: http-port port: 80 protocol: TCP targetPort: 8080 selector: app: example-app ``` ## Gateway API status conditions The Gateway API has guidance on status and condition handling in [GEP-1364](https://gateway-api.sigs.k8s.io/geps/gep-1364/) and the Gateway API [Implementer's guide](https://gateway-api.sigs.k8s.io/guides/implementers/#standard-status-fields-and-conditions). The ngrok-operator sets status and conditions on Gateway API resources. ### Viewing gateway statuses You can view your gateways and whether or not they have been programmed by running `kubectl get gateways.gateway.networking.k8s.io --all-namespaces`: ```shell theme={null} NAMESPACE NAME CLASS ADDRESS PROGRAMMED AGE infra custom-gateway ngrok vxvuakisp8tmkwmn.4ras8yu7nq6zsudp3.ngrok-cname.com True 97d infra invalid-http-port-gw ngrok invalid-gw-2.tests.ngrok.io Unknown 62d infra invalid-https-port-gw ngrok invalid-gw-1.tests.ngrok.io Unknown 62d infra invalid-no-hostname-http-gw ngrok Unknown 62d infra invalid-no-hostname-https-gw ngrok Unknown 62d infra shared-gateway ngrok gateway.my-sub-domain.ngrok.io True 140d ``` In this example, you can see that 2 gateways have been successfully programmed in ngrok. The first one, `custom-gateway`, is using a [Custom Domain](./custom-domain) and shows the ngrok CNAME assigned to it. The last one, `shared-gateway`, is using an ngrok managed domain and has also been programmed. The other Gateways you see here are invalid and not able to be programmed. Check the reason why a gateway is invalid by checking the status condition on the gateway and the listener status conditions with `kubectl -n infra get gateway -o yaml invalid-http-port-gw`: ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-http-port-gw namespace: infra spec: gatewayClassName: ngrok listeners: - allowedRoutes: kinds: - group: gateway.networking.k8s.io kind: HTTPRoute namespaces: from: All hostname: invalid-gw-2.tests.ngrok.io name: http port: 443 protocol: HTTP status: addresses: - type: Hostname value: invalid-gw-2.tests.ngrok.io conditions: - lastTransitionTime: "2025-06-11T15:00:27Z" message: gateway listeners are not valid observedGeneration: 1 reason: ListenersNotValid status: "False" type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed listeners: - attachedRoutes: 0 conditions: - lastTransitionTime: "2025-06-11T15:00:27Z" message: ngrok only supports HTTP on port 80 observedGeneration: 1 reason: PortUnavailable status: "False" type: Accepted - lastTransitionTime: "2025-06-11T20:28:15Z" message: listener is not valid observedGeneration: 1 reason: Invalid status: "False" type: Programmed name: http supportedKinds: [] ``` The listener for this Gateway is invalid because the listener specified listening for HTTP on port 443 and ngrok only supports HTTPS listeners on port 443. # How the ngrok Operator Uses Ingresses Source: https://ngrok.com/docs/gateway/k8s/guides/using-ingresses Learn how the ngrok Kubernetes Operator consumes Ingress resources and translates them into CloudEndpoint and AgentEndpoint resources. ## How the ngrok Operator uses Ingresses The ngrok Kubernetes Operator consumes `Ingress` resources and translates them into the native ngrok `CloudEndpoint` and `AgentEndpoint` custom resources. Each hostname from the `Ingress` resources you configure will cause the Operator to create a `CloudEndpoint` for the hostname. Each unique upstream service you reference using `HTTPRoute` resources will cause the Operator to create an `AgentEndpoint` with an `internal` binding so that it is not accessible directly on the internet. The Operator then generates ngrok [Traffic Policy](/docs/gateway/traffic-policy/) configuration on those `CloudEndpoint` to perform all the routing and actions specified in your Gateway API configuration and route to the internal `AgentEndpoint` resources that handle forwarding traffic to your upstream services. When you create/update/delete Gateway API resources, the Operator will automatically handle creating/updating/deleting the `CloudEndpoint` and `AgentEndpoint` resources as needed. ## Fan-out versus multiple The Operator supports using a single ingress for your hostname that includes several rules such as the below example. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-ingress.ngrok.io http: paths: - path: /foo pathType: Prefix backend: service: name: foo-service port: number: 80 - path: /bar pathType: Prefix backend: service: name: bar-service port: number: 80 ``` Or you can create multiple different `Ingress` resources that produce the same effect such as the example below. The Operator will translate both examples into the same configuration. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress-foo namespace: default spec: ingressClassName: ngrok rules: - host: example-ingress.ngrok.io http: paths: - path: /foo pathType: Prefix backend: service: name: foo-service port: number: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress-bar namespace: default spec: ingressClassName: ngrok rules: - host: example-ingress.ngrok.io http: paths: - path: /bar pathType: Prefix backend: service: name: bar-service port: number: 80 ``` ## Using `NgrokTrafficPolicy` with `Ingress` To extend the functionality of `Ingress` resources, you have the option of combining them with `NgrokTrafficPolicy` resources to take advantage of ngrok's [Traffic Policy](/docs/gateway/traffic-policy/) system. `NgrokTrafficPolicy` can be used with `Ingress` resources in three different ways: * As an annotation * When supplied via an annotation, the rules from the `NgrokTrafficPolicy` run first when requests are sent to any endpoints created for the `Ingress` before any rules from Traffic Policies supplied as backends or the default backend. * If used in this way, the Traffic Policy may "terminate" the request by sending back a response or by forwarding to another endpoint using the `forward-internal` action, but if done so, then the request will not be further processed by any other rules in the `Ingress`. * This can allow you to enforce common actions such as header manipulation or authentication for any request matching the ingress regardless of which `Ingress` rule the request may match. * As the backend for a specific rule * `NgrokTrafficPolicy` resources can be used in place of a `Service` for the backend of an Ingress rule. * When used this way, the rules from these Traffic Policies are executed after any Traffic Policy rules from an annotation `NgrokTrafficPolicy` (if supplied) and only if the request matches the `Ingress` rule. Since the `NgrokTrafficPolicy` is used in-place of a `Service` it is expected that the `NgrokTrafficPolicy` will "terminate" the request either by sending back the response on its own, or forwarding to another endpoint using the `forward-internal` action. * As the default backend * You may use an `NgrokTrafficPolicy` as the default backend of your `Ingress`. The rules from the Traffic Policy will only be executed if the request does not match any of your other `Ingress` rules. * Since the `NgrokTrafficPolicy` is used in-place of a `Service` it is expected that the `NgrokTrafficPolicy` will "terminate" the request either by sending back the response on its own, or forwarding to another endpoint using the `forward-internal` action. You can use one or multiple of the above options depending on your use-case and preferences. The following example shows how you can supply an `NgrokTrafficPolicy` to an `Ingress` using an annotation. It must be in the same namespace as the `Ingress`. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: my-ngroktrafficpolicy name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-ingress.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` The following example shows how you can supply an `NgrokTrafficPolicy` to an `Ingress` as the backend for a specific rule. It must be in the same namespace as the `Ingress`. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-ingress.ngrok.io http: paths: - path: / pathType: Prefix backend: resource: apiGroup: ngrok.k8s.ngrok.com kind: NgrokTrafficPolicy name: my-ngroktrafficpolicy ``` The following example shows how you can supply an `NgrokTrafficPolicy` to an `Ingress` as the default backend. It must be in the same namespace as the `Ingress`. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress namespace: default spec: ingressClassName: ngrok defaultBackend: resource: apiGroup: ngrok.k8s.ngrok.com kind: NgrokTrafficPolicy name: my-ngroktrafficpolicy rules: - host: example-ingress.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ## Ingress HTTPS upstreams If you have an Ingress that routes to a Service that expects HTTPS instead of HTTP, you can specify this using the `k8s.ngrok.com/app-protocols` annotation on your `Service`. For example, the following `Service` uses the annotation to let the Operator know which ports use HTTPS ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: example-service labels: app: example-app annotations: k8s.ngrok.com/app-protocols: '{"https-port":"https","http-port":"http"}' spec: ports: - name: https-port port: 443 protocol: TCP targetPort: 8443 - name: http-port port: 80 protocol: TCP targetPort: 8080 selector: app: example-app ``` ## Ingress conflicts In order to support the ability for users to configure multiple `Ingress` resources that use the same hostname like in the example above, the Operator enforces a requirement that all `Ingress` resources that use a given hostname must have the same configuration for the `k8s.ngrok.com/pooling-enabled` and `k8s.ngrok.com/traffic-policy` annotations. This means that if you use an ingress such as the following example for `foo.ngrok.io`, any other `Ingress` using the `foo.ngrok.io` hostname **must** have the same pooling and Traffic Policy annotations as the one below. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: k8s.ngrok.com/traffic-policy: my-ngroktrafficpolicy k8s.ngrok.com/pooling-enabled: "true" name: example-ingress namespace: default spec: ingressClassName: ngrok rules: - host: example-ingress.ngrok.io http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` # How the ngrok Operator Uses LoadBalancer Services Source: https://ngrok.com/docs/gateway/k8s/guides/using-loadbalancers Learn how the ngrok Kubernetes Operator automatically exposes LoadBalancer services using TCP or TLS endpoints with reserved addresses. ## How the ngrok Operator uses LoadBalancer services By default, services of type `LoadBalancer` are exposed using a TCP Endpoint. A reserved address is automatically created for the service and the service's status will be updated with the reserved address. Other projects like [external-dns](https://github.com/ngrok/ngrok-operator/blob/main/docs/examples/external-dns/README.md) can be used to create a CNAME record for the reserved address automatically. See the [TCP Endpoint](/docs/gateway/endpoints/tcp/) and [TLS Endpoint](/docs/gateway/endpoints/tls/) pages for more information about how ngrok handles these kinds of endpoints. For example, the following `LoadBalancer` service will be provided by a TCP Endpoint. ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: mysite spec: allocateLoadBalancerNodePorts: false # ngrok's tunneling technology does not require NodePorts to be allocated. loadBalancerClass: ngrok type: LoadBalancer selector: app: mysite ports: - name: traffic port: 9000 protocol: TCP targetPort: 9000 ``` If you would instead like a TLS Endpoint, the following example showcases how you can create one. The `k8s.ngrok.com/domain` annotation is required to use TLS and will expose the service as a TLS endpoint. Once the reserved domain is ready and the endpoint is created, the service's status will be updated with the ngrok address. ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: mysite annotations: k8s.ngrok.com/domain: mysite.mydomain.com # Required to use TLS external-dns.alpha.kubernetes.io/hostname: "mysite.mydomain.com" spec: allocateLoadBalancerNodePorts: false # ngrok's tunneling technology does not require NodePorts to be allocated. loadBalancerClass: ngrok type: LoadBalancer selector: app: mysite ports: - name: traffic port: 9000 protocol: TCP targetPort: 9000 ``` While using Services offers simplicity, using the `CloudEndpoint` and `AgentEndpoint` resources directly instead of translating `LoadBalancer` services into them using the Operator offers more configuration options and flexibility for configuring TCP and TLS endpoints. # How the ngrok Kubernetes Operator Works Source: https://ngrok.com/docs/gateway/k8s/how-it-works Learn how the ngrok Kubernetes Operator uses secure outbound connections to provide public endpoints for your Kubernetes services. A Kubernetes cluster has its own private network internally that pods get IP addresses from. This network is not accessible to the outside world, similar to how your home network is usually not accessible by anyone on the internet. By default, Kubernetes offers similar types of approaches to exposing traffic as you'd expect at home: * You can port forward: In Kubernetes, this is a temporary solution using `kubectl`. At home, you can configure your router. Both may be disrupted if your public IP address changes. * You can use an external load balancer of some sort that still needs access to the internal network somehow. In this case below, the user cannot access the pod directly in the k8s private network without some sort of entry point. Diagram showing a user blocked from accessing pods in a Kubernetes private network, while pods can communicate with each other internally In Kubernetes, operators (like the ngrok Kubernetes Operator) provide configuration options such as the existing Kubernetes Ingress and Gateway resources, and sometimes define their own configuration resources. These configuration resources provide a standard way of defining what traffic should be accepted, what operations should happen to the traffic (such as enforcing authentication or editing headers), and finally where the traffic should go. This follows the declarative nature of Kubernetes since the developer describes what they need and the Operator figures out how to provide it. Diagram showing a Kubernetes ingress controller routing external user traffic into pods within a private Kubernetes network With ngrok, the Operator establishes a secure outbound connection to the ngrok service which creates a public endpoint for your Kubernetes service. When someone hits the public endpoint, that traffic is sent down to the ngrok Kubernetes Operator and is forwarded to the services in the cluster. Diagram showing the ngrok Kubernetes ingress controller creating a public endpoint and routing traffic through ngrok Edge into a private Kubernetes cluster Once installed, the wide variety of configuration options make it easy to separate concerns and responsibilities. For example, an operations/infrastructure team can manage the resources like `Gateway` from Gateway API that define what hostnames/protocols/ports to accept traffic on while other developers or teams can create, update, and manage configuration resources such as `HTTPRoute`, which define which services the traffic should be sent to. This allows your operations/infrastructure team to focus on the higher-level policy about what kinds of traffic to accept while allowing other teams to self-service deploying their services/applications and routing traffic to them without needing to put in a request for the operations/infrastructure team to do that for them. Diagram showing an infra team managing the ngrok Kubernetes ingress controller while separate service teams self-service their own ingress configurations # Kubernetes Operator Source: https://ngrok.com/docs/gateway/k8s/index Learn about the ngrok Kubernetes Operator for providing secure ingress to your k8s applications. The ngrok Kubernetes Operator exposes your Kubernetes services securely using standard Kubernetes resources like Ingress or the Gateway API. With the Operator, teams can create and manage ngrok endpoints for their apps using a shared ngrok account. Optionally, bindings let you make services running elsewhere (including your local machine or another cluster) appear inside a cluster as normal Kubernetes Services. This enables private, cross-cluster connectivity without publishing publicly accessible URLs. ## Concepts Learn how the Operator establishes secure connections and routes traffic. Understand the Operator's control loops and design. Distribute traffic across multiple Kubernetes services. ## Features * [Local cluster options](/docs/gateway/k8s/guides/local-cluster/) - Set up a local Kubernetes cluster for development and testing. * [Helm configuration](/docs/gateway/k8s/installation/helm/) - Reference for Helm config values when installing the Operator. * [Observability](/docs/gateway/k8s/installation/observability/) - Monitor and observe your Operator deployment. ## Use cases Configure OAuth authentication in Kubernetes to secure your endpoints by requiring valid OAuth tokens before forwarding requests. Configure request routing to direct incoming traffic to the appropriate backend services based on rules. Configure TLS routing in Kubernetes to handle encrypted traffic using SNI-based routing with optional TLS termination. Configure rate limiting in Kubernetes to protect your services from excessive traffic and ensure fair usage across clients. ## What's next? * Get started with [Ingress](/docs/getting-started/kubernetes/ingress/), [Gateway API](/docs/getting-started/kubernetes/gateway-api/), [Custom Resources](/docs/getting-started/kubernetes/crds/), or [Endpoints](/docs/getting-started/kubernetes/endpoints/). * Explore the [guides](/docs/gateway/k8s/guides/using-crds/) for common use cases and configurations. # Kubernetes Operator Architecture Source: https://ngrok.com/docs/gateway/k8s/installation/architecture Learn about the architecture of the ngrok Kubernetes Operator including controllers, control loops, and design decisions. The ngrok Kubernetes Operator is a series of Kubernetes style control loops that watch Ingress and other k8s resources and manage tunnels in your cluster and the ngrok API to provide public endpoints for your Kubernetes services leveraging ngrok's endpoints features. This page is meant to be a living document detailing noteworthy architecture notes and design decisions. This document should help: * Consumers of the Operator understand how it works * Contributors to the Operator understand how to make changes to the Operator and plan for future changes * Integration partners can understand how it works to see how to integrate together. Before jumping directly into the ngrok Operator's specific architecture, first go over some core concepts about what controllers are and how they are built. ## What is a controller The word *controller* can sometimes be used in ways that can be confusing. While the whole thing is commonly referred to as the Operator or an ingress *controller*, in reality, many *controllers* are actually a Controller Runtime Manager which runs multiple individual [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/) and provides common things to them like shared Kubernetes client caches and leader election. Kubebuilder architecture diagram showing the Controller Runtime Manager running multiple controllers with shared Kubernetes client caches > From: [https://book.kubebuilder.io/architecture.html](https://book.kubebuilder.io/architecture.html) Individual controllers and the overall Operator are built using the kubernetes controller-runtime library which k8s itself leverages for its internal controllers (such as Deployment or StatefulSet controllers). Its Go-docs explain each concept fairly well starting here [https://pkg.go.dev/sigs.k8s.io/controller-runtime#hdr-Managers](https://pkg.go.dev/sigs.k8s.io/controller-runtime#hdr-Managers) ## Controllers Internally, the ngrok Kubernetes Operator is made up of many different controllers working in concert with each other, communicating via the Kubernetes API to interpret Ingress objects, Gateway API resources, and custom resources to create ngrok infrastructure that provides the specified functionality. Each of these controllers uses the same basic workflow to manage its resources. ## Deployments The ngrok Kubernetes Operator comes with two primary `Deployments` and one third optional `Deployment` ### The manager deployment The manager deployment is primarily responsible for synchronizing the state of your resources with the ngrok API. It will perform actions such as creating/updating/deleting Cloud Endpoints, Domains, etc. as needed. It also has the responsibility of watching `Ingress` and Gateway API resources and translating them into ngrok native custom resources. It runs Controllers that watch and manage the following resources * `Ingress` * `Service` * `Namespace` * `Secret` * `Domain` * `NgrokTrafficPolicy` * `KubernetesOperator` * `CloudEndpoint` * `IPPolicy` * `BoundEndpoint` (only enabled if the bindings feature is enabled ) * `GatewayClass` (only enabled when Gateway API CRDs are detected) * `Gateway` (only enabled when Gateway API CRDs are detected) * `HTTPRoute` (only enabled when Gateway API CRDs are detected) * `ReferenceGrant` (only enabled when Gateway API CRDs are detected) * `TCPEdge` (deprecated, will be removed soon) * `TLSEdge` (deprecated, will be removed soon) * `HTTPSEdge` (deprecated, will be removed soon) * `NgrokModuleSet` (deprecated, will be removed soon) ### The Agent deployment The Agent deployment is responsible for handling the connections to your upstream services. It starts controllers to watch the following resources related to upstream services. * `AgentEndpoint` * `Secret` * `Tunnel` ### The bindings forwarder deployment The bindings forwarder deployment is responsible for forwarding requests to and from Kubernetes bound endpoints that are projected into the cluster as `Service` resources. It starts a controller that watches `BoundEndpoint` resources to know what it needs to forward and where it needs to forward to/from. For more information about bindings, see [the bindings guide](/docs/gateway/k8s/guides/bindings) # Kubernetes Operator Helm Configuration Source: https://ngrok.com/docs/gateway/k8s/installation/helm Reference documentation for Helm configuration values when installing and configuring the ngrok Kubernetes Operator. The following YAML block contains the default `values.yaml` used when installing the ngrok Kubernetes Operator. You can reference these values and change them individually with `--set` flags while installing/upgrading the ngrok Kubernetes Operator. You can also see these values in [the ngrok Operator repo](https://github.com/ngrok/ngrok-operator/blob/main/helm/ngrok-operator/values.yaml). ## Helm values ```yaml title="helm values" wrap theme={null} ## ## @section Common parameters ## ## @param nameOverride String to partially override generated resource names ## @param fullnameOverride String to fully override generated resource names ## @param commonLabels Labels to add to all deployed objects ## @param commonAnnotations Annotations to add to all deployed objects ## nameOverride: "" fullnameOverride: "" commonLabels: {} commonAnnotations: {} ## ## @section Image configuration ## ## @param image.registry The ngrok operator image registry. ## @param image.repository The ngrok operator image repository. ## @param image.tag The ngrok operator image tag. Defaults to the chart's appVersion if not specified ## @param image.pullPolicy The ngrok operator image pull policy. ## @param image.pullSecrets An array of imagePullSecrets to be used when pulling the image. ## image: registry: docker.io repository: ngrok/ngrok-operator tag: "" pullPolicy: IfNotPresent ## Example ## pullSecrets: ## - name: my-imagepull-secret ## pullSecrets: [] ## ## @section RBAC ## ## @param crdAccessRoles.create Whether to create editor/viewer ClusterRoles for CRDs ## @param crdAccessRoles.annotations Annotations for CRD access ClusterRoles (e.g., RBAC aggregation) ## crdAccessRoles: create: true annotations: {} ## ## @section Custom Resource Definitions installation ## ## @param installCRDs When true, the ngrok CRDs will be installed alongside the operator ## installCRDs: true ## ## @section ngrok ## ## How the operator connects to ngrok and what it does. Set once for every ## component: which component reads a setting is an implementation detail. ## `log` is the exception and can be overridden for one component under ## `components..log` (for example `components.agent.log.level`). ## ## An empty value means "not set": the operator's built-in default applies. ## Those defaults live in the operator binary and are listed here for reference. ## ## @param ngrok.credentials.secret.name The name of the secret the credentials are in. If not provided, one will be generated using the helm release name. ## @param ngrok.credentials.accessToken Your ngrok access token. Used by every component that needs one, unless overridden below. ## @param ngrok.credentials.agent.accessToken Optional access token for the agent-manager only. Falls back to ngrok.credentials.accessToken. ## @param ngrok.credentials.apiManager.accessToken Optional access token for the api-manager only. Falls back to ngrok.credentials.accessToken. ## @param ngrok.description Description of this installation in the ngrok dashboard. Default: `The official ngrok Kubernetes Operator.` ## @param ngrok.region ngrok region to use. Default: the account's default region ## @param ngrok.serverAddr Address of the ngrok server to use for tunnels. Default: the ngrok default ## @param ngrok.apiURL Base URL for the ngrok API. Default: the ngrok default ## @param ngrok.rootCAs Root CAs to trust: `trusted` for the ngrok CA, `host` for the host's CA bundle. Default: `trusted` ## @param ngrok.metadata Key/value pairs added as metadata to the ngrok API resources the operator creates ## @param ngrok.clusterDomain Cluster domain used when resolving in-cluster service addresses. Default: `svc.cluster.local` ## @param ngrok.log.level Log level: `debug`, `info`, `error`, `panic`, or an integer for more verbose debug levels. Default: `info` ## @param ngrok.log.format Log format: `json` or `console`. Default: `json` ## @param ngrok.log.stacktraceLevel Level at and above which stacktraces are captured: `info`, `error` or `panic`. Default: `error` ## @param ngrok.features.ingress.enabled Enable the Kubernetes Ingress controller ## @param ngrok.features.ingress.controllerName Controller name matched by IngressClasses, and set on the IngressClass this chart creates ## @param ngrok.features.ingress.watchNamespace Namespace to watch for Ingress and AgentEndpoint resources. Default: all namespaces ## @param ngrok.features.ingress.ingressClass.name IngressClass resource name ## @param ngrok.features.ingress.ingressClass.create Create the IngressClass resource ## @param ngrok.features.ingress.ingressClass.default Set the IngressClass as the cluster default ## @param ngrok.features.gateway.enabled Enable Gateway API support, if the Gateway API CRDs are detected ## @param ngrok.features.gateway.disableReferenceGrants Disable the ReferenceGrant requirement for cross-namespace references ## @param ngrok.features.bindings.enabled Enable the Endpoint Bindings feature, including the bindings-forwarder ## @param ngrok.features.bindings.endpointSelectors CEL expressions filtering which endpoints are projected into this cluster. Default: `["true"]` ## @param ngrok.features.bindings.serviceAnnotations Annotations applied to projected services ## @param ngrok.features.bindings.serviceLabels Labels applied to projected services ## @param ngrok.features.bindings.ingressEndpoint Hostname of the bindings ingress endpoint. Default: `kubernetes-binding-ingress.ngrok.io:443` ## @param ngrok.features.domains.defaultReclaimPolicy Reclaim policy given to the Domains the operator creates: `Delete` or `Retain`. Default: `Delete` ## @param ngrok.features.cleanup.enabled Run a pre-delete hook on uninstall that deletes the KubernetesOperator resource, so the operator drains before it is removed ## @param ngrok.features.cleanup.timeout Seconds the hook waits for the drain to finish ## @param ngrok.features.cleanup.drainPolicy What the drain does with the ngrok API resources the operator created: `Delete` or `Retain`. Default: `Retain` ## @param ngrok.features.oneClickDemoMode.enabled Start without credentials and become Ready without reconciling, for marketplace installs. Also skips rendering the agent and bindings-forwarder ## ngrok: credentials: secret: name: "" accessToken: "" ## One token for everything is the simple path. The per-component values set a ## separate token for each component, falling back to accessToken when empty, ## so each token can be scoped to only the permissions its component needs. agent: accessToken: "" apiManager: accessToken: "" description: "" region: "" serverAddr: "" apiURL: "" rootCAs: "" metadata: {} clusterDomain: "" log: level: "" format: "" stacktraceLevel: "" features: ingress: enabled: true controllerName: k8s.ngrok.com/ingress-controller watchNamespace: "" ingressClass: name: ngrok create: true default: false gateway: enabled: true disableReferenceGrants: false bindings: enabled: false endpointSelectors: [] serviceAnnotations: {} serviceLabels: {} ingressEndpoint: "" domains: defaultReclaimPolicy: "" cleanup: enabled: true timeout: 300 drainPolicy: "" oneClickDemoMode: enabled: false ## ## @section Components ## ## Kubernetes settings for each pod the chart runs. ## ## `components.common` applies to the api-manager, agent and bindings-forwarder. ## Each of them can override any common key, listed commented out under the ## component: ## - a key the component leaves unset inherits the common value; ## - a non-empty map merges with the common one, the component winning per key; ## - any other value, including an empty `{}`, `[]` or `""`, replaces it, so ## `components.agent.tolerations: []` drops the common tolerations for the ## agent alone. ## A null value is removed by Helm before the chart sees it, so setting one key ## of a map to null clears the whole map for that component. ## ## Commented-out keys have no chart default; Kubernetes' own default applies ## until one is set. ## ## @param components.common.podAnnotations Pod annotations ## @param components.common.podLabels Pod labels ## @param components.common.nodeSelector Node labels for pod assignment ## @param components.common.tolerations Tolerations for pod assignment ## @param components.common.affinity Affinity rules. Overrides the presets below when set ## @param components.common.podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` ## @param components.common.podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` ## @param components.common.nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` ## @param components.common.nodeAffinityPreset.key Node label key to match. Ignored if `affinity` is set. ## @param components.common.nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set. ## @param components.common.topologySpreadConstraints Topology spread constraints for pod assignment ## @param components.common.priorityClassName Priority class for pod scheduling ## @param components.common.extraEnv Additional environment variables, as a map of name to value. A name the chart also sets replaces the chart's entry ## @extra components.common.lifecycle Container lifecycle hooks ## @extra components.common.extraVolumes Additional volumes ## @extra components.common.extraVolumeMounts Additional volume mounts ## @extra components.common.terminationGracePeriodSeconds Graceful shutdown period, in seconds. Kubernetes default: `30` ## @extra components.common.updateStrategy Deployment update strategy. Kubernetes default: `RollingUpdate` ## ## @section Components: API Manager ## ## @param components.apiManager.replicaCount The number of api-manager replicas to run ## @param components.apiManager.podDisruptionBudget.create Whether to create a PodDisruptionBudget ## @param components.apiManager.podDisruptionBudget.maxUnavailable Maximum unavailable pods ## @extra components.apiManager.podDisruptionBudget.minAvailable Minimum available pods. Set this instead of `maxUnavailable`, not alongside it ## @param components.apiManager.serviceAccount.create Whether to create a ServiceAccount ## @extra components.apiManager.serviceAccount.name ServiceAccount name. Generated if empty ## @extra components.apiManager.serviceAccount.annotations ServiceAccount annotations ## @extra components.apiManager.resources Resource requests and limits ## @extra components.apiManager.log Overrides of `ngrok.log` for the api-manager ## @extra components.apiManager.podAnnotations Overrides `components.common.podAnnotations` for the api-manager ## @extra components.apiManager.podLabels Overrides `components.common.podLabels` for the api-manager ## @extra components.apiManager.nodeSelector Overrides `components.common.nodeSelector` for the api-manager ## @extra components.apiManager.tolerations Overrides `components.common.tolerations` for the api-manager ## @extra components.apiManager.affinity Overrides `components.common.affinity` for the api-manager ## @extra components.apiManager.podAffinityPreset Overrides `components.common.podAffinityPreset` for the api-manager ## @extra components.apiManager.podAntiAffinityPreset Overrides `components.common.podAntiAffinityPreset` for the api-manager ## @extra components.apiManager.nodeAffinityPreset Overrides `components.common.nodeAffinityPreset` for the api-manager ## @extra components.apiManager.topologySpreadConstraints Overrides `components.common.topologySpreadConstraints` for the api-manager ## @extra components.apiManager.priorityClassName Overrides `components.common.priorityClassName` for the api-manager ## @extra components.apiManager.extraEnv Overrides `components.common.extraEnv` for the api-manager ## @extra components.apiManager.lifecycle Overrides `components.common.lifecycle` for the api-manager ## @extra components.apiManager.extraVolumes Overrides `components.common.extraVolumes` for the api-manager ## @extra components.apiManager.extraVolumeMounts Overrides `components.common.extraVolumeMounts` for the api-manager ## @extra components.apiManager.terminationGracePeriodSeconds Overrides `components.common.terminationGracePeriodSeconds` for the api-manager ## @extra components.apiManager.updateStrategy Overrides `components.common.updateStrategy` for the api-manager ## ## @section Components: Agent ## ## @param components.agent.replicaCount The number of agent replicas to run ## @param components.agent.serviceAccount.create Whether to create a ServiceAccount ## @extra components.agent.serviceAccount.name ServiceAccount name. Generated if empty ## @extra components.agent.serviceAccount.annotations ServiceAccount annotations ## @extra components.agent.resources Resource requests and limits ## @extra components.agent.log Overrides of `ngrok.log` for the agent ## @extra components.agent.podAnnotations Overrides `components.common.podAnnotations` for the agent ## @extra components.agent.podLabels Overrides `components.common.podLabels` for the agent ## @extra components.agent.nodeSelector Overrides `components.common.nodeSelector` for the agent ## @extra components.agent.tolerations Overrides `components.common.tolerations` for the agent ## @extra components.agent.affinity Overrides `components.common.affinity` for the agent ## @extra components.agent.podAffinityPreset Overrides `components.common.podAffinityPreset` for the agent ## @extra components.agent.podAntiAffinityPreset Overrides `components.common.podAntiAffinityPreset` for the agent ## @extra components.agent.nodeAffinityPreset Overrides `components.common.nodeAffinityPreset` for the agent ## @extra components.agent.topologySpreadConstraints Overrides `components.common.topologySpreadConstraints` for the agent ## @extra components.agent.priorityClassName Overrides `components.common.priorityClassName` for the agent ## @extra components.agent.extraEnv Overrides `components.common.extraEnv` for the agent ## @extra components.agent.lifecycle Overrides `components.common.lifecycle` for the agent ## @extra components.agent.extraVolumes Overrides `components.common.extraVolumes` for the agent ## @extra components.agent.extraVolumeMounts Overrides `components.common.extraVolumeMounts` for the agent ## @extra components.agent.terminationGracePeriodSeconds Overrides `components.common.terminationGracePeriodSeconds` for the agent ## @extra components.agent.updateStrategy Overrides `components.common.updateStrategy` for the agent ## ## @section Components: Bindings Forwarder ## ## Rendered only when `ngrok.features.bindings.enabled` is true. ## ## @param components.bindingsForwarder.replicaCount The number of bindings-forwarder replicas to run ## @param components.bindingsForwarder.serviceAccount.create Whether to create a ServiceAccount ## @extra components.bindingsForwarder.serviceAccount.name ServiceAccount name. Generated if empty ## @extra components.bindingsForwarder.serviceAccount.annotations ServiceAccount annotations ## @extra components.bindingsForwarder.resources Resource requests and limits ## @extra components.bindingsForwarder.log Overrides of `ngrok.log` for the bindings-forwarder ## @extra components.bindingsForwarder.podAnnotations Overrides `components.common.podAnnotations` for the bindings-forwarder ## @extra components.bindingsForwarder.podLabels Overrides `components.common.podLabels` for the bindings-forwarder ## @extra components.bindingsForwarder.nodeSelector Overrides `components.common.nodeSelector` for the bindings-forwarder ## @extra components.bindingsForwarder.tolerations Overrides `components.common.tolerations` for the bindings-forwarder ## @extra components.bindingsForwarder.affinity Overrides `components.common.affinity` for the bindings-forwarder ## @extra components.bindingsForwarder.podAffinityPreset Overrides `components.common.podAffinityPreset` for the bindings-forwarder ## @extra components.bindingsForwarder.podAntiAffinityPreset Overrides `components.common.podAntiAffinityPreset` for the bindings-forwarder ## @extra components.bindingsForwarder.nodeAffinityPreset Overrides `components.common.nodeAffinityPreset` for the bindings-forwarder ## @extra components.bindingsForwarder.topologySpreadConstraints Overrides `components.common.topologySpreadConstraints` for the bindings-forwarder ## @extra components.bindingsForwarder.priorityClassName Overrides `components.common.priorityClassName` for the bindings-forwarder ## @extra components.bindingsForwarder.extraEnv Overrides `components.common.extraEnv` for the bindings-forwarder ## @extra components.bindingsForwarder.lifecycle Overrides `components.common.lifecycle` for the bindings-forwarder ## @extra components.bindingsForwarder.extraVolumes Overrides `components.common.extraVolumes` for the bindings-forwarder ## @extra components.bindingsForwarder.extraVolumeMounts Overrides `components.common.extraVolumeMounts` for the bindings-forwarder ## @extra components.bindingsForwarder.terminationGracePeriodSeconds Overrides `components.common.terminationGracePeriodSeconds` for the bindings-forwarder ## @extra components.bindingsForwarder.updateStrategy Overrides `components.common.updateStrategy` for the bindings-forwarder ## ## @section Components: Cleanup Hook ## ## The pod of the pre-delete hook `ngrok.features.cleanup` runs. It does not ## take `components.common`. ## ## @param components.cleanupHook.image.repository The repository for the kubectl image used by the cleanup hook ## @param components.cleanupHook.image.tag The tag for the kubectl image ## @param components.cleanupHook.image.pullPolicy The pull policy for the cleanup hook image ## @param components.cleanupHook.resources.limits The resources limits for the cleanup hook container ## @param components.cleanupHook.resources.requests The requested resources for the cleanup hook container ## components: common: podAnnotations: {} podLabels: {} nodeSelector: {} tolerations: [] affinity: {} podAffinityPreset: "" podAntiAffinityPreset: soft nodeAffinityPreset: type: "" key: "" values: [] topologySpreadConstraints: [] priorityClassName: "" extraEnv: {} # lifecycle: {} # extraVolumes: [] # extraVolumeMounts: [] # terminationGracePeriodSeconds: 30 # updateStrategy: # type: RollingUpdate apiManager: replicaCount: 1 podDisruptionBudget: create: false maxUnavailable: "1" # minAvailable: "" serviceAccount: create: true # name: "" # annotations: {} # resources: # limits: {} # requests: {} # log: {} # overrides of ngrok.log ## Overrides of components.common: # podAnnotations: {} # podLabels: {} # nodeSelector: {} # tolerations: [] # affinity: {} # podAffinityPreset: "" # podAntiAffinityPreset: "" # nodeAffinityPreset: {} # topologySpreadConstraints: [] # priorityClassName: "" # extraEnv: {} # lifecycle: {} # extraVolumes: [] # extraVolumeMounts: [] # terminationGracePeriodSeconds: 30 # updateStrategy: # type: RollingUpdate agent: replicaCount: 1 serviceAccount: create: true # name: "" # annotations: {} # resources: # limits: {} # requests: {} # log: {} # overrides of ngrok.log ## Overrides of components.common: # podAnnotations: {} # podLabels: {} # nodeSelector: {} # tolerations: [] # affinity: {} # podAffinityPreset: "" # podAntiAffinityPreset: "" # nodeAffinityPreset: {} # topologySpreadConstraints: [] # priorityClassName: "" # extraEnv: {} # lifecycle: {} # extraVolumes: [] # extraVolumeMounts: [] # terminationGracePeriodSeconds: 30 # updateStrategy: # type: RollingUpdate bindingsForwarder: replicaCount: 1 serviceAccount: create: true # name: "" # annotations: {} # resources: # limits: {} # requests: {} # log: {} # overrides of ngrok.log ## Overrides of components.common: # podAnnotations: {} # podLabels: {} # nodeSelector: {} # tolerations: [] # affinity: {} # podAffinityPreset: "" # podAntiAffinityPreset: "" # nodeAffinityPreset: {} # topologySpreadConstraints: [] # priorityClassName: "" # extraEnv: {} # lifecycle: {} # extraVolumes: [] # extraVolumeMounts: [] # terminationGracePeriodSeconds: 30 # updateStrategy: # type: RollingUpdate cleanupHook: image: repository: bitnami/kubectl tag: latest pullPolicy: IfNotPresent resources: limits: {} requests: {} ``` ## Common configurations This section provides some common use cases and recommendations when using this helm chart in a production setting. ### Deployment scaling By default, the replica count is set to 1 via `replicaCount`. Override this to 2 or more to ensure high availability during roll-outs and failures, and to spread out the load. ### ngrok region ngrok runs globally distributed tunnel servers around the world to enable fast, low latency traffic to your applications. See [ngrok's points of presence](/docs/gateway/points-of-presence/) for more information on ngrok's regions. Similar to the agent, if you do not explicitly pick a region via helm when installing the Operator, the Operator will attempt to pick the region with the least latency, which is usually the one geographically closest to your machine. Specifying a region applies this setting across all agent connect URLs in your cluster; regions cannot currently be configured on a per-connection basis. See the [helm value `region`](https://github.com/ngrok/ngrok-operator/blob/main/helm/ngrok-operator/values.yaml#L47) to configure a specific region for the controller to use. ### Watching specific namespaces By default, the Operator watches all namespaces. It's a common use case to need a controller to watch only a specific namespace in the case where you may run a controller in a namespace for each team or environment. In order to watch only a specific namespace for ingress objects, you can set the helm value `watchNamespace` to the namespace you want to watch. # ngrok Kubernetes Operator Network Egress Requirements Source: https://ngrok.com/docs/gateway/k8s/installation/network-egress Learn which outbound hostnames and ports the ngrok Kubernetes Operator needs so you can allowlist it through a default-deny egress firewall. The ngrok Kubernetes Operator reaches ngrok over a small number of outbound connections. In most clusters this works without any extra configuration, since outbound connections on port `443` are usually allowed by default. Some environments are more restrictive. If your cluster runs under a default-deny egress policy (common on private EKS clusters and other locked-down networks), outbound traffic is blocked unless you explicitly allow it. This page lists the hostnames and ports you'll need to allow so the Operator can start, sync your resources, and serve traffic. Every connection the Operator makes to ngrok is outbound over TLS on port `443`, so your firewall only needs egress rules, not inbound ones. ngrok's cloud service uses a dynamic, rotating set of IP addresses that can change without notice, so you can't allowlist it by IP. Allow traffic by hostname (FQDN) instead. This means a standard Kubernetes `NetworkPolicy`, which matches on IP/CIDR, isn't enough on its own. You'll need an egress firewall or proxy that supports FQDN-based rules, or a CNI that adds FQDN matching (such as Cilium's `toFQDNs`). See [ngrok IP addresses](/docs/gateway/domains/ip-addresses) for more detail. ## Required egress Every Operator installation needs to reach these hosts: * `api.ngrok.com` on TCP port `443`, so that the Operator can reconcile your Kubernetes resources with the ngrok API. * `connect.ngrok-agent.com` on TCP port `443`, so that the Operator can establish its agent session and serve traffic. ngrok uses latency-based DNS and may route you to a regional connect host, so allow the whole `*.ngrok-agent.com` family rather than pinning a single address. If you only expose inbound services (for example, running the Operator as a webhook gateway), these two hosts plus the image registry below are all you need. The section on endpoint bindings applies only if you turn that feature on. ## Installation and upgrades You install and upgrade the Operator with Helm, which pulls two container images from Docker Hub: * `docker.io/ngrok/ngrok-operator`, the image for the Operator's deployments: the API manager, the agent, and, when endpoint bindings are enabled, the bindings forwarder. * `docker.io/bitnami/kubectl`, used only by the pre-delete cleanup job when you uninstall. To pull these, allow Docker Hub's registry and content-delivery hosts on TCP port `443`: * `registry-1.docker.io` * `auth.docker.io` * `production.cloudflare.docker.com` If your policy is to avoid Docker Hub entirely, mirror both images to a private registry (such as Amazon ECR) and point the `image.registry` and `cleanupHook.image.repository` Helm values at your mirror. In that case you allow your own registry instead of the hosts above. Bitnami changed how it distributes images on Docker Hub in 2025. If you run in an air-gapped or mirrored environment, plan to mirror `bitnami/kubectl` (or set `cleanupHook.image.repository` to a `kubectl` image you control) rather than relying on it being pulled at uninstall time. ## Endpoint bindings [Endpoint bindings](/docs/gateway/k8s/guides/bindings) are disabled by default (`bindings.enabled: false`), so skip this section unless you turn them on. When enabled, the bindings forwarder opens an additional connection to carry that traffic. Allow this host on TCP port `443`: * `kubernetes-binding-ingress.ngrok.io` The forwarder connects to the bound endpoint host your ngrok account returns, which defaults to `kubernetes-binding-ingress.ngrok.io`. If your account uses a different host, allow that one instead. ## Verify connectivity from the cluster To check connectivity from where your pods actually run, you can run [`ngrok diagnose`](/docs/gateway/agent/connectivity#troubleshooting) as a `Job`, using the [pre-built agent image](https://hub.docker.com/r/ngrok/ngrok). This checks name resolution, TCP, and TLS to `connect.ngrok-agent.com`. `ngrok diagnose` only exercises the agent connect host, not `api.ngrok.com` or the bindings host. To check those, run a shell in the cluster and test them directly, for example `curl -sv https://api.ngrok.com`. The standalone agent image also checks a certificate revocation list on port `80` that the Operator itself doesn't use, so ignore a CRL failure when you're diagnosing the Operator. Create a manifest file (for example, `ngrok-diagnose.yaml`): ```yaml theme={null} apiVersion: batch/v1 kind: Job metadata: name: ngrok-diagnose spec: template: spec: containers: - name: ngrok-diagnose image: ngrok/ngrok:latest command: ["/bin/sh", "-c"] args: ["ngrok diagnose"] restartPolicy: Never ``` Apply it to your cluster: ```bash theme={null} kubectl apply -f ngrok-diagnose.yaml ``` Then check the logs: ```bash theme={null} kubectl logs -l "job-name=ngrok-diagnose" ``` ```bash theme={null} ngrok Connectivity - Region: Auto (lowest latency) Name Resolution [ OK ] TCP [ OK ] TLS [ OK ] Tunnel Protocol [ OK ] Successfully established ngrok connection! (region: 'auto', latency: 54.895145ms) ``` If any check fails, the host for that check isn't reachable from the cluster. Revisit the egress rules above with your network team. # Monitoring the Kubernetes Operator Source: https://ngrok.com/docs/gateway/k8s/installation/observability Learn how to enable and customize ngrok's Kubernetes ingress Observability features. Configuring metrics and logging for the ngrok Kubernetes Operator is essential for gaining visibility into how the Operator is handling your ingress resources and other CRDs. This guide explains the available observability features, and how to enable and customize them to: * Diagnose issues more quickly. * Track system performance. * Ensure the Operator is running reliably in production. ## Metrics The ngrok Kubernetes Operator exposes a set of [Prometheus-compatible](https://prometheus.io/) metrics that provide insight into the Operator’s internal state and the resources it manages. By enabling metrics, you can monitor reconciliation activity, track resource health, and build alerts around key operational behaviors. ### Supported metrics This project is built using [Kubebuilder](https://github.com/kubernetes-sigs/kubebuilder?tab=readme-ov-file#kubebuilder). By default, it exposes the metrics described in the Kubebuilder [metrics reference](https://book.kubebuilder.io/reference/metrics-reference.html?highlight=metrics#default-exported-metrics-references). ### Accessing metrics This Operator exposes prometheus metrics on the `/metrics` endpoint. The metrics are exposed on the `:8080` port and can be scraped by Prometheus or other services. The controller applies these annotations [by default](https://github.com/ngrok/ngrok-operator/blob/15e015e5b559ca3f4eb18ded6b5192c6de5bc3ce/helm/ngrok-operator/templates/controller-deployment.yaml#L25-L27). ## Logging The ngrok Kubernetes Operator generates structured logs that capture its reconciliation processes, configuration changes, and any errors encountered while managing resources. These logs are a primary source of truth when diagnosing issues or understanding the Operator’s actions. ### Configure log levels By tuning the Operator’s log level, you can control the level of detail emitted in logs. This lets you strike the right balance between observability and signal-to-noise, whether you’re troubleshooting an issue, monitoring production workloads, or reducing log volume. ### Available log levels | Log Level | Description | | - | - | | `debug` | The most verbose level. `debug` logs include detailed internal state, reconciliation steps, API interactions, and decision-making paths inside the Operator.

This level is intended for development or troubleshooting. It provides maximum insight, but can generate a large log volume. | | `info` | The default and generally recommended level. `info` logs capture high-level operational events such as resource reconciliation, configuration changes, and important lifecycle actions.

These logs provide good observability with minimal noise and are appropriate for most production environments. | | `error` | The most minimal level. `error` logs are emitted only when something fails—such as an unexpected response from the Kubernetes API, a reconciliation error, or a misconfiguration that prevents the Operator from completing an action.

Use this level when you want to minimize log output while still surfacing critical failures. | ### Via `helm` To configure the log level via `helm`, use `--set` to supply the desired log ```sh theme={null} helm install ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --create-namespace \ --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN --set log.level=info ``` ### Via `helm` value overrides The log level can be configured through Helm value overrides, allowing you to define logging behavior in a reproducible, version-controlled way. This approach is ideal when you want consistent settings across environments or when deploying via automated pipelines. To override the log level, specify the configuration in your `values.yaml` file and pass the file to `helm` using the `--values` option ([more info](https://helm.sh/docs/helm/helm_install/#options)). ```yaml theme={null} log: level: debug format: json ``` # Uninstalling the ngrok Kubernetes Operator Source: https://ngrok.com/docs/gateway/k8s/installation/uninstall How to uninstall the ngrok Kubernetes Operator, clean up resources, and understand what happens during the process. The `ngrok-operator` helm chart uses [finalizers](/docs/gateway/k8s/guides/finalizers) on its custom resources to ensure proper cleanup of ngrok API resources before deletion. The chart includes a `pre-delete` helm hook that automates removing these finalizers and optionally deleting resources during uninstall. ## Quick uninstall (preserves ngrok account resources) By default, uninstalling the operator **preserves** resources in your ngrok account such as CloudEndpoints and Domains. This prevents an uninstall from taking down cloud infrastructure that may be supporting a production site. ```bash theme={null} helm uninstall ngrok-operator -n ngrok-operator ``` When you run this command, a `pre-delete` helm hook job automatically: 1. Signals the operator to stop reconciling resources 2. Patches every custom resource to remove its finalizer 3. Allows the helm uninstall to proceed, tearing down the operator deployment (and CRDs, if they were bundled with the operator release) Your ngrok API resources remain in your account and can be managed through the [ngrok Dashboard](https://dashboard.ngrok.com) or API. ## Cleaning up ngrok account resources If you want to also **delete** ngrok API resources (CloudEndpoints, Domains, etc.) from your account during uninstall, first upgrade your release to set the `drainPolicy` to `Delete`: ```bash theme={null} helm upgrade ngrok-operator ngrok/ngrok-operator \ --namespace=ngrok-operator \ --reuse-values \ --set drainPolicy="Delete" ``` Now when you run `helm uninstall ngrok-operator -n ngrok-operator`, the `pre-delete` hook job will: 1. Delete each custom resource and wait for the operator to clean up the corresponding ngrok API resource 2. Remove finalizers from `Ingress` objects (but does NOT delete them) 3. Allow the helm uninstall to proceed The cleanup job removes finalizers from user-created `Ingress` objects but does not delete them. You can delete them manually afterwards if desired. ## Recommended setup The following recommendations make uninstalling simpler and help avoid stuck resources: * **Install the operator in its own namespace** (for example, `ngrok-operator`). Keep it separate from the namespace where you create `Ingress` objects or custom resources. This way, if you delete a namespace full of resources, the operator stays running and can process the deletions. * **Install CRDs separately** using the `ngrok-crds` chart. This decouples CRD lifecycle from the operator. See [Separate CRD installation](#separate-crd-installation) below. * **Delete resources before uninstalling** if you want to clean up your ngrok account. Deleting `Ingress` objects and custom resources like `CloudEndpoint` while the operator is still running gives it time to properly clean up the corresponding ngrok API resources. ## How the uninstall process works The ngrok-operator uses [finalizers](/docs/gateway/k8s/guides/finalizers) on its custom resources. These prevent Kubernetes from removing objects until the operator has performed cleanup tasks, such as deleting the corresponding resource from the ngrok API. When you `helm uninstall`, the chart's `pre-delete` hook job handles these finalizers so you don't have to. What happens depends on two factors: 1. **How CRDs were installed:** bundled with the operator chart (default) or separately via the `ngrok-crds` chart 2. **The `drainPolicy` value:** `Retain` (default) or `Delete` ### CRDs bundled with operator (default) When CRDs are part of the same helm release, uninstalling the operator also uninstalls the CRDs, which forces all custom resources into deletion. | drainPolicy | Behavior | | - | - | | `Retain` (default) | Hook removes all finalizers. Helm then deletes CRDs, forcing CR deletion. ngrok API resources are **preserved**. | | `Delete` | Hook deletes each CR and waits for cleanup. Helm then deletes CRDs. ngrok API resources are **deleted**. | ### CRDs installed separately When CRDs are installed via the `ngrok-crds` chart with `installCRDs=false` on the operator, uninstalling the operator does **not** remove the CRDs or force CR deletion. | drainPolicy | Behavior | | - | - | | `Retain` (default) | Hook removes all finalizers. CRDs and CRs remain in the cluster (without finalizers). ngrok API resources are **preserved**. | | `Delete` | Hook deletes each CR and waits for cleanup. CRDs remain in the cluster. ngrok API resources are **deleted**. | With separate CRDs and `drainPolicy=Retain`, the remaining CRs are left without finalizers, which means you can: * Delete those resources without them getting stuck * Delete namespaces containing those resources without them getting stuck * Uninstall the CRDs separately whenever you're ready ## Separate CRD installation Instead of bundling CRDs with the operator, you can install them separately. This is **strongly recommended** for production and **required** for [multiple installations](/docs/gateway/k8s/guides/multiple-installs). ```bash theme={null} helm repo add ngrok https://charts.ngrok.com helm repo update helm install ngrok-crds ngrok/ngrok-crds helm install ngrok-operator ngrok/ngrok-operator \ --namespace=ngrok-operator \ --create-namespace \ --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN \ --set installCRDs=false ``` When you're ready to remove the CRDs (after uninstalling all operators that depend on them): ```bash theme={null} helm uninstall ngrok-crds ``` ## Multiple installations If you have multiple operators installed (see [Multiple Installations](/docs/gateway/k8s/guides/multiple-installs)), the uninstall behavior depends on how they were installed and which one you are uninstalling. If one operator release includes the CRDs, all running operators rely on them. Uninstalling that release removes the CRDs and breaks every other operator. When uninstalling one of multiple operators: 1. First uninstall operators that have `installCRDs=false` 2. Last, uninstall the single installation that includes the CRDs (if applicable) ### Cleaning up account resources with multiple operators You can `helm upgrade` a specific operator to set `drainPolicy=Delete` before uninstalling it. The cleanup job uses the same `ingress.watchNamespace` filter that the operator uses, so it only deletes resources within that operator's scope. If you have multiple operators watching the same resources (for example, one scoped to a namespace and another without a namespace filter), uninstalling the unscoped operator with `drainPolicy=Delete` will delete **all** resources it can see across all namespaces. If you're in this situation, disable the cleanup job and uninstall without automated cleanup. ## Disabling the cleanup job If you prefer manual cleanup or have a custom uninstall process, disable the helm hook: ```yaml theme={null} # values.yaml cleanupHook: enabled: false ``` With the cleanup job disabled, you are responsible for removing finalizers and cleaning up resources before uninstalling. See [Manually removing finalizers](/docs/gateway/k8s/guides/finalizers#manually-removing-finalizers) for instructions. ## Triggering cleanup without Helm If you are not using a system that supports helm hooks (for example, some GitOps tools), you can trigger the cleanup process manually by deleting the `KubernetesOperator` resource: ```bash theme={null} kubectl delete kubernetesoperator -n --wait=true ``` This causes the operator to stop reconciling objects and begin draining custom resources, respecting the `drainPolicy` set during installation. Once the `KubernetesOperator` resource is deleted, you can proceed to uninstall the operator and CRDs however you see fit. ## Reinstalling after uninstall After uninstalling, your cluster may be in different states depending on how the install and uninstall were done: * **Ingress objects**: Still exist but without finalizers and aren't being reconciled * **Custom resources**: May be fully deleted, or may still exist without finalizers but with their Status information intact * **ngrok API resources**: May still exist in your account or may have been deleted (depending on `drainPolicy`) **If ngrok API resources were cleaned up** (`drainPolicy=Delete`): Reinstalling the operator will recreate them from your existing Kubernetes resources. **If ngrok API resources were preserved** (`drainPolicy=Retain`): * If CRDs were installed separately and CRs still exist in the cluster, reinstalling the operator will re-add finalizers and resume managing them with no duplication. * If CRDs were bundled and were deleted (forcing CR deletion), you'll need to reapply your manifests. This may create duplicate resources (for example, a second CloudEndpoint in a pool) if the originals still exist in your ngrok account. Check the [ngrok Dashboard](https://dashboard.ngrok.com) and clean up any duplicates. * For `Ingress` objects, the new operator will see them and automatically create the corresponding resources. ## Troubleshooting ### Viewing cleanup job logs The `pre-delete` helm hook runs as a Kubernetes Job. To view its logs: ```bash theme={null} # List jobs in the operator namespace (the cleanup job will have a name like -cleanup) kubectl get jobs -n ngrok-operator # View the job's pod logs kubectl logs -n ngrok-operator job/-cleanup ``` ### Viewing Operator drain logs If you triggered cleanup via the `KubernetesOperator` resource, the drain process runs inside the operator pod: ```bash theme={null} kubectl logs -n ngrok-operator deploy/ngrok-operator-manager | grep -i "drain\|cleanup\|finalizer" ``` ### Checking drain status The `KubernetesOperator` custom resource reports the current state of the drain process in its status: ```bash theme={null} kubectl get kubernetesoperator -n -o yaml ``` The drain-related status fields are: | Field | Description | | - | - | | `drainStatus` | Current state: `pending`, `draining`, `completed`, or `failed` | | `drainProgress` | How many resources have been processed versus total (format `X/Y`) | | `drainMessage` | Human-readable information about the current drain state | | `drainErrors` | List of the most recent errors encountered during drain | ### Resources stuck in Terminating state If resources are stuck with unresolved finalizers (for example, because the operator was removed before it could clean them up), see [Manually removing finalizers](/docs/gateway/k8s/guides/finalizers#manually-removing-finalizers) for how to patch individual resources or [bulk remove finalizers](/docs/gateway/k8s/guides/finalizers#removing-finalizers-from-all-ngrok-resources) from all ngrok resources. ### Namespace stuck in Terminating state A namespace will not delete if it contains resources with unresolved finalizers. Resolve the stuck resources first using the methods above, then the namespace deletion will complete. ### Cleanup job timeout If the cleanup job is taking too long (for example, waiting for many resources to be deleted from the ngrok API), you can configure the job's timeout in your helm values. If the job times out, you may need to [manually remove finalizers](/docs/gateway/k8s/guides/finalizers#manually-removing-finalizers) from any remaining resources. ## Decision flowchart The following diagram walks you through the uninstall process based on your setup: ```mermaid theme={null} flowchart TD A["I want to uninstall my ngrok-operator"] --> B{"Do you have multiple
ngrok-operator installations?"} %% ========================= %% MULTIPLE INSTALLATIONS %% ========================= B -- "Yes" --> M1{"Were CRDs installed separately
(ngrok-crds chart)?"} M1 -- "Yes" --> M2["CRDs are shared and preserved
while uninstalling operators"] M1 -- "No" --> M3["One operator release installed the CRDs.
Uninstalling it will remove CRDs
and impact all operators."] M2 --> M4{"Do you want to delete
ngrok API resources?"} M3 --> M4 M4 -- "Keep account resources (default)" --> M5["For EACH operator release:
Run uninstall (default drainPolicy=Retain)"] M4 -- "Delete account resources" --> M6["For EACH operator release:
Upgrade and set drainPolicy=Delete
Then run uninstall"] M6 --> M7{"Are operators strictly scoped
by watchNamespace?"} M7 -- "Yes" --> M8["Cleanup targets only resources
in this operator's scope"] M7 -- "No" --> M9["Risk: cleanup may delete resources
owned by other operators"] M9 --> M10["Recovery path:
Disable cleanup hook
Then run uninstall (preserve account resources)"] M3 --> M11["Safer uninstall order:
1) Uninstall ALL operator releases
with installCRDs=false
2) Uninstall the single release
that installed the CRDs LAST"] %% Multi-install final states M5 --> FS1["Final state:
CRDs: Preserved
CRs: Preserved (finalizers removed)
ngrok API resources: Preserved"] M8 --> FS2["Final state:
CRDs: Preserved
CRs: Deleted (in operator scope)
ngrok API resources: Deleted (in operator scope)"] M10 --> FS1 M11 --> FS3["Final state (after completing the order):
CRDs: Deleted (when final release removed)
CRs: Deleted (forced by CRD removal)
ngrok API resources: Preserved
(unless you separately used drainPolicy=Delete)"] %% ========================= %% SINGLE INSTALLATION %% ========================= B -- "No" --> S1{"Were CRDs installed separately
(ngrok-crds chart)?"} S1 -- "Yes" --> S2["Uninstalling the operator
will NOT remove CRDs or CRs"] S1 -- "No" --> S3["Uninstalling the operator
WILL remove CRDs and force CR deletion"] %% Duplicate the ngrok API question per branch S2 --> SA{"Do you want to delete
ngrok API resources?"} S3 --> SB{"Do you want to delete
ngrok API resources?"} %% Single-install (CRDs separate) outcomes SA -- "Keep account resources (default)" --> S5A["Run uninstall (default drainPolicy=Retain)"] SA -- "Delete account resources" --> S6A["Upgrade and set drainPolicy=Delete
Then run uninstall"] S5A --> FS1 S6A --> FS4["Final state:
CRDs: Preserved
CRs: Deleted
ngrok API resources: Deleted"] %% Single-install (CRDs bundled) outcomes SB -- "Keep account resources (default)" --> S5B["Run uninstall (default drainPolicy=Retain)"] SB -- "Delete account resources" --> S6B["Upgrade and set drainPolicy=Delete
Then run uninstall"] S5B --> FS5["Final state:
CRDs: Deleted
CRs: Deleted (forced by CRD removal)
ngrok API resources: Preserved"] S6B --> FS6["Final state:
CRDs: Deleted
CRs: Deleted
ngrok API resources: Deleted"] ``` # How to update the ngrok Kubernetes Operator Source: https://ngrok.com/docs/gateway/k8s/installation/update Learn how to update the ngrok Kubernetes Operator to the latest version using Helm, including Gateway API CRD updates. Updating the ngrok Kubernetes Operator is a very simple and straight-forward process. Before updating, it is always best to check which version of the ngrok Kubernetes Operator you are running and comparing your current image version (not the helm chart version) to the most recent version or the version you want to update to. You can refer to the [Operator changelog](/docs/gateway/k8s/changelog) to view what has changed since the version you are running to be informed about the changes you are making by updating. ## 1. (Optional) Update Gateway API CRDs If you're using the Gateway API CRDs, you might want to update them while updating the Operator. For the most up-to-date information about installing/updating these, it is best to refer directly to the Gateway API website. [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/) ## 2. Update your helm repo Fetch all the latest updates for your helm repositories to pull down all of the latest ngrok Kubernetes Operator chart versions. ```bash theme={null} helm repo update ``` ## 3. (optional) Find a specific release The following command will print out a list of all the available versions of the ngrok Kubernetes Operator chart including development releases. If you want to update to a specific release of the Operator instead of updating to the latest version, you can set the following variable to the version of the helm chart (not the `APP VERSION`) you would like to install. This step is necessary if you plan on upgrading to a development version as helm will never install these versions for you unless you explicitly specify that you want to install a development version. Development versions are considered any version that does not follow the format `..` such as `0.18.0-rc.1`. Many of the development versions of the ngrok Kubernetes Operator follow that format with `rc` standing for Release Candidate. These are early development builds before an official release and may contain features that have not been fully implemented or bugs that have not been identified through testing. ```bash theme={null} helm search repo ngrok/ngrok-operator --versions --devel ``` ```bash theme={null} export OPERATOR_CHART_VERSION=0.18.0 ``` ## 4. Update the Operator with Helm Set the following variables to the name and namespace of your Operator installation. You can check this with `helm list -A` if you did not use the following defaults. ```bash theme={null} export OPERATOR_NAME=ngrok-operator export OPERATOR_NAMESPACE=ngrok-operator ``` ```bash theme={null} helm upgrade $OPERATOR_NAME ngrok/ngrok-operator \ --reuse-values ``` ```bash theme={null} helm upgrade $OPERATOR_NAME ngrok/ngrok-operator \ --version $OPERATOR_CHART_VERSION \ --reuse-values ``` The --reuse-values flag will tell Helm to reuse any values you used in your last install/upgrade. You can change them or add new values by chaining --set flags just like when installing the Operator. # Load Balancing for Kubernetes Services Source: https://ngrok.com/docs/gateway/k8s/load-balancing/load-balancing-kubernetes Learn how to use Endpoint Pooling to share incoming traffic between services in different clusters or clouds. A core benefit of Kubernetes is that it simplifies creating multiple replica pods for your apps or APIs and automatically load-balancing between them. That's helpful if you have a single cluster and need to horizontally scale, but Kubernetes can't help if you want to: * Share traffic between multiple clusters * Balance traffic between more than one cloud providers * Deploy canary versions or run A/B tests of your apps/APIs on a single cluster [Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling/) makes load balancing between Kubernetes services simple—you only need to create two endpoints with the same URL. Here's how that works in your clusters, whether you're using the `AgentEndpoint` custom resource, [Ingress](/docs/gateway/k8s/guides/using-ingresses) objects, or [Gateway API](/docs/gateway/k8s/guides/using-gwapi) resources. ## 1. Install the ngrok Kubernetes Operator Check out the [installation instructions](/docs/gateway/k8s/installation/helm/) for details on how to use Helm to deploy the open-source Operator to each cluster you'd like to load-balance between. ## 2. Create your first Agent Endpoint Pooling is always enabled on `AgentEndpoint` resources, but with Ingress or Gateway API, you have to enable it with an annotation. The YAML snippets below are just illustrations—you'll also need to change the details of your services, like their names, ports, and namespaces, to match what you've already implemented. Same goes for `$NGROK_DOMAIN.ngrok.app`—you can reserve a domain in the [dashboard](https://dashboard.ngrok.com/domains) if you don't have one already. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: example namespace: default spec: upstream: url: http://example.default:80 url: https://$NGROK_DOMAIN.ngrok.app ``` ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress namespace: default annotations: k8s.ngrok.com/pooling-enabled: "true" spec: ingressClassName: ngrok rules: - host: $NGROK_DOMAIN.ngrok.app http: paths: - path: / pathType: Prefix backend: service: name: example port: number: 80 ``` ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: ngrok-gateway namespace: default annotations: k8s.ngrok.com/pooling-enabled: "true" spec: gatewayClassName: ngrok listeners: - name: example-hostname protocol: HTTP port: 80 hostname: $NGROK_DOMAIN.ngrok.app --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-route namespace: default spec: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: ngrok-gateway namespace: default hostnames: - $NGROK_DOMAIN rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: example port: 80 ``` ## 3. Create a second Agent Endpoint to enable pooling On a second cluster, apply the same or similar ingress configuration—just make sure the `url`, `host`, or `hostname` value is the same for `AgentEndpoint`, Ingress, and Gateway API implementations, respectively. Hit your endpoint a few times to get responses from multiple clusters. ## What's next? Now that you're load-balancing in Kubernetes, you can repeat the process in other clusters or clouds to add many other Agent Endpoints to the pool—there's no limit on how many services can share traffic on a single URL. You might also consider creating a single [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/) that serves as the "front door" to all your Kubernetes services, then create a pool of [internal Agent Endpoints](/docs/gateway/endpoints/internal-endpoints/) on a pooled URL like `https://example.internal`. Curious? See the [step-by-step guide](/docs/gateway/k8s/load-balancing/load-balancing-kubernetes-clusters) that details the entire process. # Load Balancing Between Multiple Kubernetes Clusters Source: https://ngrok.com/docs/gateway/k8s/load-balancing/load-balancing-kubernetes-clusters Use Endpoint Pooling to split traffic between any number of clusters without needing to deploy external services. Say you have three services, `foo`, `bar`, and `baz`, deployed in a single Kubernetes cluster, but eventually decide you need to extend to multiple clusters for reasons like: * Redundancy and failover * Load distribution and scalability * Hosting services close to your customers in different regions * Deploying some clusters on-premises and others with a cloud provider With ngrok, you don't need to provision new load balancers or other complex networking tools—[Endpoint Pooling](/docs/gateway/endpoints/endpoint-pooling/) lets you share traffic between replicas of your services on any number of clusters by deploying them on the same [internal](/docs/gateway/endpoints/internal-endpoints/) URL. With the addition of a [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints) and [Traffic Policy](/docs/traffic-policy), you'll have a single "front door" for all your load-balanced K8s services behind a single hostname, like `https://api.example.com`. Another guide covers load balancing to [individual K8s services](/docs/gateway/k8s/load-balancing/load-balancing-kubernetes) with ngrok custom resources, Ingress objects, or Gateway API resources. This guide uses [custom resources](/docs/gateway/k8s/guides/using-crds) to create endpoints for your services. ## 1. Install the ngrok Kubernetes Operator Check out the [installation instructions](/docs/gateway/k8s/installation/helm/) for details on how to use Helm to deploy the open-source Operator to each cluster you'd like to load-balance between. ## 2. Create internal Agent Endpoints on your first cluster The following YAML manifest creates three internal Agent Endpoints, each of which will proxy traffic to your `foo`, `bar`, and `baz` pods, respectively. When you use custom resources to create Agent Endpoints, pooling is [enabled automatically](/docs/gateway/k8s/guides/pooling/#endpoint-pooling-with-agentendpoints). ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: foo namespace: default spec: upstream: url: http://foo.default:8001 url: https://foo.internal --- apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: bar namespace: default spec: upstream: url: http://bar.default:8002 url: https://bar.internal --- apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: baz namespace: default spec: upstream: url: http://baz.default:8003 url: https://baz.internal ``` Save it to your local workstation as `ingress.yaml` and apply to your cluster with `kubectl apply -f ingress.yaml`. These internal Agent Endpoints won't yet be available to public traffic, because they are only accessible through other ngrok endpoints in your account, but you'll fix that in the next two steps. Repeat the process on any other clusters you want to load balance traffic between. ## 4. Create a Cloud Endpoint and add routing with Traffic Policy If you don't yet have a reserved domain, go ahead and [reserve one](https://dashboard.ngrok.com/domains) in the dashboard. The easiest way to create a Cloud Endpoint is in the [**Endpoint** section](https://dashboard.ngrok.com/endpoints) of your ngrok dashboard. Click **New**, then **Cloud Endpoint**. Leave the binding as **Public** and fill in the full URL of the domain you reserved, then click **Create Cloud Endpoint**. The following Traffic Policy example: 1. Uses the [`forward-internal` action](/docs/gateway/traffic-policy/actions/forward-internal/) to route all requests to the `/foo` path to the pool you created at `https://foo.internal`. - The pooled endpoints then proxy requests proxies the request to the `foo` Service/Deployment in your cluster. 2. Does the same for the paths `/bar` and `/baz`. 3. Denies all traffic to paths that don't (yet) have a service. ```yaml theme={null} on_http_request: - expressions: - req.url.path.startsWith('/foo') actions: - type: forward-internal config: url: https://foo.internal - expressions: - req.url.path.startsWith('/bar') actions: - type: forward-internal config: url: https://bar.internal - expressions: - req.url.path.startsWith('/baz') actions: - type: forward-internal config: url: https://baz.internal - actions: - type: deny config: status_code: 404 ``` Paste the YAML above into the dashboard and click **Save**. Now, when you request `https://api.example.com/foo` or any other route, your traffic will be automatically load-balanced between replicas of your services on multiple Kubernetes clusters. ## What's next? With load balancing solved for, you can now add new services or entire Kubernetes clusters by adding new endpoints to existing pools or configuring new routes with Traffic Policy. Speaking of Traffic Policy, here are a few common ways folks filter, manage, and orchestrate requests: * [Route requests by subdomain or header](/docs/gateway/traffic-policy/examples/route-requests/) instead of paths. * Add authentication with the [`basic-auth`](/docs/gateway/traffic-policy/actions/basic-auth/), [`oauth`](/docs/gateway/traffic-policy/actions/oauth/), or [`jwt-validation`](/docs/gateway/traffic-policy/actions/jwt-validation/) Traffic Policy actions. * [Rate limit requests](/docs/gateway/traffic-policy/actions/rate-limit/) to prevent abuse and ensure fairness. Or, if you're looking for a more step-by-step experience for deploying ngrok as an API gateway, check out the guides: * [Deploy an API gateway in one or multiple clouds](/docs/gateway/api-gateway/multicloud/) * [Deploy a multicloud API gateway](/docs/gateway/k8s/load-balancing/load-balancing-kubernetes-clusters/) # Kubernetes Operator Release Process Source: https://ngrok.com/docs/gateway/k8s/releasing Learn about the ngrok Kubernetes Operator release process including Docker images, Helm charts, and semantic versioning. ## Artifacts The ngrok Kubernetes Operator has 2 main artifacts, a docker image and a helm chart. While the helm chart is the recommended way to install the Operator, the docker image can be used to run the Operator in a Kubernetes cluster without helm. ### Docker Image The Docker image contains the ngrok Kubernetes Operator binary and is available on Docker Hub [here](https://hub.docker.com/r/ngrok/ngrok-operator). The image currently supports `amd64` and `arm64` architectures, with future plans to build for other architectures. ### Helm chart The helm chart is packaged and published to its own [helm repository](https://charts.ngrok.com/index.yaml) ## Semantic versioning This project uses [semantic versioning](https://semver.org/) for both the docker image and helm chart. From the [semver spec](https://semver.org/#spec-item-4): > Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable. That said, changes in "y" are treated as major releases and changes in "z" as minor releases until version 1.0 is reached. ## Release process The Docker Image and Helm chart are released independently since a feature or bug fix in one may not require a release in the other. Sometimes a change will require a version bump and release in both. ### Tagging There is a different git tag pattern for each artifact. #### Helm chart Releases of the helm chart will be tagged with a prefix of `helm-chart-`. For example, version `0.18.0` of the helm chart will have a git tag of `helm-chart-0.18.0` which contains the code used to package and publish version `0.18.0` of the helm chart. When changes are made to the helm chart's `Chart.yaml` file, a github workflow will trigger upon merging the PR to the `main` branch. The workflow will package and publish the helm chart for consumption. The workflow will also create a git tag as described above. When changing `version` in the helm chart's `Chart.yaml` file, the version should be bumped according to the semantic versioning spec as described above. #### Operator Releases of the Operator will be tagged with a prefix of `ngrok-operator-`. For example, version `0.15.0` of the docker image will have a git tag of `ngrok-operator-0.15.0` which contains the code used to build the docker image `ngrok/ngrok-operator:0.15.0`. When changes that would affect the Operator's docker image are pushed to `main`, a github workflow will trigger. The workflow will build and publish the docker image with the versioned tag. If the `VERSION` file at the root of the repo is changed, the workflow will also create a git tag for the Operator as described above and publish a tagged docker image. For instance when the `VERSION` is changed to `0.15.0`, the workflow will create a git tag of `ngrok-operator-0.15.0` and publish the docker image `ngrok/ngrok-operator:0.15.0`. # Gateway Overview Source: https://ngrok.com/docs/gateway/overview Learn about ngrok's building blocks for creating API and device gateways, identity-aware proxies, site-to-site connectivity, and more. ngrok's Gateway is a platform that gives you the building blocks to create powerful API gateways, device gateways, identity-aware proxies, and site-to-site connectivity. Across all use cases it secures, accelerates, and protects your applications. Additionally: * It works with software running locally or in the cloud, and with devices running on-premises or distributed in the field. * It supports [TCP](/docs/gateway/endpoints/tcp/), [TLS](/docs/gateway/endpoints/tls/), and [HTTP/S](/docs/gateway/endpoints/http/). * You can deliver traffic to internal and public APIs, and [orchestrate traffic](/docs/gateway/traffic-policy/) across your devices. * It's globally distributed by default and provides support for multiple environments with minimal configuration. ## Concepts Create and manage endpoints to orchestrate traffic to your services. Filter, match, manage, and orchestrate traffic to your endpoints. Route, manage, and secure traffic to any LLM whether it is local or hosted providers like OpenAI, Anthropic, and more. Create public HTTP, HTTPS, and TLS endpoints with hostnames that match your domain. Create public TCP endpoints on a fixed hostname and port. Automatically manage TLS certificates for terminating TLS connections to your endpoints. Automate the creation and management of endpoints for services in your Kubernetes cluster. Securely manage sensitive data such as API keys, passwords, and tokens across your endpoints using centralized and encrypted vaults. Enforce reusable groups of IP/CIDR allow and deny rules. Enforce Mutual TLS authentication (mTLS) on your endpoints with the terminate-tls Traffic Policy action. Get visibility and control of the identities which authenticate to your endpoints with the `oauth` and `openid-connect` Traffic Policy actions. ## Features * [Traffic Policy](/docs/gateway/traffic-policy/) - Filter, match, manage, and orchestrate traffic to your endpoints. * [Traffic observability](/docs/obs/) - Capture and replay requests and responses. * [Identity and access management](/docs/iam/) - Manage credentials for human users and automated processes. * [Kubernetes support](/docs/gateway/k8s/) - Ingress and cross-platform Gateway API configuration resources. * [Secure tunnels](/docs/gateway/agent/) - Expose local services or connect devices to ngrok's global network. ## Use cases Forward requests based on IP geolocation data for reduced latency or country-specific features. Pre-tier requests based on your packaging or pricing model. Restrict server access to a specific set of IP addresses. Intercept 302 redirect headers to preserve UX and agent behavior. ## What's next? * Proceed to the guides section to get started with ngrok as an [API gateway](/docs/gateway/api-gateway/get-started/), [device gateway](/docs/gateway/device-gateway/overview/), [identity-aware proxy](/docs/gateway/identity-aware-proxy/securing-with-oauth/), or for [site-to-site connectivity](/docs/gateway/identity-aware-proxy/securing-with-oauth/). * Check out the [Gateway examples collection](/docs/gateway/examples/) to see how to implement even more common use cases. # Points of Presence Source: https://ngrok.com/docs/gateway/points-of-presence Learn about ngrok's globally distributed points of presence for low-latency traffic routing to your applications worldwide. ngrok's globally distributed cloud service runs on points of presence all around the world to enable fast, low latency traffic to your applications. ngrok continuously expands regional points of presence. As each new point of presence is added, your applications become faster for global customers without any changes. ## Reference The current points of presence are: | Code | Location | | - | - | | `ap` | Asia/Pacific (Singapore) | | `au` | Australia (Sydney) | | `eu` | Europe (Frankfurt) | | `in` | India (Mumbai) | | `jp` | Japan (Tokyo) | | `sa` | South America (São Paulo) | | `us` | United States (Ohio) | | `us-cal-1` | United States (California) | ## Usage The table of region codes above are used throughout the product to communicate which points of presence are being used by a particular feature and to allow you to configure pieces of the product to use specific points of presence. Region codes are used by the following features: * [Global Load Balancer](/docs/gateway/domains/global-load-balancer/) * [`conn.server_region` Traffic Policy variable](/docs/gateway/traffic-policy/variables/connection/#connserver-region) * [`region` Agent Configuration property](/docs/gateway/agent/config/v2/#region) * [Domain Global Load Balancer configuration](/docs/domains#global-load-balancer) They are correspondingly used in the following APIs: * [Domain `region` property](/docs/api-reference/reserveddomains/list) * [TCP Address `region` property](/docs/api-reference/reservedaddrs/list) * [Tunnel Sessions `region` property](/docs/api-reference/tunnelsessions/list) # Running ngrok Behind a Corporate Firewall Source: https://ngrok.com/docs/gateway/running-behind-firewalls Learn some troubleshooting and configuration tips for run ngrok behind a corporate firewall. When you need to deploy ngrok behind a corporate firewall, there may be additional steps that you will need to take to make sure ngrok is working properly. As background, this is usually not an issue. Firewalls usually allow outbound connections, which is what an ngrok Agent makes to establish a session with the ngrok service and subsequently your tunnel. Running the ngrok Kubernetes Operator instead of the agent? Its egress requirements differ. See [network egress requirements](/docs/gateway/k8s/installation/network-egress) for the hosts and ports to allow. However, certain corporate firewalls have more restrictions around outbound connections. For example, ngrok may be blocked on Fortinet firewalls. If you're having trouble using the ngrok agent to start a tunnel, the first step is to run [`ngrok diagnose`](/docs/gateway/agent/connectivity#troubleshooting) which will produce a report that will help you identify connection issues. ```bash theme={null} $ ngrok diagnose Testing ngrok connectivity... Internet Connectivity Name Resolution [ OK ] TCP [ OK ] TLS [ OK ] ngrok Connectivity Name Resolution [ OK ] TCP [ OK ] TLS [ OK ] Tunnel Protocol [ OK ] Successfully established ngrok connection! (region: 'us', latency: 54.895145ms) ``` To resolve these issues, you have a couple options: * Work with the network team in charge of the corporate firewall to provide exceptions that allow ngrok to make the necessary outbound connections. * Set up a custom Connect URL. When working with the network team, you'll want to set up the following exceptions and allow: * Connect URLs (the default is connect.ngrok-agent.com, but there is one for each region), so that the ngrok agent can connect with ngrok servers * The update URL (the default is `update.ngrok-agent.com`), so that the ngrok agent can quickly update itself. Agent versions before `3.36.0` used `update.equinox.io` as the update URL. Equinox is fully owned by ngrok and used exclusively for building and distributing ngrok binaries. See [the FAQ page](/docs/faq#does-ngrok-own-bin-equinox-io) for more details. Setting up a custom Connect URL can be useful because it ensures that no one can bring their own ngrok account. In this case, the network admins could continue to block traffic to the default Connect URLs and only allow your custom branded Connect URL. For this, you'll need to: * Set up a [custom Connect URL in your ngrok Dashboard](https://dashboard.ngrok.com/connect-urls) * Edit your ngrok agent configuration file with a [`connect_url`](/docs/gateway/agent/config/v3/#connect-url) parameter, set to your custom Connect URL ### Certificate revocation list One of the steps in agent connection is [checking the certificate revocation list](/docs/gateway/agent/connectivity#tls-verification). This requires an outbound connection on port 80 to the CRL URL (`crl.ngrok.com` for agent versions 3.9.0 and before, `crl.ngrok-agent.com` for agent version 3.10.0 and after).If you are unable to connect to this URL, it is possible to skip the CRL check by setting `crl_noverify: true` in your configuration file. However, disabling the CRL check does expose you to the possibility of using a certificate that has been revoked which could mean that a third party could intercept and view your traffic. ## Testing in a Kubernetes cluster If you are using ngrok from within a Kubernetes Cluster, you may need to diagnose the network connectivity from the cluster to the ngrok cloud. To do this, you can run the previously mentioned `ngrok diagnose` command using the [pre-built docker images](https://hub.docker.com/r/ngrok/ngrok) for the agent as a `Job` in Kubernetes. Create a manifest file (for example `ngrok-manifest.yaml`): ```yaml theme={null} apiVersion: batch/v1 kind: Job metadata: name: ngrok-diagnose spec: template: spec: containers: - name: ngrok-diagnose image: ngrok/ngrok:latest command: ["/bin/sh", "-c"] args: ["ngrok diagnose"] restartPolicy: Never ``` Apply this manifest to your cluster: ```bash theme={null} kubectl apply -f ngrok-manifest.yaml ``` Wait a few seconds and check its logs to see the `diagnose` command's output: ```bash theme={null} kubectl logs -l "job-name=ngrok-diagnose" ``` ```bash theme={null} TLS [ OK ] Localhost Connectivity Name Resolution [ OK ] ngrok Connectivity - Region: Auto (lowest latency) Name Resolution [ OK ] TCP [ OK ] TLS [ OK ] Tunnel Protocol [ OK ] Successfully established ngrok connection! (region: 'auto', latency: 54.895145ms) ``` To generate the [more verbose diagnostic report](/docs/gateway/agent/cli/#flags-11), update your job's `args` with the `-w` flag and file location: ```yaml theme={null} apiVersion: batch/v1 kind: Job metadata: name: ngrok-diagnose spec: template: spec: containers: - name: ngrok-diagnose image: ngrok/ngrok:latest command: ["/bin/sh", "-c"] args: [ "ngrok diagnose -w /tmp/diagnose_output.txt && cat /tmp/diagnose_output.txt", ] restartPolicy: Never ``` # Setting up HIPAA-compliant ngrok services Source: https://ngrok.com/docs/gateway/security-dev-productivity/hipaa-compliance Read ngrok's recommendations for setting up ngrok services in a HIPAA-compliant manner. The Health Insurance Portability and Accountability Act (HIPAA) is the U.S. federal law enacted to protect patient health information. The law sets stringent standards to secure electronic protected health information (ePHI/PHI). HIPAA compliance is a self-serve add-on for Pay-as-you-go accounts. You enable it yourself from the dashboard and sign the Business Associate Agreement (BAA) there. No sales conversation or contract is required. This page explains how to enable it and contains ngrok's recommendations for setting up ngrok services in a HIPAA-compliant manner. These recommendations from ngrok's team do not constitute legal advice. Please consult your own legal and engineering teams to ensure HIPAA compliance. ## Get started You can enable HIPAA compliance yourself from the [ngrok dashboard](https://dashboard.ngrok.com/billing) by navigating to **Billing**, then adding the **HIPAA compliance** add-on and signing the BAA. Before you enable HIPAA, make sure your account meets these requirements: 1. You must be an **admin**. 2. You must have a **v3 pay-as-you-go account**. 3. You must execute a Business Associate Agreement (BAA) by providing the **executor name** and **legal business name**. You can view the executed BAA on the billing page at any time after you agree to it. While HIPAA compliance is enabled, you can't downgrade your account plan. ### What changes when HIPAA compliance is enabled Enabling HIPAA compliance applies the following changes to your account to help keep PHI (protected health information) out of stored data: * **Price:** usage-based charges are billed at +50%. The base plan fee and per-seat charges are not affected. * **HTTP endpoints:** you can't create public endpoints that use the `http` scheme ([ERR\_NGROK\_6031](/docs/errors/err_ngrok_6031), [ERR\_NGROK\_9043](/docs/errors/err_ngrok_9043)). HTTPS, TCP, and TLS endpoints are still allowed, as are HTTP endpoints with an internal or Kubernetes binding. * **Traffic Inspector:** [full capture](/docs/obs/traffic-inspection#full-capture-mode) can't be enabled, and any existing full capture setting is ignored. Traffic Inspector stores only request and response metadata, not headers or payloads. * **TLS termination:** data in transit is encrypted throughout ngrok's network. After ngrok's edge terminates TLS, it's re-encrypted before being sent on to the ngrok agent running in your network. * **Event Subscriptions:** your endpoints don't emit [traffic events](/docs/obs/events/reference) such as `http_request_complete.v0` and `tcp_connection_closed.v0`. ## Shared responsibility model ngrok operates with a shared responsibility model. There are many safeguards that ngrok puts in place to protect customers, and there are steps customers must take to remain compliant with HIPAA. ngrok is responsible for providing you, the customer, with all the information you need to use ngrok in a compliant manner, and how to configure the ngrok platform to remain compliant. You are responsible for ensuring your use case is compliant and configuring ngrok correctly to ensure compliance. ## Compliant use cases ngrok is HIPAA-compliant for use cases where PHI is stored within a packet payload. You are responsible for ensuring that PHI is only present within the packet payload. ngrok won't store this data in HIPAA workloads but does store other, non-PHI, data. See [Data at ngrok](https://ngrok.com/blog-post/data-at-ngrok) for more details on what data ngrok stores. ngrok account user information, ngrok account billing information, and packet headers should not be considered PHI within any use cases. ## Customer safeguards These are ngrok's recommendations for setting up and configuring your ngrok account securely: * Ensure packet payloads are the only PHI data being sent over the ngrok network * Don't put PHI in JWT tokens * Don't put PHI in packet headers * Don't put PHI in URL parameters * Ensure the ngrok agent is on a secure machine. The agent-local inspector may have ePHI. * Ensure that any traffic forwarded from the ngrok agent through your network is secure. * [Use managed certificates](/docs/gateway/domains/tls-certificates/#automated). ngrok will automatically provision and renew TLS certificates on your behalf. * [Verify webhooks](/docs/gateway/traffic-policy/actions/verify-webhook/) to ensure the authenticity of incoming requests See the guide on [security quick wins](/docs/gateway/security-dev-productivity/securing-your-tunnels/) for information on how to set up authentication, authorization, encryption, auth tokens, logging, and related features in a secure manner. # Best security practices for developer productivity Source: https://ngrok.com/docs/gateway/security-dev-productivity/index Learn the best practices for developing securely with ngrok. Organizations and enterprises must ensure robust security for their endpoints without causing any interruptions to their developers' experience and productivity. This guide describes the best practices and features organizations can apply to consistently secure developers using ngrok. ## The "Front Door" pattern Instead of exposing individual developer endpoints publicly, you can implement a more secure approach: Creating a single, centralized Cloud Endpoint managed by security teams. Known as the [front door pattern](/docs/gateway/examples/front-door-pattern/), this endpoint can be customized with Traffic Policies while keeping developer endpoints private and protected. ## Architectural reference Architecture diagram showing a centralized Cloud Endpoint with Traffic Policy routing traffic via wildcard domains to developer internal endpoints on individual machines Create one ngrok account, managed by security or DevOps. The following can be managed centrally at this account's dashboard: * Cloud endpoint Traffic Policies * Authtokens and ACL restrictions * Users and access control ## Add developers as team members to your ngrok account This will allow you to add each developer using ngrok to your central ngrok account. With RBAC (available on ngrok's pay-as-you-go plan), you also have the option to restrict their access to read-only. ngrok dashboard Invite Team Member dialog showing RBAC permission options for Developer, Team, Billing, and Admin roles Once the invite is sent, have the developer go to their email and sign up for their account which will be registered under the master account. ## Create an authtoken for each developer Create a tunnel authtoken for each developer. This authtoken will be specific to each user and will be their key to setting up internal endpoints. Next, assign it an ACL rule. By assigning an ACL rule, you can ensure that this user with this authtoken may only create one internal endpoint bound to their alias. Navigate to the authtokens section on your dashboard and click "new authtoken" and set the description, set the owner to the developer email, and assign it an ACL rule as shown below: ngrok authtoken ACL Rules field showing a bind:alias1.internal rule that restricts the token to a specific internal endpoint ## Developer installs ngrok Agent and defines an internal endpoint in ngrok.yml Have the developer [install the ngrok agent](https://dashboard.ngrok.com/get-started/setup/) and then have them run the command below to ensure their agent uses the proper authtoken you provisioned for them in the previous step. ```bash theme={null} ngrok config add-authtoken ``` Internal Endpoints are private endpoints that only receive traffic when forwarded through the [forward-internal Traffic Policy action](/docs/gateway/traffic-policy/actions/forward-internal/). This allows you to route traffic to an application through ngrok without making it publicly addressable. Internal endpoint URLs must: * End with the .internal domain extension * Use the internal [binding](/docs/gateway/endpoints/bindings/) Each developer will have their own configuration file for their agent installed on their machine. As shown below, each config file will contain an internal endpoint unique to that developer's alias. The configuration file can be found at `/path/to/ngrok/ngrok.yml`. Have the developer add their internal endpoint in their configuration file. The final config file should look like this: ```yaml theme={null} version: 3 agent: authtoken: endpoints: - name: Internal Endpoint for alias1 url: https://alias1.internal upstream: url: {port serving alias1's local application} ``` Within this configuration file, developers have the ability to add any further Traffic Policy actions that may aid them in their testing. In this way, developers only handle private endpoints and don't have permissions to alter any configurations on public endpoints. ## Reserve a domain for your wildcard Cloud Endpoint Reserving a wildcard domain is the first step in creating a wildcard Cloud Endpoint to receive traffic on any subdomain of your domain. Navigate to the [domains](https://dashboard.ngrok.com/domains) section in your dashboard and click "new domain". Any naming convention you use for this domain will require proper DNS and CNAME records. These targets are returned after domain creation and can take 5-10 mins for ssl to be established. For billing information on wildcard endpoints, see [wildcard endpoints pricing](/docs/pricing-limits/#wildcard-endpoints). ngrok New Domain dialog with a wildcard domain *.devtest.example.com entered for reserving a custom subdomain for the developer Cloud Endpoint ## Create a public Cloud Endpoint and Traffic Policy Cloud Endpoints are persistent, always-on endpoints whose creation, deletion, and configuration are managed centrally via the Dashboard or API. They exist permanently until they are explicitly deleted. Cloud Endpoints do not forward their traffic to an agent by default and instead only use their attached Traffic Policy to handle connections. This Cloud Endpoint will multiplex across several internal endpoints which point to their corresponding local development environments. You can use Traffic Policy to forward traffic from your Cloud Endpoint to the correct internal endpoint based on the hostname of the Cloud Endpoint that developers make requests to. Since the Cloud Endpoint is set up as a wildcard URL, any text placed where the wildcard is will forward to its corresponding internal endpoint. `alias1.devtest.example.com → alias1.internal → alias1's local env` So, all alias1 has to do is go to that Cloud Endpoint URL in their browser and they will be able to access their local environment. Same goes for alias2, alias3, and aliasN. To achieve this dynamic routing, replace the default Traffic Policy on the Cloud Endpoint in the dashboard with the following: ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://${req.host.split(".devtest.example.com")[0]}.internal ``` ## Secure your Cloud Endpoint Now that traffic is forwarded correctly from the Cloud Endpoint to its corresponding internal endpoints, you can layer on security to the public Cloud Endpoint. There are a variety of Traffic Policy actions to choose from to achieve this. Listed below are YAML snippets and curl commands below for how to enable IP Restrictions, JWT Validation, and mTLS. There are many other actions to choose from which can be found in [Traffic Policy Actions](/docs/gateway/traffic-policy/actions/). These actions will be added to your existing Traffic Policy config, preceding the forward-internal action. ### IP restrictions For developers with a designated source IP, you can use the restrict-ips action to allowlist trusted source IPs where only these IPs will be able to access this Cloud Endpoint. ```yaml theme={null} on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - e680:5791:be4c:5739:d959:7b94:6d54:d4b4/128 - 203.0.113.42/32 - type: forward-internal config: url: https://${req.host.split(".devtest.example.com")[0]}.internal ``` ### JWT validation You also have the option to send a JWT to ngrok, specify an allowlist of issuers/JWKS URLs, and ngrok will validate the JWT. ```yaml theme={null} on_http_request: - actions: - type: jwt-validation config: issuer: allow_list: - value: https://example.com/issuer audience: allow_list: - value: urn:example:api http: tokens: - type: access_token method: header name: Authorization prefix: "Bearer " - type: it+jwt method: body name: _id_token jws: allowed_algorithms: - RS256 - ES256 keys: sources: additional_jkus: - https://example.com/issuer/jku - type: forward-internal config: url: https://${req.host.split(".devtest.example.com")[0]}.internal ``` ### mTLS If you would rather use a CA or PEM version in this scenario, you can also enable mTLS where TLS termination will happen at the ngrok cloud edge. ```yaml theme={null} on_tcp_connect: - actions: - type: terminate-tls config: mutual_tls_certificate_authorities: - |- -----BEGIN CERTIFICATE----- ...your CA certificate PEM... -----END CERTIFICATE----- on_http_request: - actions: - type: forward-internal config: url: https://${req.host.split(".devtest.example.com")[0]}.internal ``` For all of these options, you can either use one or layer multiple actions onto the Cloud Endpoint. ## Create a custom connect URL Custom connect URLs are available with ngrok's pay-as-you-go plan. This provides a white-labeling capability so that your ngrok agents will connect to *connect.example.com* instead of the default connection hostname (connect.ngrok-agent.com). Dedicated IPs that are unique for your account which your agents will connect to are also available.  This takes away any danger of rogue agents in your network trying to call home and adds an additional layer of security by specializing your ngrok connectivity. ```bash theme={null} curl \ -X POST \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"description":"Custom connect URL","domain":"connect.example.com"}' \ https://api.ngrok.com/agent_ingresses ``` Once you have created the custom connect url, specify this field within the agent configuration file. Add this section to your agent configuration file to specify the custom connect url: ```yaml theme={null} version: 3 agent: connect_url: connect.example.com:443 ``` ## Recap You have now integrated a system that allows your security team to centrally manage and secure a Cloud Endpoint which developers can seamlessly use to access applications running in their local environments. To recap what you've built: 1. One ngrok agent per developer machine - ACL restricted authtokens provisioned by security specialists for developers. 2. Local, uninterrupted development and testing for engineers, made securely available via cloud and internal endpoints. 3. Granular access with a composable Traffic Policy offering refined and robust security measures for a singular public Cloud Endpoint. # Security Quick Wins Source: https://ngrok.com/docs/gateway/security-dev-productivity/securing-your-tunnels Learn some quick wins that will help you use ngrok more securely. This guide will walk you through recommendations to ensure you're using ngrok securely. ## Agent tips * Do not run ngrok as root, as it should not be necessary. * Do not open any additional incoming ports in your firewall. ngrok only makes an outbound connection upon start. * Consider restricting the IPs that are able to start ngrok agent sessions. ### Authtokens * Assign a [unique Authtoken to each ngrok agent](/docs/gateway/agent/authtokens) deployment to isolate issues if a specific Authtoken is compromised. * Set up a [minimum ACL per Authtoken](/docs/gateway/agent/authtokens#authtoken-acls) to limit the endpoints each agent is able to start. ### Encryption * If you're running the latest ngrok agent, your tunnels will use `scheme https` so that it only opens HTTPS endpoints and not HTTP endpoints. If you're running an older version that does not do this, be sure to configure this behavior yourself. * If your local service is not running on the same machine as the ngrok agent, it is recommended to set up TLS encryption for the ngrok agent to the upstream service leg of the tunnel using the [local HTTPS feature](/docs/gateway/endpoints/http/#https-forwarding). * For custom domains, use ngrok's [Automated TLS certificates](/docs/gateway/domains/tls-certificates/#automated) to have ngrok automatically provision a TLS certificate for your endpoint from [Let's Encrypt](https://letsencrypt.org/about/). Certificates automatically provisioned from Let's Encrypt are securely managed, and their private keys are not accessible to the requester. This ensures the security and integrity of the certificate by preventing key exposure. ### Using a custom connect URL If your organization uses a custom Connect URL, your default ngrok configuration will not work. Edit your ngrok agent configuration to add a [`connect_url`](/docs/gateway/agent/config/v3/#connect-url) parameter to use the custom Connect URL of your organization. ## Tunnel configuration ### TLS termination TLS Encryption is terminated at different locations depending on the ngrok endpoint type and configuration. See [Terminating TLS Connections](/docs/gateway/domains/tls-termination/) for more details. ### Minimum TLS version It is possible to specify the minimum TLS version that clients are required to use to talk to the ngrok cloud service for your endpoint. See [the `terminate-tls` Traffic Policy action](/docs/gateway/traffic-policy/actions/terminate-tls/#changing-supported-tls-versions) for more details. ### Observability ngrok provides functionality for consuming logs for events in the system. For more information, check out the [Log Exporting documentation](/docs/obs/). ### Blocking non-corporate accounts For a tighter security policy, most administrators will want to block ngrok accounts that are not a part of their organization. The fastest approach is to block DNS lookups to the URLs used by the agent to connect to the ngrok cloud. These are the DNS lookups to block: * `connect.ngrok-agent.com` * `connect.us.ngrok-agent.com` * `connect.eu.ngrok-agent.com` * `connect.ap.ngrok-agent.com` * `connect.au.ngrok-agent.com` * `connect.sa.ngrok-agent.com` * `connect.jp.ngrok-agent.com` * `connect.in.ngrok-agent.com` * `tunnel.ngrok.com` * `tunnel.us.ngrok.com` * `tunnel.eu.ngrok.com` * `tunnel.ap.ngrok.com` * `tunnel.au.ngrok.com` * `tunnel.sa.ngrok.com` * `tunnel.jp.ngrok.com` * `tunnel.in.ngrok.com` ## API configuration You need an API key to authenticate with [the ngrok REST API](/docs/api/). API keys are Base64 encoded strings that you can find [in the dashboard](https://dashboard.ngrok.com/get-started/your-authtoken) or from [an API endpoint](/docs/api-reference/apikeys/list), which makes it easy to rotate your API keys. Consider restricting the IPs permitted to access the API. You can do so [in the ngrok Dashboard](https://dashboard.ngrok.com/ip-restrictions). ## Dashboard configuration ### Dashboard authentication * For authenticating access to the dashboard, ngrok has features for [role-based access control](https://dashboard.ngrok.com/team-members) (RBAC), [IP Policy](https://dashboard.ngrok.com/ip-policies), and [Single Sign-On](https://dashboard.ngrok.com/settings). * With RBAC, you can configure permissions for groups of users within your team (for example admins and developers). * Consider restricting the IPs permitted to access the dashboard. # Install ngrok as a Background Service Source: https://ngrok.com/docs/gateway/site-to-site-connectivity/background-service Install ngrok as a service so it stays running. You can install ngrok in your customer's network as a native background system service. This ensures the ngrok agent starts on boot and restarts if it crashes for any reason. Installing ngrok as a service helps you recover connectivity in the case of unexpected software or hardware failures. When you do so, ngrok starts all endpoints defined in the configuration file. ```bash theme={null} ngrok service install --config /etc/ngrok.yml ngrok service start ``` In most cases, installing ngrok as a service requires administrator privileges. ## What's next * [Eliminate single points of failure with redundant agents](/docs/gateway/site-to-site-connectivity/redundant-agents) to achieve high availability. # Set Up a Custom Connect URL Source: https://ngrok.com/docs/gateway/site-to-site-connectivity/custom-connect-url Use your own domain for connections (white label). When the ngrok agent starts, it makes an outbound connection via TLS over port 443 to the ngrok cloud service. This connection is made to the "agent connect URL". By default, [this connect URL](/docs/gateway/agent/connect-url/) is `connect.ngrok-agent.com`, but you can white-label it so that your ngrok agents connect to `connect.acme.com` instead of the default connection hostname. This is highly recommended because your customers may need to allowlist egress traffic out of their networks and they see this traffic in their network logs. You can also request dedicated IPs for your connect URL that are unique to your account. ```bash theme={null} curl \ -X POST \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"description":"Custom connect URL","domain":"connect.acme.com"}' \ https://api.ngrok.com/agent_ingresses ``` Once the custom connect URL has been created, specify this field within the agent configuration file: ```yaml theme={null} version: 3 agent: connect_url: connect.acme.com:443 ``` ## What's next * [Install ngrok as a background service](/docs/gateway/site-to-site-connectivity/background-service) to ensure the agent starts on boot and recovers from failures. # Securely connect to remote customer networks from your K8s cluster Source: https://ngrok.com/docs/gateway/site-to-site-connectivity/customer-networks Use the ngrok Kubernetes Operator for secure access to private services in customer networks. ngrok's Gateway allows you to securely connect to any app, IoT device, or service running in a private environment. This guide walks you through a practical example of using the [**ngrok Kubernetes Operator**](/docs/gateway/k8s/) to establish secure, remote access to services running inside a protected customer network which is not k8s native—all managed seamlessly from within your Kubernetes cluster in your cloud instance. While a traditional site-to-site VPN topology can achieve similar connectivity, this approach takes ngrok's secure ingress capabilities to the next level. Connect critical systems without any risk of public addressability or wrestling with the complexity, fragility, and operational overhead of conventional VPN solutions. The result is a privatized, streamlined way to enable secure access. ## Example scenario You run Acme Corp, a managed service provider (MSP) serving several nationwide retail chains. Each of your customers operates an on-premises retail headquarters with essential back-office systems locked behind strict firewalls. Among these critical services are inventory databases, payment APIs (requiring end-to-end encryption through the ngrok cloud to maintain PCI compliance), and store management web portals. To keep operations running smoothly, you need a secure, controlled way to reach these resources directly from your Kubernetes cluster—without exposing them to the public internet or maintaining complex VPN infrastructure. In this guide, walk through setting up remote access to the systems in a single customer HQ (Customer 1) using the ngrok Kubernetes Operator, with a repeatable workflow you can apply across your entire retail network. ## Architectural reference Architecture diagram showing Kubernetes Bindings with ngrok routing from an Acme Cloud cluster through ngrok Cloud Endpoints and Internal Agent Endpoints to a customer on-premises network ### What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * An ngrok agent configured on a server within customer 1's network. See the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/) for instructions on how to install the ngrok agent. * The ngrok Operator installed on your local Kubernetes cluster. * [An ngrok API Key](https://dashboard.ngrok.com/api-keys/new). You'll need an account first. ### 1. Create a Service User and authtoken for Customer 1 First, you'll create a Service User and an associated authtoken for each of your customers. A Service User is intended for automated systems that programmatically interact with your ngrok account (other platforms sometimes call this concept a Service Account). Create a separate Service User + associated authtoken for each of your customers so that: 1. Their usage of your ngrok account is isolated and scoped with a specific permission set 2. If a customer is compromised you can revoke their access independently 3. Agent start/stop audit events are properly attributed to each customer 4. Your ngrok agents don't stop working if the human user who set them up leaves your ngrok account. Navigate to the [Service Users](https://dashboard.ngrok.com/service-users) section of your dashboard and click "new Service User". ngrok dashboard New Service User dialog with a name field set to Service User for Customer 1 Create an authtoken for that Service User and assign it an ACL rule. By assigning an ACL rule, you can restrict what endpoints the Service User with this authtoken can create. Navigate to the authtokens section on your dashboard and click "new authtoken". Assign it an ACL rule as shown below to restrict any agent using it with permission to only create internal endpoints with a URL matching `*.cust1.internal`. ngrok New Authtoken dialog with owner set to a Service User and an ACL rule restricting it to bind:*.cust1.internal endpoints ### 2. Install the ngrok Agent within Customer 1's network and configure internal Agent endpoints in ngrok.yml Configure the agent to create internal Agent Endpoints that point to the services you want to remotely access. This will connect the services to your ngrok account but nothing will be able to connect to them until completing the subsequent steps. Additionally, for the TLS endpoint, configure it so that mTLS is terminated at the ngrok agent and data remains encrypted through the ngrok cloud edge. The configuration to achieve this is shown below in the example agent configuration file. **Internal Endpoints** are private endpoints that only receive traffic when forwarded through the [forward-internal Traffic Policy action](/docs/gateway/traffic-policy/actions/forward-internal/). This allows you to route traffic to an application through ngrok without making it publicly addressable. Internal endpoint URL hostnames must end with `.internal`. After installing the ngrok agent, define internal endpoints for each service you want to remotely access inside the ngrok configuration file. You can install ngrok and its configuration file in `/path/to/ngrok/ngrok.yml` and the executable in `/path/to/ngrok/ngrok`. ```yaml theme={null} version: 3 agent: authtoken: endpoints: - name: Internal Endpoint for Cust 1 Web Application url: http://webapp.cust1.internal upstream: url: 8080 - name: Internal Endpoint for Cust 1 Payment API url: tls://api.cust1.internal upstream: url: 443 agent_tls_termination: server_certificate: "/path/to/app-example-com-crt.pem" server_private_key: "/path/to/app-example-com-crt.key" mutual_tls_certificate_authorities: "/path/to/app-example-com-ca" - name: Internal Endpoint for Cust 1 Inventory Database url: tcp://database.cust1.internal upstream: url: 5432 ``` ### 3. Install the ngrok Operator within your Kubernetes cluster Enable applications in your cluster to connect to the remote services. To do this, install the ngrok Kubernetes Operator into your cluster. The Operator will create v1.Service objects that your apps can connect to for access to the remote services in your customer's network. First, create a separate Service User with an associated authtoken **and** API key specifically for the Operator. Then install the Operator: ```bash theme={null} helm install ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --create-namespace \ --set description="acme ngrok Operator" \ --set bindings.enabled=true \ --set credentials.apiKey=$NGROK_API_KEY \ # api key created for the Operator --set credentials.authtoken=$NGROK_AUTHTOKEN # authtoken created for the Operator ``` Lastly, create a namespace for your customer fleet where the Service objects in the next step will be created. ```bash theme={null} kubectl create ns acme-customers ``` ### 4. Create Kubernetes bound Cloud Endpoints and attach Traffic Policy Create a **Cloud Endpoint** with a [\*\*Kubernetes binding](/docs/gateway/endpoints/bindings/).\*\* This will create a privately addressable way to access the remote services. These endpoints will only be accessible within Kubernetes clusters where you install the ngrok Operator. Endpoints with a kubernetes binding are secure, private endpoints that are only addressable inside of Kubernetes clusters where you install the ngrok Operator. They enable you to connect to ngrok endpoints without making them publicly addressable. In this case, create Cloud Endpoints with a binding of kubernetes. Cloud Endpoints are persistent, always-on endpoints whose creation, deletion, and configuration is managed centrally via the Dashboard or API. They exist permanently until they are explicitly deleted. Cloud Endpoints do not forward their traffic to an agent by default and instead only use their attached Traffic Policy to handle connections. Create a kubernetes bound Cloud Endpoint in the ngrok dashboard by navigating to endpoints and clicking "new". Configure it as shown in the screenshot below: ngrok New Cloud Endpoint dialog with Kubernetes Operator binding selected and URL set to http://webapp-cust1.acme-customers Additionally, create a Cloud Endpoint for the other two services: *"tls\://api-cust1.acme-customers"* and *"tcp\://database-cust1.acme-customers:5432"*. A v1.Service object is created in the namespace determined by the second part of the kubernetes endpoint URL's hostname (acme-customers). The name of the service is denoted by the first part of the endpoint URL's hostname . Once you've created these endpoints, check your acme-customers namespace in your kubernetes cluster, and you should see three v1.Service objects created named webapp-cust1, api-cust1, and database-cust1. Click on your newly created web app Cloud Endpoint and replace the default Traffic Policy with: ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: http://webapp.cust1.internal ``` Click on your newly created API Cloud Endpoint and replace the default Traffic Policy with: ```yaml theme={null} on_tcp_connect: - actions: - type: forward-internal config: url: tls://api.cust1.internal ``` Click on your newly created database Cloud Endpoint and replace the default Traffic Policy with: ```yaml theme={null} on_tcp_connect: - actions: - type: forward-internal config: url: tcp://database.cust1.internal:5432 ``` The [CloudEndpoint Kubernetes custom resource](/docs/gateway/k8s/crds/cloudendpoint/) offers another way to create cluster-addressable endpoints. This infrastructure-as-code approach integrates naturally with Kubernetes workflows while maintaining the same automation capabilities as the ngrok API. By using custom resources, you can manage Cloud Endpoints declaratively—making them accessible only within your cluster while leveraging standard Kubernetes tooling for deployment and lifecycle management. Here's the CloudEndpoint custom resource config with the required Traffic Policy for the web app Cloud Endpoint: ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: acme-k8s-resource namespace: acme-customers spec: url: http://webapp-cust1.acme-customers trafficPolicy: policy: on_http_request: - actions: - type: forward-internal config: url: http://webapp.cust1.internal bindings: - kubernetes ``` ## Additional ngrok features ### Set up a custom connect URL When the ngrok agent starts, it makes an outbound connection via TLS over port 443 to the ngrok cloud service. This connection is made to the "agent connect URL". By default, [this connect URL](/docs/gateway/agent/connect-url/) is *connect.ngrok-agent.com*, but it can be white-labeled so that your ngrok agents will connect to *connect.acme.com* instead of the default connection hostname. This is highly recommended because your customers may need to whitelist egress traffic out of their networks and customers will see this traffic in their network logs. You can also request dedicated IPs for your connect URL that are unique to your account. ```bash theme={null} curl \ -X POST \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"description":"Custom connect URL","domain":"connect.acme.com"}' \ https://api.ngrok.com/agent_ingresses ``` Once the custom connect url has been created, specify this field within the agent configuration file. Add this section to your agent configuration file to specify the custom connect url: ```yaml theme={null} version: 3 agent: connect_url: connect.acme.com:443 ``` ### Install ngrok as a background service You can install ngrok in your customer's network as a native background system service. This ensures the ngrok agent starts on boot and that it will be restarted if it crashes for any reason. This helps ensure that you recover connectivity in the case of unexpected software or hardware failures. When you do so, ngrok will start all endpoints defined in the configuration file. ```bash theme={null} ngrok service install --config /etc/ngrok.yml ngrok service start ``` In most cases, installing ngrok as a service requires administrator privileges. ### Eliminate single points of failure with redundant agents Running a single agent in your customer's network is a single point of failure. If that agent or the hardware it runs on fails for any reason, you can no longer connect to the remote services in your customer's network. You can run multiple agents to achieve redundancy and high availability in the case of these failures. To do this, you will run a separate, second ngrok agent in the customer's network (ideally on a different machine) with an identical configuration file with one small tweak: on each endpoint definition you must specify `pooling_enabled: true`. This is ngrok's Endpoint Pooling feature. When two endpoints start with the same URL and have pooling enabled, they form an endpoint pool. Incoming traffic to the pooled URL is automatically distributed among all healthy endpoints in the pool. If one endpoint goes offline, traffic is seamlessly routed to the remaining endpoints, ensuring redundancy and failover. ```yaml theme={null} # Server 1 (agent1.yml) version: 3 agent: authtoken: endpoints: - name: my-service url: 'https://service.internal' pooling_enabled: true upstream: url: http://localhost:8080 --- # Server 2 (agent2.yml) version: 3 agent: authtoken: endpoints: - name: my-service url: 'https://service.internal' # Same URL as Server 1 pooling_enabled: true upstream: url: http://localhost:8080 ``` ### Customize Agent connection resiliency By default, the ngrok agent is already configured for maximum resiliency and built for persistent connectivity. There are some additional measures you can take to ensure control over this. ### Connect timeout config variable There also exists a config variable (connect\_timeout) to specify how long to wait when establishing an agent session connection to the ngrok service. This is specified as a duration, with the default being 10s. ```yaml theme={null} version: 3 agent: authtoken: connect_timeout: 5s endpoints: - name: my-service url: 'https://app.example.com' upstream: url: http://localhost:8080 ``` ### Agent heartbeat The agent heartbeat mechanism is crucial for maintaining endpoint stability and ensuring fast reconnections when agents become unhealthy. The ngrok agents send periodic heartbeat signals to the ngrok edge servers where the default interval is 10 seconds. This enables rapid detection of failed agents. Heartbeat interval and tolerance values can be specified in the agent configuration file allowing for flexibility in detection strategy. ```yaml theme={null} version: 3 agent: authtoken: heartbeat_interval: 10s # How often to send heartbeat heartbeat_tolerance: 15s # How long before considering connection dead endpoints: - name: my-service url: 'https://app.example.com' upstream: url: http://localhost:8080 ``` ## Recap You have now integrated a system that allows you to seamlessly and granularly access your remote resources within your customer's network directly from your Kubernetes cluster. Recap what you've built: 1. Singular ngrok Operator installed in your k8s cluster to create service objects using k8s Cloud Endpoints. 2. One ngrok agent per customer network, with multiple installs/endpoint pooling available for resiliency failover and reliability. 3. Private, kubernetes bound Cloud Endpoints used in service projection, forwarded traffic to internal Agent Endpoints for e2e connectivity. 4. E2E encryption with agent mTLS termination and TLS endpoints # Why Your Vendor Uses ngrok Source: https://ngrok.com/docs/gateway/site-to-site-connectivity/end-customers Why your vendor has requested you install ngrok for site-to-site connectivity, how it works, and why it's secure. ## Overview ### Why you're being asked to install ngrok Your vendor wants to create a secure persistent connection between your network and theirs, which allows them to access and take action on your services and data. This is called [**site-to-site connectivity**](https://ngrok.com/use-cases/saas-customer-connectivity). Your vendor will create the required configuration files and deployment strategy and share them with you directly. You must contact with your vendor to request changes to that configuration, including those based on the content of this document. ### What is ngrok? ngrok's Gateway is an all-in-one reverse proxy, API gateway, Kubernetes ingress, firewall, and global load balancer to deliver apps and APIs. ### Why is ngrok used instead of other solutions, like a VPN or opening ports? ngrok is simpler and more secure than these other solutions for a few reasons: * ngrok does not require you to open inbound ports in your network to the public internet. * ngrok's agent is infrastructure-agnostic, operating the same way in any cloud, region, or environment. * The agent is simpler to configure, deploy, and maintain than VPNs or VPC peering by collapsing many networking components, like load balancing, encryption, certificate management, and authentication into a unified platform. * ngrok's network includes DDoS protection and global acceleration for all connections through the [Global Server Load Balancer](https://ngrok.com/blog-post/gslb-global-server-load-balancing) (GSLB). ngrok's solution supports multiple models for TLS encryption, including end-to-end encryption. Learn more about how your vendor can configure ngrok's encryption: [How does ngrok's traffic encryption work?](#ngrok-traffic-encryption) ### Who else uses ngrok to provide access to private services? Organizations worldwide trust ngrok for site-to-site connectivity, unified ingress, device gateways, and developer productivity. Customers include Twilio, Okta, Zoom, Microsoft, Zendesk, Cyera, and many more. [Databricks](https://ngrok.com/customers/databricks), the leading unified lakehouse platform for data, analytics, and AI, uses ngrok for its site-to-site connectivity across all of its customers. You can learn more about customers and read case studies about their successes on the [customers page](https://ngrok.com/customers). ### Who is ngrok? Where is it based? ngrok was first released in 2013 as an open-source project by Alan Shreve, who founded ngrok as a company in 2015 and remains the CEO. The executive team has extensive experience at companies building distributed systems and winning organization cultures. In December 2022, ngrok [closed \$50 million in Series A funding](https://techcrunch.com/2022/12/13/ngrok-a-service-to-help-devs-deploy-sites-services-and-apps-raises-50m/) with [Lightspeed Venture Partners](https://lsvp.com/) and [Coatue](https://www.coatue.com/). ngrok has a primarily U.S.-based workforce. More information on the geographic distribution of full time employees and contractors can be found at the [Trust Center](https://trust.ngrok.com). ### Can ngrok be trusted as a company? 15.3 million developers use ngrok. It is recommended by category leaders including Twilio, GitHub, Okta, Microsoft, Zoom, and Shopify. ngrok operates a global network and has handled over 100 trillion total requests. ngrok is SOC 2 Type 2 compliant, which certifies that security processes and operations meet AICPA's criteria for security. ngrok is also CCPA, EU-US DPF, and GDPR compliant. ## How ngrok works ### How does ngrok work? ngrok has three primitives: the **cloud service**, the [**agent**](/docs/gateway/agent/), and the [**dashboard**](https://dashboard.ngrok.com/). * The cloud service is globally distributed infrastructure that accepts traffic to endpoints, applies policies, and routes connections to the appropriate agent. * The agent is a lightweight program with zero runtime dependencies, which forwards requests from your vendor to your upstream services and data. * The dashboard is a SaaS web app for configuring accounts, endpoints, and access policy. Depending on the architecture and arrangement with your vendor, you may or may not have access to an ngrok dashboard. In a generic site-to-site connectivity architecture, these pieces come together to do the following: 1. The ngrok agent in your network connects to one or more of your upstream services. 2. The ngrok agent connects out to the ngrok network at the default [Connect URL](/docs/gateway/agent/connect-url/) at `connect.ngrok-agent.com` (or a custom one). 3. The ngrok network creates an endpoint like `your-company.your-vendor.com`, which then points to your upstream service. 4. Your vendor sends requests from their services to the endpoint, which are then routed through to the agent and finally your upstream service. ### What is ngrok's network architecture? ngrok's primary network and services run on AWS. This architecture includes multiple [Points of Presence](/docs/gateway/points-of-presence/) (PoPs) worldwide that run services, route customer traffic, and make up the always-on [Global Server Load Balancer](https://ngrok.com/blog-post/gslb-global-server-load-balancing) (GSLB). The GSLB, which is active by default for all ngrok endpoints and tunnels, enhances the resiliency of the connection between your services and your vendor's with DDoS protection and geo-failover that automatically routes traffic to the lowest-latency PoP in the event of downtime or service interruption. If you have specific data residency requirements (like those located in the EU), then you and your vendor can configure ngrok to use a specific region in Europe or pin all traffic to a specific PoP. There are many possible architectures for setting up a site-to-site network based on the shared requirements of you and your vendor. Your vendor will work with you to find a mutually secure and reliable architecture. ### How does ngrok handle data residency requests? Your vendor can configure your site-to-site architecture to match your data residency needs. They will first [configure the agent](/docs/gateway/agent/changelog/) to use a PoP in one of the [supported regions](/docs/gateway/points-of-presence/), then work with you to set up appropriate DNS to route all connections through the same data plane. Regional data planes are located in Australia (Sydney), Europe (Frankfurt), India (Mumbai), Japan (Tokyo), South America (São Paulo), United States (California and Ohio). ### Is ngrok a dedicated instance or multi-tenant? ngrok is a multi-tenant application with services shared across the customer base, including your vendor and your site-to-site architecture. ## Security ### How to ensure ngrok does not pose a security risk to your infrastructure It is recommended to begin by exploring the [Security, Privacy, and Compliance](https://ngrok.com/security) page, followed by the [Trust Center](https://trust.ngrok.com/). Your vendor can implement multiple security practices, including: * [Prevent unauthorized usage of ngrok](#how-can-i-prevent-ngrok-from-being-used-for-any-purpose-other-than-in-site-to-site-connectivity-with-my-vendor?) outside of the site-to-site connectivity with your vendor. * Create a single account tenant, enforce [Account Domain Controls](https://ngrok.com/blog-post/account-domain-controls) to route all ngrok users with an email address `@your-company.com` into it, and enable role-based access controls to limit those accounts. * Create [Service Users](/docs/iam/service-users/) to create authtokens not tied to any individual user, and use a different, scoped authtoken for each agent. * Apply [Access Control Lists](/docs/iam/rbac/#additional-restrictions-with-acls) (ACLs) to authtokens to control where ngrok agents can be created within your network. * Enable the [IP restrictions](/docs/gateway/traffic-policy/actions/restrict-ips/) action on your endpoints to allow only trusted connections from your vendor. * Enforce [basic auth](/docs/gateway/traffic-policy/actions/basic-auth/), [OAuth](/docs/gateway/traffic-policy/actions/oauth/), [OpenID](/docs/gateway/traffic-policy/actions/oidc/), [JWT](/docs/gateway/traffic-policy/actions/jwt-validation/), [SAML](/docs/gateway/traffic-policy/actions/saml), or [mTLS](/docs/gateway/domains/mtls-termination/) authentication on your endpoints. The configuration and operation of these security practices will be handled by your vendor. ### How does ngrok's traffic encryption work? The agent always connects to the ngrok network via TLS. Three encryption models are supported based on where TLS is *terminated*: 1. **TLS termination at the ngrok network**. This is the default behavior when using HTTPS tunnels, where ngrok manages TLS certificate generation and renewal for both you and your vendor. 2. **TLS termination at the ngrok agent**. Often referred to as [zero-knowledge TLS](/docs/gateway/domains/tls-termination/#end-to-end-encryption), this model encrypts your TLS tunnels end-to-end, using filesystem paths to your TLS certificate and key, without requiring you to reconfigure your upstream service for TLS termination. 3. **TLS termination at your upstream service**. This model implements [end-to-end zero-knowledge encryption](/docs/gateway/domains/tls-termination/#end-to-end-encryption), where the ngrok network forwards unterminated connections through to your upstream application. ### Is data transmitted through ngrok end-to-end encrypted? Yes—if you vendor configures ngrok to terminate TLS at the agent or your upstream service using the second or third models listed above. Contact your vendor if you're unsure how TLS termination is managed in your site-to-site connectivity architecture. ### Can ngrok see the traffic it forwards? No—if your vendor configures ngrok to terminate TLS at the agent or your upstream service. In all encryption models, the ngrok agent cannot see the traffic it forwards on to your upstream service. If your vendor requests HTTP tunnels for your site-to-site connectivity use case and also uses the [Traffic Inspector](/docs/obs/traffic-inspection/#ngrok-traffic-inspector) feature, then ngrok can [see and will store](https://ngrok.com/blog-post/data-at-ngrok#traffic-inspector-provides-network-traffic-observability-throughout-the-ngrok-platform) up to 90 days of metadata about each request and response. If your vendor enables [Full Capture mode](/docs/obs/traffic-inspection/#full-capture-mode), ngrok also stores the full request and response parameters, headers, and bodies for each traffic event. ### Does ngrok support deep packet inspection or observing what data is transmitted through ngrok? Yes. Your vendor can configure your site-to-site connectivity architecture in a few ways to enable this functionality. They can: 1. Configure your ngrok agent to trust a [specific root certificate](/docs/gateway/agent/config/v3/#connect-cas) you own on the host's OS or a specific PEM file on disk instead of the trusted certificate root for the ngrok network. You can then use a proxy for deep packet inspection. 2. Require the outbound connection to `connect.ngrok-agent.com:443` go through a proxy capable of inspection. 3. Set up a bypass rule for `connect.ngrok-agent.com:443` (or a custom Connect URL hostname) to not perform TLS inspection. 4. Help you set up software between the ngrok agent and your upstream service, or the ngrok agent and the ngrok network, to see what's transmitted through ngrok. In any case, your vendor is responsible for configuring these inspection strategies for your site-to-site architecture. ### How to prevent ngrok from being used for any purpose other than in site-to-site connectivity with your vendor The best way to disallow other uses of ngrok on your network is working with your vendor to specify a [custom Connect URL](/docs/gateway/agent/connect-url/#custom-connect-url). Your vendor will configure their DNS to issue certificates for the custom address, such as `your-company.us.connect.your-vendor.com:443`. Then they'll work with you to [reconfigure your ngrok agents](/docs/gateway/agent/connect-url/) to utilize the custom Connect URL. Your vendor can also work with ngrok to provision dedicated IPs for their custom Connect URL. At this point, you can block the default Connect URL at `connect.ngrok-agent.com:443`, which all agents use by default to connect outbound to ngrok's network. This address resolves to a dynamic set of IP addresses, and blocking it at your network prevents any usage outside of this site-to-site connectivity use case in partnership with your vendor, such as developers utilizing ngrok to tunnel local development environments to the public internet. You should also block the [ngrok-managed public domains](/docs/gateway/domains/#ngrok-managed-domains), for example, `ngrok.app`, `ngrok.io`, etc. Work with your vendor to design an architecture that [prevents unauthorized use](#how-can-i-prevent-ngrok-from-being-used-for-any-purpose-other-than-in-site-to-site-connectivity-with-my-vendor?) and uses the appropriate [encryption model](#ngrok-traffic-encryption) between your services. ### How does ngrok mitigate abuse of its platform? ngrok has a multi-pronged strategy for combating malicious use of the network, including automated systems that flag suspicious activity in combination with human moderation. ngrok works with authorized third-party security vendors, who report suspected abuse to [abuse@ngrok.com](mailto:abuse@ngrok.com) or the [abuse API](/docs/api-reference/abusereports/create). The abuse response team investigates these reports, collaborates with these third parties, and proactively shuts down attacks. Various passive abuse prevention techniques are employed, such as restricting product usage for free and unverified ngrok accounts. The [IP Intelligence](/docs/gateway/traffic-policy/variables/ip-intel/) system is also used to prevent signups from IPs on trusted blocklists, from specific regions, and those using anonymous proxies. Finally, global firewalls protect against unauthorized access and DDoS attacks. See the [abuse page](https://ngrok.com/abuse) for more details. ## Privacy and compliance ### What compliance certifications does ngrok maintain? ngrok complies with SOC 2, GDPR, EU-US DPF, and CCPA. You can request the SOC 2 report and view other documents about ngrok's security measures, like annual penetration testing, on the [Trust Center](https://trust.ngrok.com/). ### What data does ngrok have access to, and how long is it stored? ngrok's access to your data depends on the [encryption model](#ngrok-traffic-encryption) specified by your site-to-site connectivity architecture—reach out to your vendor for more details. In all cases, ngrok stores information about the machine where you run your agent, such as its IP address, operating system, CPU architecture, and anonymized details about the environment. ngrok also logs changes to account configurations, such as agent start/stop, API key creation, and more. You can see the list in the [Log Store](/docs/obs/events/reference/) documentation. Data about your site-to-site connection is retained at the direction of your vendor, who can also delete data at any time. Read the [primer on data at ngrok](https://ngrok.com/blog-post/data-at-ngrok) for more details. ### Where does data transmitted through ngrok go? ngrok is split into a control plane and data plane. The control plane is responsible for handling API requests and is the source of truth for account and user data. The data planes are responsible for hosting the tunnels and routing the customer traffic. ngrok also leverages several third party providers when handling customer data. Control Plane: Any information stored by ngrok would reside in the control plane. This is hosted out of a U.S.-based AWS region. Data Plane: Customer traffic runs through ngrok's regional data planes, located in Australia (Sydney), Europe (Frankfurt), India (Mumbai), Japan (Tokyo), South America (São Paulo), United States (California and Ohio)\*. By default, ngrok will route traffic via the most low latency route. Customers can control which regional data plane their tunnels connect to via the agent configuration and which regional data plane their customers connect into via DNS. If a region is specified, customer traffic will only be routed through that region. ### What security practices does ngrok follow? ngrok uses the shared security responsibility model where ngrok is responsible for the security of the network, and delivers features your vendor can use to configure and secure your site-to-site connectivity architecture. Fundamental security practices include access control via an identity provider, change management, full encryption at rest, and much more. See the [Security, Privacy, and Compliance](https://ngrok.com/security) and [Trust Center](https://trust.ngrok.com/) pages for details. ## Operations and Agent deployment ### Where can the ngrok Agent be run and what are the system requirements? The ngrok agent runs on Linux, Windows, and macOS systems and most CPU architectures. See the [supported platforms documentation](/docs/gateway/agent/install#system-requirements) for details about supported CPU architectures and resource requirements. The agent is also distributed as [SDKs](/docs/agent-sdks/), a [Docker container](https://hub.docker.com/r/ngrok/ngrok), and a [Kubernetes Operator](/docs/gateway/k8s/). Your vendor will work with you to find the right form factor for your site-to-site connectivity architecture. ### How to manage the lifecycle and maintenance of the Agent Your vendor is responsible for helping you configure and maintain your agents. They might configure your ngrok agent to [run in the background](/docs/gateway/agent/service#running-ngrok-in-the-background) as a native operating system service. This functionality works on all Linux, Windows, and macOS systems, and once installed, the ngrok service starts at boot-time, automatically restarts after crashes, and sends logs to your system's native logging service. If they suggest deploying with Docker, they will likely recommend Docker's [restart policies](https://docs.docker.com/engine/reference/run/#restart-policies-restart) or [systemd integration](https://docs.docker.com/engine/install/linux-postinstall/#configure-docker-to-start-on-boot-with-systemd); with the [Kubernetes Operator](/docs/gateway/k8s/), they will recommend the [container restart policy](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy) is set to `Always`. ngrok releases security and feature updates through all installation channels and package managers. These updates are *not* automatic. Do not update your ngrok agents manually—your vendor will work with you on when and how to update your ngrok agents because the process depends on how they asked you to install them. * Package manager (`brew` or `apt`): Get updated agent versions through the same channel (for example, `brew update && brew upgrade package_name` or `apt update && apt install ngrok`). * Binary from the [downloads page](https://download.ngrok.com): Follow the same process again or [update directly from your CLI](/docs/gateway/agent/install#updates) with `ngrok update`. * Agent SDK: Use the package manager for your language or framework (for example, `go get -u`) * Kubernetes Operator: Use `helm upgrade`. ### Can I run the ngrok Agent in a DMZ? Yes. There are two requirements in this case: 1. The systems in your DMZ must support the general [system requirements](#where-can-i-run-the-ngrok-agent-and-what-are-the-ngrok-agent's-system-requirements?). 2. The upstream service your vendor wants to connect to with ngrok must also be running in your DMZ. ### How does the Agent connect to the ngrok network? By default, the ngrok agent attempts to connect to the default Connect URL at `connect.ngrok-agent.com:443`, which resolves to a dynamic list of IP addresses. You can view the full list of addresses and IPs available for the agent to connect to in the [AWS-hosted `tunnel.json` file](https://s3.amazonaws.com/dns.ngrok.com/tunnel.json). This connection includes the following steps: 1. [DNS resolution](/docs/gateway/agent/connectivity#dns-resolution) to discover the appropriate connection address. 2. [Verification](/docs/gateway/agent/connectivity#tls-verification) of the TLS certificate returned by the ngrok network, signed by ngrok's root certificate authority, against the certificate authorities bundled into the agent. 3. A Certificate Revocation List (CRL) check to `crl.ngrok-agent.com/ngrok.crl:80` to ensure you never use an ngrok network certificate that has been revoked. After the agent connects to the network, it also sends [heartbeat](/docs/gateway/agent/connectivity#heartbeats) messages to validate the liveness of the connection, and [checks for updates](/docs/gateway/agent/connectivity#ngrok-agent-update-check) on startup. ### How does the Agent continue running if my server loses connection or times out? The ngrok agent utilizes a [heartbeat](/docs/gateway/agent/connectivity#heartbeats) that attempts to reach ngrok's network every 10 seconds. If the agent doesn't receive a response within the tolerance window (15 seconds by default), it terminates the existing connection and attempts to reconnect. This mechanism allows your site-to-site connectivity to automatically reestablish after packet loss, dynamic IP changes, or complete network outages. Your vendor can [configure](/docs/gateway/agent/config/v3/#heartbeat-interval) both the heartbeat interval and tolerance per agent. ### How does using ngrok, and the availability of the platform, affect your own? ngrok is only utilized for connectivity to the agent. It won't affect the rest of your platform in any way. The ngrok platform is designed for high availability. It operates out of 8 separate isolated regions and multiple availability zones in each region within the AWS network. Your traffic will take the lowest latency path to the ngrok servers and will fail over to a secondary region in the case of network outages or infrastructure failures. ### Who to contact for support For issues or concerns around configuring and securing your site-to-site architecture, please contact your vendor. # Site-to-Site Connectivity FAQ Source: https://ngrok.com/docs/gateway/site-to-site-connectivity/faq Answers to common questions about setting up and managing site-to-site connectivity with ngrok. ## General ### How does site-to-site connectivity work with ngrok? Your customer runs a lightweight ngrok agent inside their network that creates a secure, outbound TLS connection to the ngrok cloud service on port 443. You create Cloud Endpoints that route traffic through that connection to the customer's internal services. No inbound ports need to be opened in the customer's network. ### Do I need one agent per service in the customer's network? No. A single ngrok agent acts as a central gateway that can reach any service on the local network. You configure multiple Internal Endpoints in the agent's configuration file, each pointing to a different upstream service. ### Does the customer need to make firewall changes? No. The ngrok agent connects outbound over TLS on port 443, which is typically already allowed. No inbound firewall rules or open ports are required. ### Why use ngrok instead of a VPN or VPC peering? Running an agent container is often more straightforward than configuring VPC peering. The ngrok agent works the same way in any cloud, and any on-prem environment. You don't need to reverse-engineer your customer's network or firewall configuration. ## Security ### How do I authenticate connections to customer services? You can authorize connections from your cloud to ngrok using Traffic Policy. With Traffic Policy, you can authenticate connections using [IP restrictions](/docs/gateway/traffic-policy/actions/restrict-ips/), [JWT validation](/docs/gateway/traffic-policy/actions/jwt-validation/), [OAuth](/docs/gateway/traffic-policy/actions/oauth/), [OpenID Connect](/docs/gateway/traffic-policy/actions/oidc/), [SAML](/docs/gateway/traffic-policy/actions/saml/), and more. Apply these as Traffic Policy actions on your Cloud Endpoints. ### Can I scope access to specific services? Yes. By default, you access only the specific APIs and databases at the ports associated with your Internal Endpoints. ### Is traffic end-to-end encrypted? Yes, if you configure TLS termination at the ngrok agent or at the upstream service. In these models, the ngrok cloud service only sees ciphertext. See [TLS termination options](/docs/gateway/domains/tls-termination/#end-to-end-encryption) for details. ### Can ngrok see the traffic it forwards? No. TLS terminates at the agent or upstream service. In all encryption models, the ngrok agent itself cannot see the traffic it forwards. See the [end customer guide](/docs/gateway/site-to-site-connectivity/end-customers#ngrok-traffic-encryption) for a full breakdown of the encryption models. ### How do I prevent unauthorized use of ngrok in the customer's network? Set up a [custom connect URL](/docs/gateway/site-to-site-connectivity/custom-connect-url) so the agent connects to your branded domain (for example, `connect.acme.com:443`) instead of the default `connect.ngrok-agent.com`. The customer can then block the default connect URL, preventing any other ngrok usage on their network. ### How do I lock down what agents can do? Apply [Access Control Lists](/docs/iam/rbac/#additional-restrictions-with-acls) (ACLs) to authtokens to restrict which endpoints an agent can create. Since you can't fully trust agents running in a customer's network, ACLs lock them down to only create the endpoints you authorize. ### What compliance certifications does ngrok maintain? ngrok complies with SOC 2 Type II, HIPAA (with BAA), GDPR, CCPA, and EU-US DPF. ngrok also supports RBAC, SSO with SCIM, audit logs, data residency controls, and white-labeling. Visit the [Trust Center](https://trust.ngrok.com/) for reports and documentation. ## Reliability ### What happens if the agent loses its connection? The ngrok agent sends a [heartbeat](/docs/gateway/agent/connectivity#heartbeats) every 10 seconds. If it doesn't receive a response within the tolerance window (15 seconds by default), it terminates the existing connection and automatically reconnects. This allows site-to-site connectivity to recover after packet loss, dynamic IP changes, or complete network outages. ### How do I achieve high availability? Run multiple agents in the customer's network with [Endpoint Pooling](/docs/gateway/site-to-site-connectivity/redundant-agents) enabled. Connections are balanced across all healthy agents. If one agent or its host machine fails, traffic is seamlessly routed to the remaining agents. ### Does ngrok stay online during region outages? Yes. Agents create secure connections to multiple [Points of Presence](/docs/gateway/points-of-presence/) in the ngrok cloud service. If an entire data center fails, traffic automatically fails over to a healthy region. ### Can I monitor agent connectivity? Yes. You can publish tunnel status and connection events to your telemetry platform using ngrok's [event subscriptions](/docs/obs/). When a connection drops, you'll know before your customer does. ## Data residency ### Can I restrict traffic to specific regions? Yes. Configure the agent to connect to a PoP in one of the [supported regions](/docs/gateway/points-of-presence/), then set up DNS to route all connections through the same data plane. Regional data planes are located in Australia (Sydney), Europe (Frankfurt), India (Mumbai), Japan (Tokyo), South America (São Paulo), and the United States (California and Ohio). ### Is ngrok multi-tenant? Yes. ngrok is a multi-tenant application with services shared across the customer base. If you need a dedicated instance, [inquire about ngrok's private edition](https://ngrok.com/contact). ## Deploying the Agent ### Where can the agent run? The ngrok agent runs on Linux, Windows, and macOS across most CPU architectures. It's also available as a [Docker image](https://hub.docker.com/r/ngrok/ngrok), [Kubernetes Operator](/docs/gateway/k8s/), and [agent SDKs](/docs/agent-sdks/) for Go, Python, JavaScript, and Rust. ### Can I embed the agent in my own application? Yes. Use the [agent SDKs](/docs/agent-sdks/) to embed ngrok directly in your code. This is useful when your software is already running in the customer's environment, like a bring-your-own-cloud deployment. ### How do I keep the agent running? [Install ngrok as a system service](/docs/gateway/site-to-site-connectivity/background-service) so it starts on boot, automatically restarts after crashes, and sends logs to the system's native logging service. ### How do I update agents? ngrok releases updates through all installation channels, but updates are not automatic. Coordinate with your customer on when and how to update based on the deployment method. See the [agent version support policy](/docs/gateway/agent/version-support-policy/) for details. ### Can the Agent run in a DMZ? Yes, as long as the systems in the DMZ meet the agent's [system requirements](/docs/gateway/agent/install#system-requirements) and the upstream service is also reachable from the DMZ. ## Branding and automation ### Can I white-label the connection? Yes. Use a [custom connect URL](/docs/gateway/site-to-site-connectivity/custom-connect-url) to brand the address the agent connects to with your own domain. You can also use [custom domains](/docs/gateway/domains/custom-domains/) for your Cloud Endpoint URLs and request dedicated IPs. ### Can I automate setup across customers? Yes. Every ngrok feature has a corresponding [API](/docs/api-reference/) so you can automate provisioning across all your customers. ngrok also integrates with declarative IaC tools like [Terraform](https://registry.terraform.io/providers/ngrok/ngrok/latest/docs). ### How do I explain ngrok to my customers? Share the [end customer guide](/docs/gateway/site-to-site-connectivity/end-customers) with your customers. It covers what ngrok is, why it's being installed, how it works, security details, and operational considerations. # Connect to Your Customer's Network Source: https://ngrok.com/docs/gateway/site-to-site-connectivity/index Follow an in-depth tutorial to learn how to use ngrok to remotely access APIs and DBs in your customer's network, connecting remotely without a VPN. ngrok's Gateway allows you to connect to any app, IoT device, or service without networking expertise. This guide will walk you through an example scenario using ngrok to set up secure, remote access to APIs and DBs running in your customer's on prem, private network. The solution will enable you to grant trusted parties access to critical systems without exposing those systems to the public internet or relying on complex VPN setups. When you need to explain this setup to customers who install and run the ngrok agent in their network, you can share our [end-customer guide](/docs/gateway/site-to-site-connectivity/end-customers) which covers why ngrok is used, how it works, and why it's secure. ## Example scenario Consider a situation where your company (Acme Corp.) needs direct access to resources running in your customer's private network. This network is a clinic that runs a third party API and a DB containing sensitive patient information. In this scenario, this clinic blocks inbound connections. However, your SaaS applications need to reach that API to make requests and fetch data from that DB for mission critical operations. Specifically, the patient DB needs to be e2e TLS encrypted, with mTLS terminating in the customer's network. ## Architectural reference Architecture diagram showing a SaaS Platform routing through ngrok Cloud Endpoints and Internal Agent Endpoints to an ngrok Agent in a remote clinic network serving an API and patient database ### Why only one ngrok Agent per network? Traditionally, you might assume that every server inside the network needs its own ngrok agent, but this isn't necessary. A single ngrok agent is installed on a network-accessible machine inside the network. The agent acts as a central gateway (jumpbox) that can reach any machine on the local network, eliminating the need for multiple agents. ## Tutorial ### What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * An ngrok agent configured in a remote network. See the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/) for instructions on how to install the ngrok agent. ### 1. Create a Service User and authtoken for isolated network access First, you'll create a service user and an associated authtoken for each of your customers. A Service User is intended for automated systems that programmatically interact with your ngrok account (other platforms sometimes call this concept a Service Account). Create a separate Service User + associated authtoken for each of your customers so that: 1. Their usage of your ngrok account is isolated and scoped with a specific permission set 2. If a customer is compromised you can revoke their access independently 3. Agent start/stop audit events are properly attributed to each customer 4. Your ngrok agents don't stop working if the human user who set them up leaves your ngrok account. Navigate to the [Service Users](https://dashboard.ngrok.com/service-users) section of your dashboard and click "new Service User". ngrok dashboard New Service User dialog with a name field for creating an isolated service account per customer Next, create an authtoken assigned to this specific service user. ngrok dashboard New Authtoken dialog with description and owner assigned to a Service User for isolated network access ### 2. Install ngrok and configure Internal Endpoints Configure the agent to create internal Agent Endpoints that point to the services you want to remotely access. This will connect the services to your ngrok account but nothing will be able to connect to them until completing the subsequent steps. The configuration to achieve this is shown below in the example agent configuration file. Internal Endpoints are private endpoints that only receive traffic when forwarded through the [forward-internal Traffic Policy action](/docs/gateway/traffic-policy/actions/forward-internal/). This allows you to route traffic to an application through ngrok without making it publicly addressable. Internal endpoint URL hostnames must end with `.internal`. After installing the ngrok agent, define internal endpoints for each service you want to remotely access inside the ngrok configuration file. Additionally, here is where you'll configure the agent to terminate mTLS for the patient DB endpoint. You can install ngrok and its configuration file in `/path/to/ngrok/ngrok.yml` and the executable in `/path/to/ngrok/ngrok`. ```yaml theme={null} version: 3 agent: authtoken: AUTHTOKEN_CREATED_IN_STEP_1 endpoints: - name: Internal Endpoint for API url: https://api.internal upstream: url: 8080 - name: Internal Endpoint for patient DB url: tls://patient-db.internal upstream: url: 8081 agent_tls_termination: server_certificate: /path/to/your-cert.crt server_private_key: /path/to/your-key.key mutual_tls_certificate_authorities: - "list CAs here" ``` ### 3. Create your Cloud Endpoints and attach a Traffic Policy Cloud Endpoints are persistent, always-on endpoints whose creation, deletion, and configuration is managed centrally via the Dashboard or API. They exist permanently until they are explicitly deleted. Cloud Endpoints do not forward their traffic to an agent by default and instead only use their attached **Traffic Policy** to handle connections. Create a public Cloud Endpoint in the ngrok dashboard by navigating to endpoints and clicking **new** as shown in the screenshot below: ngrok dashboard New Cloud Endpoint dialog with Public binding selected and URL set to https://api.acme.com Additionally, create a Cloud Endpoint for the patient DB: `tls://db.acme.com`. Click on your newly created API Cloud Endpoint and replace the default Traffic Policy with: ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://api.internal ``` Click on your newly created DB Cloud Endpoint and replace the default Traffic Policy with: ```yaml theme={null} on_tcp_connect: - actions: - type: forward-internal config: url: tls://db.internal ``` ### 4. Secure your API Cloud Endpoint with IP restrictions Navigate to your newly created Cloud Endpoints in the [endpoints](https://dashboard.ngrok.com/endpoints) tab on your ngrok dashboard, and apply a Traffic Policy action to each. For this example, you can apply IP restrictions to your API endpoint. Browse through the full list of Traffic Policy actions listed [here](/docs/gateway/traffic-policy/actions/). You can add this action directly to the Cloud Endpoint's YAML configuration. The final Traffic Policy config for each endpoint can be seen below: **[https://api.acme.com](https://api.acme.com)** ```yaml theme={null} on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - "allowed ips here (IPv4 or IPv6)" - type: forward-internal config: url: https://api.internal ``` Now that you have this Traffic Policy action in place, you have IP restrictions layered in front of your API endpoint, and your connection to the patient DB is end-to-end encrypted, with the ngrok agent validating mTLS. ## What's next * [Share the end-customer guide](/docs/gateway/site-to-site-connectivity/end-customers) with customers who need to understand why ngrok is in their network. * [Set up a custom connect URL](/docs/gateway/site-to-site-connectivity/custom-connect-url) to white-label the agent connection for your customers. * [Install ngrok as a background service](/docs/gateway/site-to-site-connectivity/background-service) to ensure the agent starts on boot and recovers from failures. * [Eliminate single points of failure with redundant agents](/docs/gateway/site-to-site-connectivity/redundant-agents) to achieve high availability. # Private Connectivity to Remote Customer Networks with ngrokd Source: https://ngrok.com/docs/gateway/site-to-site-connectivity/non-k8s-environment Use ngrokd to configure private, remote access to services in remote customer networks. ngrok's Gateway allows you to connect to any app, IoT device, or service without networking expertise. This guide walks you through how to configure `ngrokd`: an ngrok component that enables a private, end-to-end solution for accessing remote services in a customer's private network. This solution lets you securely access critical systems remotely without exposing them through public, authenticated endpoints. Instead, it leverages private endpoints that are only reachable from your local environment, eliminating your external attack surface while maintaining full access. ## Architectural reference Architecture diagram showing ngrokd in a local network connecting through Private Agent Endpoints in ngrok Cloud to an ngrok agent serving REST API and Postgres DB in a remote customer network ## What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * An ngrok API key. You'll need an account first. * An ngrok agent installed in a remote customer network. See the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/) for instructions on how to install the ngrok agent. ## 1. Install ngrokd in your local environment, start it, and set your API key `ngrokd` is a lightweight daemon that makes your ngrok endpoints privately accessible and locked down to your local services. It automatically discovers your endpoints, assigns them local addresses, and forwards traffic through ngrok's secure infrastructure, allowing your apps to reach remote services without any network configuration. Think of it as a local proxy that turns ngrok endpoints into services that feel like they're running on your machine. ```bash theme={null} curl -fsSL https://raw.githubusercontent.com/ngrok/ngrokd/main/install.sh | sudo bash sudo ngrokd install sudo ngrokd start ngrokctl set-api-key YOUR_NGROK_API_KEY ``` ```bash theme={null} curl -fsSL https://raw.githubusercontent.com/ngrok/ngrokd/main/install.sh | sudo bash sudo ngrokd install sudo ngrokd start ngrokctl set-api-key YOUR_NGROK_API_KEY ``` ```powershell theme={null} # Run as Administrator iwr -useb https://raw.githubusercontent.com/ngrok/ngrokd/main/install.ps1 | iex Start-Process ngrokd -ArgumentList '--config=C:\ProgramData\ngrokd\config.yml' -WindowStyle Hidden ngrokctl set-api-key YOUR_NGROK_API_KEY ``` ```bash theme={null} docker pull ngrok/ngrokd:latest docker run -d \ --name ngrokd \ --cap-add=NET_ADMIN \ -e NGROK_API_KEY= \ -v ngrokd-data:/etc/ngrokd \ ngrok/ngrokd:latest ``` ## 2. Create a service user and authtoken for isolated network access Create a service user and an associated authtoken for each of your customers. A service user is intended for automated systems that programmatically interact with your ngrok account (other platforms sometimes call this concept a Service Account). Create a separate service user and associated authtoken for each customer so that: * Their usage of your ngrok account is isolated and scoped with a specific permission set * If an agent is compromised, you can revoke its access independently * Agent start/stop audit events are properly attributed to each customer * Your ngrok agents don't stop working if the human user who set them up leaves your ngrok account Navigate to the [Service Users](https://dashboard.ngrok.com/service-users) section of your dashboard and click **New Service User**. ngrok dashboard New Service User dialog with a name field for creating an isolated service account per customer Next, create an authtoken assigned to this specific service user. ngrok dashboard New Authtoken dialog with description and owner assigned to a Service User for isolated network access ## 3. Install the ngrok agent within your remote network and configure private Agent Endpoints in ngrok.yml First, install the agent in the customer's remote network, either on a gateway or directly on a device. Follow the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/) for installation instructions. Then, configure the agent to create private Agent Endpoints pointing to the services you want to remotely access. This connects the services to your ngrok account, and the configuration is shown in the example agent configuration file below. **Private Agent Endpoints** are private endpoints that can **only** receive traffic from services on your local network where `ngrokd` is installed. These endpoints are not publicly addressable anywhere on the internet, and access is **completely locked down** to your local environment. After installing the ngrok agent, define private Agent Endpoints for each service you want to remotely access inside the ngrok configuration file at `/path/to/ngrok/ngrok.yml`: ```yaml theme={null} version: 3 agent: authtoken: AUTHTOKEN_CREATED_IN_STEP_2 endpoints: - name: Internal Endpoint for Controller url: http://controller.example.com upstream: url: 9080 bindings: - kubernetes - name: Internal Endpoint for Database url: tcp://db.example.com:5432 upstream: url: 5432 bindings: - kubernetes ``` Once you've saved the configuration, activate your endpoints by running: ```bash theme={null} ngrok start --all ``` ## 4. Test end-to-end connectivity from your local environment to upstream services From the machine where `ngrokd` is running, run: ```bash theme={null} ngrokctl list ``` You should see your private endpoints listed: ngrokctl list output showing Discovered Bound Endpoints with TCP and HTTP endpoints assigned local virtual addresses From here, you can curl your HTTP endpoint and verify you get the expected response. The same can be done for your TCP endpoint, for example connecting to a database: ```bash theme={null} psql -h db.example.com -p 5432 -U myuser mydb ``` ## What's next * [Install ngrok as a background service](/docs/gateway/site-to-site-connectivity/background-service) to ensure the agent starts on boot and recovers from failures. * [Eliminate single points of failure with redundant agents](/docs/gateway/site-to-site-connectivity/redundant-agents) to achieve high availability. # Site-to-Site Connectivity Overview Source: https://ngrok.com/docs/gateway/site-to-site-connectivity/overview Use ngrok to securely connect to APIs, databases, and services in remote private networks without a VPN. ngrok helps you securely access APIs, databases, and other private services running in remote networks through [site-to-site connectivity](https://ngrok.com/use-cases/saas-customer-connectivity). This is useful for securely connecting to your customers' on-prem environments, branch offices, data centers, and more, all without opening inbound ports or managing VPN infrastructure. You can run the ngrok agent inside remote networks, enabling you to enforce access control with IP restrictions, white-label connections with a custom connect URL, run redundant agents for high availability, and more. ## Why use ngrok for site-to-site connectivity? * **No inbound ports:** The ngrok agent connects outbound over TLS on port 443. No firewall changes or open ports required in the remote network. * **No VPN complexity:** Replace VPN tunnels, VPC peering, and custom networking with a single agent and a Cloud Endpoint. * **Centralized traffic control:** Apply IP restrictions, authentication, rate limiting, and more using Traffic Policy—all managed from a single dashboard. * **End-to-end encryption:** TLS termination at the agent or upstream service, including mTLS for zero-knowledge encryption. ## Next steps Get a basic site-to-site connection working in minutes Walk through a complete setup with multiple endpoints, mTLS, and IP restrictions Share this reference with customers who need to understand why ngrok is being installed in their network Achieve private site-to-site connectivity with the ngrok Kubernetes Operator # Site-to-Site Connectivity Quickstart Source: https://ngrok.com/docs/gateway/site-to-site-connectivity/quickstart Get a basic site-to-site connection working in minutes by forwarding traffic from a Cloud Endpoint to a service in a remote network. Running in K8s? Check out [this guide](/docs/gateway/site-to-site-connectivity/customer-networks) on securely connecting your local cluster to customer networks. This quickstart walks you through the fastest path to a working site-to-site connection using ngrok. You'll learn how to: * Configure an agent in a remote network * Create a Cloud Endpoint * Verify that traffic flows end-to-end ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/signup). * The [ngrok agent](/docs/gateway/endpoints/agent-cli-quickstart/) installed on a machine inside the remote network you want to connect to. * A service running in the remote network that you want to reach (this quickstart uses port `8080` as an example). ## 1. Configure an Internal Endpoint Open the ngrok configuration file on the remote machine and add an Internal Endpoint that points to the service you want to access. You can do so by editing your config file with the `ngrok config edit` terminal command. Update yours to resemble the following snippet. ```yaml title="ngrok.yml" highlight={6-10} theme={null} version: 3 agent: authtoken: YOUR_AUTHTOKEN endpoints: - name: my-service url: https://my-service.internal upstream: url: 8080 ``` Replace `YOUR_AUTHTOKEN` with the authtoken from your [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). Internal Endpoints are private endpoints that only receive traffic when a Cloud Endpoint forwards to them using the [forward-internal](/docs/gateway/traffic-policy/actions/forward-internal/) Traffic Policy action. Next, start the agent: ```bash theme={null} ngrok start --all ``` ```bash theme={null} ngrok start --config /path/to/ngrok.yml --all ``` Your service still can't be reached until you create a Cloud Endpoint to route traffic to it. ## 2. Create a Cloud Endpoint Cloud Endpoints are persistent, always-on endpoints managed from the dashboard or API. They use an attached Traffic Policy to handle incoming connections. [Create a new Cloud Endpoint](https://dashboard.ngrok.com/endpoints/new/cloud) in the dashboard. After it's created, you're taken to the Traffic Policy editor view. Replace the default Traffic Policy with the following: ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://my-service.internal ``` This routes all incoming traffic on the Cloud Endpoint to the Internal Endpoint you configured in step 1. ## 3. Test the connection Copy the URL of the Cloud Endpoint you just created from the dashboard, then send a request to verify traffic reaches your service: ```bash theme={null} curl https:// ``` You should see the response from the service running on port `8080` in the remote network. ## What's next * [Configure site-to-site connectivity](/docs/gateway/site-to-site-connectivity/) for a full walkthrough with multiple endpoints, mTLS, and IP restrictions. * [Set up a custom connect URL](/docs/gateway/site-to-site-connectivity/custom-connect-url) to white-label the agent connection for your customers. * [Install ngrok as a background service](/docs/gateway/site-to-site-connectivity/background-service) so the agent starts on boot and recovers from failures. * [Eliminate single points of failure](/docs/gateway/site-to-site-connectivity/redundant-agents) by running redundant agents for high availability. * Create [Service Users](/docs/iam/service-users/) to isolate and scope access per customer. # Configure Active-Active Agent Redundancy Source: https://ngrok.com/docs/gateway/site-to-site-connectivity/redundant-agents Use multiple ngrok agents so traffic switches if one goes down. Running a single agent in your customer's network is a single point of failure. If that agent or the hardware it runs on fails for any reason, you can no longer connect to the remote services in your customer's network. You can run multiple agents to achieve redundancy and high availability in the case of these failures. To do this, run a separate, second ngrok agent in the customer's network (ideally on a different machine) with an identical configuration file with one small tweak: on each endpoint definition you must specify `pooling_enabled: true`. This is ngrok's Endpoint Pooling feature. When two endpoints start with the same URL and have pooling enabled, they form an endpoint pool. Incoming traffic to the pooled URL is automatically distributed among all healthy endpoints in the pool. If one endpoint goes offline, traffic is seamlessly routed to the remaining endpoints, ensuring redundancy and failover. You'd configure your agent running on server 1 similar to the following: ```yaml theme={null} # Server 1 (agent1.yml) version: 3 agent: authtoken: endpoints: - name: my-service url: 'https://service.internal' pooling_enabled: true upstream: url: http://localhost:8080 ``` Then configure the agent on the first server similar to the following: ```yaml theme={null} # Server 2 (agent2.yml) version: 3 agent: authtoken: endpoints: - name: my-service url: 'https://service.internal' # Same URL as Server 1 pooling_enabled: true upstream: url: http://localhost:8080 ``` ## What's next * [Set up a custom connect URL](/docs/gateway/site-to-site-connectivity/custom-connect-url) to white-label the agent connection for your customers. # Secure Remote Access with ngrok for SSH and RDP Source: https://ngrok.com/docs/gateway/ssh-rdp/index Learn how to use ngrok to access remote devices via SSH or RDP. By creating secure TCP endpoints, ngrok enables easy, centralized, access for technicians, engineers, and IT admins to maintain and update remote devices and services. This guide will help you to streamline connectivity without compromising security when it comes to remote access using SSH or RDP. Examples where this is applicable include secure SSH into remote IoT devices or connecting to a Windows RDP server in a remote network. This guide demonstrates usability for a single remote network with an edge gateway per network (edge gateway 1). It can be scaled to many networks either with or without a central gateway. ## Architectural reference Architecture diagram for SSH and RDP remote access through ngrok ### Why only one ngrok Agent per remote network? Traditionally, you might assume that every service/device inside the network needs its own ngrok agent, but this isn't necessary. A single ngrok agent is installed on a network-accessible server inside the remote network, and it: 1. Acts as a central gateway that can reach any service on the local network, eliminating the need for multiple agents. 2. Creates Internal Endpoints so that each server/device is securely exposed inside ngrok, never publicly visible. 3. Uses Cloud Endpoints for controlled, granular access. External cloud apps can access only what they need. 4. Runs as a background service configured to automatically start on boot, restart after crashes, and log events. This setup minimizes security risks, simplifies deployment, and ensures continuous uptime for mission-critical connections within your remote devices and servers. ## What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * An ngrok agent, configured on your edge gateway or remote device/service. See the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/) for instructions on how to install the ngrok agent. * [An ngrok API Key](https://dashboard.ngrok.com/api-keys/new). You'll need an account first. ### 1. Create a Service User and authtoken to enable isolated Agent management Create a Service User so that you can create an agent authtoken independent of any user account. A Service User represents a service account, and allows each server to have its own authtoken. In the case one authtoken is compromised, only that agent may be affected rather than all of them. *** ```bash theme={null} curl \ -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"name":"Service User for Edge Gateway 1"}' \ https://api.ngrok.com/service_users ``` Create an authtoken assigned to this specific Service User. Apply an ACL rule to ensure this Service User can only create internal endpoints for edge gateway 1. ```bash theme={null} curl -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"description":"Authtoken for Gateway 1", "owner_id":"", "acl": ["bind:*.gateway1.internal"]}' \ "https://api.ngrok.com/tunnel_credentials" ``` ### 2. Define internal endpoints in ngrok.yml for privatized device access An [internal endpoint](/docs/gateway/endpoints/internal-endpoints/) enables a service inside the network to be reachable within ngrok, without being publicly exposed. They can: 1. Only receive traffic from Cloud Endpoints or internal services that explicitly route traffic to them. 2. Not be accessed directly from the internet. 3. Be used for telemetry APIs, databases, and dashboards. After installing the ngrok agent, define all required internal endpoints inside the ngrok configuration file. You can install ngrok and its configuration file in `/path/to/ngrok/ngrok.yml` and the executable in `/path/to/ngrok/ngrok`. ```yaml theme={null} version: 3 agent: authtoken: AUTHTOKEN_CREATED_IN_STEP_1 endpoints: - name: Internal Endpoint for Device 1 url: 'tcp://device1.gateway1.internal:443' upstream: url: 22 - name: Internal Endpoint for Device 2 url: 'tcp://device2.gateway1.internal:443' upstream: url: 22 - name: Internal Endpoint for Device N url: 'tcp://deviceN.gateway1.internal:443' upstream: url: 22 - name: Internal Endpoint for RDP Server url: 'tcp://rdp-server.gateway1.internal:443' upstream: url: 3389 ``` If there is no edge gateway present in your network, you must configure each agent on each device with an internal endpoint for that device. ### 3. Reserve a tcp address for each device/server Reserving a TCP address is required for creating a Cloud Endpoint which will be done in a later step. By reserving a TCP address, this address is held specifically for your ngrok account. You will do this for each device connected to an edge gateway as well as for the rdp server. *** ```bash theme={null} curl \ -X POST \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"description":"TCP Addr for Device 1","region":"us"}' \ https://api.ngrok.com/reserved_addrs ``` Repeat the above command for each device/server you need remote access to. ### 4. Create your TCP Cloud Endpoint and attach a Traffic Policy A [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints/) is a permanent, externally accessible entry point into the network that's also: 1. Managed centrally via the ngrok API or dashboard. 2. Always on, not tied to the lifecycle of the agent. Configuration can be modified at the cloud level via the dashboard/API (no need to change the config at the device itself) 3. Does not forward traffic to the agent by default—it must be configured to route traffic to internal endpoints. 4. Used for exposing services to external cloud apps securely. The curl command below leverages ngrok's platform API to create a Cloud Endpoint and attach a Traffic Policy to it. ngrok's [Traffic Policy](/docs/gateway/traffic-policy/) is the perfect way to forward and manipulate traffic in a flexible and robust manner. In this case, this command creates the endpoint and attaches a [forward-internal](/docs/gateway/traffic-policy/actions/forward-internal/) action which will forward traffic from the Cloud Endpoint to the internal endpoint. This will also need to be done for each device connected to the edge gateway where the agent sits. *** ```bash theme={null} curl \ - X POST \ - H "Authorization: Bearer " \ - H "Content-Type: application/json" \ - H "Ngrok-Version: 2" \ - d '{ "bindings": ["public"], "description": "TCP Cloud Endpoint for Device 1", "metadata": "{\"environment\\": \"staging\"}", "traffic_policy": "{\"on_tcp_connect\":[{\"actions\":[{\"type\":\"forward-internal\",\"config\":{\"url\":\"tcp://device1.gateway1.internal:443\"}}]}]}", "type": "cloud", "url": ""}' \ https://api.ngrok.com/endpoints ``` ### 5. Secure your Cloud Endpoint with IP restrictions Navigate to your newly created Cloud Endpoint in the [endpoints](https://dashboard.ngrok.com/authtokens) tab on your ngrok dashboard, and apply a restrict-ips Traffic Policy action to enable a source IP whitelist. By enabling IP restrictions, you can directly filter who/what can use the endpoint and prevent port scanners or other malicious actors. You can add this action directly to the Cloud Endpoint's YAML configuration. The properly formatted config for this action can be seen below: ```yaml theme={null} version: 3 agent: authtoken: AUTHTOKEN_CREATED_IN_STEP_1 endpoints: - name: Internal Endpoint for Device 1 url: 'tcp://device1.gateway1.internal:443' upstream: url: 22 - name: Internal Endpoint for Device 2 url: 'tcp://device2.gateway1.internal:443' upstream: url: 22 - name: Internal Endpoint for Device N url: 'tcp://deviceN.gateway1.internal:443' upstream: url: 22 - name: Internal Endpoint for RDP Server url: 'tcp://rdp-server.gateway1.internal:443' upstream: url: 3389 ``` You can also create the TCP endpoint and attach its Traffic Policy with the forward-internal and restrict-ips actions all in one API call: ```bash theme={null} curl -X POST https://api.ngrok.com/endpoints \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{ "type": "cloud", "url": "RESERVED_TCP_ADDR_FROM_STEP_3", "traffic_policy": "{ \"on_tcp_connect\": [ { \"actions\": [ { \"type\": \"restrict-ips\", \"config\": { \"enforce\": true, \"allow\": [ \"e680:5791:be4c:5739:d959:7b94:6d54:d4b4/128\", \"203.0.113.42/32\" ] } }, { \"type\": \"forward-internal\", \"config\": { \"url\": \"tcp://device1.gateway1.internal:443\" } } ] } ] }" }' ``` ### 6. Use your ngrok TCP endpoints with your SSH/RDP clients Now that you have created and secured your tcp Cloud Endpoints and they forward to the correct upstream devices, you can use these TCP endpoints in your existing SSH/RDP client setups to test your remote connectivity. ## Additional ngrok features ### Enable Endpoint Pooling You can use Endpoint Pooling with multiple internal Agent Endpoints to achieve redundancy and high availability for services inside your network. If desired, you can install a second agent within the remote network as a failover in case agents in the wild go offline. Configure each Agent Endpoint to use the same ngrok internal URL. This automatically forms an endpoint pool. Incoming traffic to the pooled URL is automatically distributed among all healthy endpoints in the pool. If one endpoint goes offline, traffic is seamlessly routed to the remaining endpoints, ensuring redundancy and failover ```yaml theme={null} version: 3 agent: authtoken: endpoints: - name: service-a url: 'https://service.internal' pooling_enabled: true upstream: url: 8080 - name: service-b url: 'https://service.internal' pooling_enabled: true upstream: url: 8081 ``` ### Create a custom connect URL This provides a white-labeling capability so that your ngrok agents will connect to *connect.example.com* instead of the default connection hostname (connect.ngrok-agent.com). Dedicated IPs that are unique for your account which your agents will connect to are also available. This takes away any danger of rogue agents in your network trying to call home and adds an additional layer of security by specializing your ngrok connectivity. Custom connect URLs are available with ngrok's pay-as-you-go plan as an additional paid feature. ```bash theme={null} curl \ -X POST \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"description":"Custom connect URL","domain":"connect.example.com"}' \ https://api.ngrok.com/agent_ingresses ``` Once you have created the custom connect url, specify this field within the agent configuration file. Add this section to your agent configuration file to specify the custom connect url: ```yaml theme={null} version: 3 agent: connect_url: connect.example.com:443 ``` ### Install ngrok as a background service Install and start the service: ```bash theme={null} ngrok service install --config /etc/ngrok.yml ngrok service start ``` In most cases, installing ngrok as a service requires administrator privileges. This will start all tunnels defined in the configuration file, ensure ngrok runs persistently in the background, and integrate with native OS service tooling. ## Recap You have now integrated a system that allows you to seamlessly and securely access any and all devices/servers within your remote network. To recap what you've built: 1. One ngrok agent per remote network and no need for multiple installs. 2. Always-online devices and servers, securely available via cloud and internal endpoints. 3. Granular access with a composable Traffic Policy offering refined and robust security measures for endpoints. 4. Not sure how to explain ngrok to your end users? Check out [this guide which details ngrok's standards on security, trust, compliance, and privacy](/docs/gateway/site-to-site-connectivity/). # Add Headers Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/add-headers Add headers to an HTTP request or response before it is delivered upstream or back to the client. The Add Headers action enables you to add custom headers to an HTTP request or response before delivery to the upstream service or client. This is useful for attaching metadata like request identifiers, applying security controls, or adjusting how applications process traffic. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request`, `on_http_response` ### Type `add-headers` ### Configuration fields Map of header key to header value to be added. Minimum `1`, Maximum `10`. ## Behavior When configured in the `on_http_request` phase, this action will add the specified headers to incoming http requests before reaching the upstream server. When configured in the `on_http_response` phase, the specified headers are added to the http response from the upstream server. ### Addition only This action will only add headers to the request or response. If the header already exists it will append another header with the same key unless it is the `host` header, see [Special Cases](#special-cases). To replace or remove headers use the [`remove-headers`](/docs/gateway/traffic-policy/actions/remove-headers) action then add the header with the desired value. ### Case sensitivity Header keys added through this action are normalized to lowercase per the [HTTP/2 specification](https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2). ### Ordering Since actions are run in the order they are specified, to modify headers that have been added by other actions you must place this action after them in your Traffic Policy document. ### Special cases * Adding the `host` header override the existing value instead of appending another header. * You may not add or remove the `user-agent` header. * You may not use this action to add the `ngrok-skip-browser-warning` header to skip the ngrok browser warning on free accounts. For more information, check out the [free plan limits guide](/docs/pricing-limits/free-plan-limits#removing-the-interstitial-page). ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Adding client IP headers to all HTTP requests The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration will add the client IP address to all HTTP requests. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: "add-headers" config: headers: x-client-ip: "${conn.client_ip}" ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "add-headers", "config": { "headers": { "x-client-ip": "${conn.client_ip}" } } } ] } ] } ``` For this example, assume that ngrok is pointing at the upstream service [https://httpbin.org](https://httpbin.org) and the following header is added to all requests: * `x-client-ip` with the value `${conn.client_ip}` to demonstrate the use of CEL interpolation to include the client IP address. #### Example request ```bash theme={null} $ curl -i https://httpbin.ngrok.app/get ``` ```http theme={null} HTTP/2 200 OK content-type: application/json { "headers": { "X-Client-Ip": "2600:1700:4fa6:1a00:2051:938:7373:5563", } } ``` ### Adding headers to an HTTP response The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration will add headers to the response from the upstream service when the method is `GET` and the URL starts with `/status/200`. #### Example Traffic Policy document ```yaml policy.yml highlight={3} theme={null} on_http_response: - expressions: - "req.method == \"GET\" && req.url.path.startsWith(\"/status/200\")" actions: - type: "add-headers" config: headers: x-custom-header: "my-custom-value" x-string-interpolation-example: "started at ${conn.ts.start}" ``` ```json policy.json highlight={5} theme={null} { "on_http_response": [ { "expressions": [ "req.method == \"GET\" && req.url.path.startsWith(\"/status/200\")" ], "actions": [ { "type": "add-headers", "config": { "headers": { "x-custom-header": "my-custom-value", "x-string-interpolation-example": "started at ${conn.ts.start}" } } } ] } ] } ``` For this example, assume that ngrok is pointing at the upstream service [https://httpbin.org](https://httpbin.org) and two headers are added: * `x-custom-header` with the value `my-custom-value` * `x-string-interpolation-example` with the value `started at ${conn.ts.start}` to demonstrate the use of CEL interpolation to include the request connection start time. ### Example request ```bash theme={null} $ curl -i https://httpbin.ngrok.app/status/200 ``` ```http theme={null} HTTP/2 200 OK x-custom-header: my-custom-value x-string-interpolation-example: started at 2024-07-13T00:10:16Z ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated.

Map of headers that were added by the action.

# Basic Auth Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/basic-auth Enforce HTTP Basic Auth on your endpoints. The Basic Auth action enables you to enforce HTTP Basic Auth on your endpoints. Requests with a valid username and password will be sent to your upstream service, all others will be rejected with a `401 Unauthorized` response. ## Configuration reference This is the [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request` ### Type `basic-auth` ### Configuration fields

A list of up to 10 allowed username:password credential pairs.

Password must be at least 8 characters and no more than 128 characters.

The HTTP realm of the request as per RFC 7235.

If false, continue to the next action even if basic authentication failed. This is useful for handling fall-through, debugging, and testing purposes. ## Behavior The **basic-auth** action enforces HTTP Basic Authentication on incoming requests, as specified in [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235). When a request is received, the action verifies the request by validating against a known set of `user:password` credentials. If the verification is successful, the action allows the request to continue through the action chain and finally to your application; if verification fails, the request will be terminated with a `401 Unauthorized` response. ### Additional restrictions You can specify up to 10 `username:password` pairs. The password must be at least 8 characters and no more than 128 characters. Including multiple colons in the `username:password` pair will result in the first colon being treated as the delimiter between the username and password. Realm cannot exceed 128 characters, and cannot contain non-ASCII characters. ### Verification process * **HTTP Authentication**: The action validates incoming HTTP requests to confirm they contain the required `Authorization` header, in the form of `Authorization: Basic `, where `` is the Base64 encoding of username and password joined by a single colon `:`. * **Request Handling**: If the authentication is successful, the request is forwarded to the next action. If the authentication fails, it will return to user a `401` response, which will prompt the user to provide a correct set of credentials. * **Configurable Enforcement**: By default, authentication failures result in 401s. However, setting `enforce: false` allows unauthenticated requests to proceed, while logging the authentication result. This option is for debugging and testing. ### Secret handling and encryption All secrets used for basic authentication are encrypted at config validation. When ngrok processes a request, the secret is decrypted. ### Using CEL in credentials You can use CEL interpolation in credentials, but the expression must stay properly scoped. A CEL expression can: * produce the entire credential, * produce the whole username or password, or * produce part of the username or password. If the expression does not produce the entire credential, you must include the `:` separator literally, and the expression itself cannot introduce another colon. #### Valid patterns * `${CEL_EXPRESSION}` - Entire credential is dynamic * `${CEL_EXPRESSION}:password` - Dynamic username, literal password * `${CEL_EXPRESSION}:${CEL_EXPRESSION}` - Both username and password are dynamic * `user-prefix${CEL_EXPRESSION}:password` - Part of the username is dynamic, literal password #### Invalid patterns * `${CEL_EXPRESSION}password` - Missing colon separator between username and password * `user-prefix${CEL_EXPRESSION}password-suffix` - Missing colon separator between username and password ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Basic example The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration is an example configuration of the `basic-auth` action. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: "basic-auth" config: realm: "sample-realm" credentials: - "user:password1" - "admin:password2" enforce: true - type: "custom-response" config: status_code: 200 headers: content-type: "text/plain" body: "Successfully Authenticated!" ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "basic-auth", "config": { "realm": "sample-realm", "credentials": [ "user:password1", "admin:password2" ], "enforce": true } }, { "type": "custom-response", "config": { "status_code": 200, "headers": { "content-type": "text/plain" }, "body": "Successfully Authenticated!" } } ] } ] } ``` ### Example request First, base64 encode the `username:password` pair. `user:password1` becomes `dXNlcjpwYXNzd29yZDE=` ```bash theme={null} $ curl --request GET \ --url http://example-api.ngrok.app/ \ --header 'Authorization: Basic dXNlcjpwYXNzd29yZDE=`' ``` ```http theme={null} HTTP/2 200 OK content-type: text/html Successfully Authenticated! ... ``` In this example, a request is sent to the API with a valid set of credentials in the `Authorization` header with the `Basic` prefix and getting back a `200 OK` response. You can try it without the header, and you will receive a `401 Unauthorized` prompt instead. ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated.

Whether there were Basic Auth credentials presented in the Authorization header.

The username of the credentials presented.

Whether the presented basic auth credentials were authorized for this request.

# Circuit Breaker Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/circuit-breaker Prevent overload and maintain system reliability by rejecting requests when errors and volume exceed set thresholds. The Circuit Breaker Traffic Policy action helps you maintain system reliability by rejecting requests when the error rate and request volume within a rolling window exceed defined thresholds. It pauses traffic temporarily to allow the system to recover and automatically re-evaluates upstream health before resuming. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request` ### Type `circuit-breaker` ### Configuration fields Threshold percentage of errors that must be met before requests are rejected. Must be a value between `0.0` and `1.0`. Number of requests in a rolling window before checking the error threshold. Must be a number between `1` and `2,000,000,000`. Number of seconds in the rolling window that metrics are retained for. Must be a value between `1s` and `2m`. Number of seconds the system waits after rejecting a request before re-evaluating upstream health. Must be a value between `1s` and `2m`. Number of buckets that metrics are divided into within the rolling window. Fixed at `10`. Determines if the circuit breaker is active. If `false`, the circuit breaker never trips, and no requests are rejected. ## Behavior ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Basic example This example configuration sets up an endpoint (`hotdog.ngrok.app`) that allows only 1 request every 60 seconds and trips the circuit breaker for 2 minutes. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: "circuit-breaker" config: error_threshold: 0 volume_threshold: 1 window_duration: "60s" tripped_duration: "2m" enforce: true ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "circuit-breaker", "config": { "error_threshold": 0, "volume_threshold": 1, "window_duration": "60s", "tripped_duration": "2m", "enforce": true } } ] } ] } ``` #### Start endpoint with Traffic Policy ```bash theme={null} ngrok http 8080 --url hotdog.ngrok.app --traffic-policy-file /path/to/policy.yml ``` #### Helper script ```python circuit_breaker.py theme={null} import requests import time url = "https://hotdog.ngrok.app" attempt = 1 while True: try: response = requests.get(url) # Log the response if response.status_code == 200: print(f"Attempt {attempt}: Success ({response.status_code})") else: print(f"Attempt {attempt}: Failure ({response.status_code})") # Stop when circuit breaker trips if response.status_code == 503: print("\nCircuit breaker tripped!") break except requests.exceptions.RequestException as e: print(f"Attempt {attempt}: Error ({e})") break attempt += 1 time.sleep(0.01) # Reduce delay for faster request rate ``` ```bash theme={null} ➜ ~ python3 circuit_breaker.py Attempt 1: Success (200) Attempt 2: Failure (503) Circuit breaker tripped! ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. This action does not expose any result variables. # Close Connection Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/close-connection Forcibly close connections to an endpoint. The Close Connection Traffic Policy action lets you forcibly close connections to an endpoint when certain conditions are met, for example, when the number of requests exceeds a given limit (to mitigate DDoS attacks). This action can be used with other actions such as [rate-limit](/docs/gateway/traffic-policy/actions/rate-limit) and [deny](/docs/gateway/traffic-policy/actions/deny). ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request`, `on_tcp_connect` ### Type `close-connection` ### Configuration fields This action does not have any configuration fields. ## Behavior When this action is executed, connection to the endpoint is closed immediately. This can be useful for dealing with DDoS attacks. ### Terminating action This is a **terminating action**. It will return a response and cause Traffic Policy processing to end without executing any further Actions. ## Examples ### Basic example This basic example configuration will guide you through setting up an endpoint on ngrok (`hotdog.ngrok.app`) and closing the connection to the endpoint. ```yaml policy.yml theme={null} on_http_request: - name: "Immediately close connection" expressions: - req.url.path.startsWith("/dc") actions: - type: close-connection ``` ```bash theme={null} ngrok http 8080 --url hotdog.ngrok.app --traffic-policy-file ./policy.yml ``` ```bash theme={null} curl http://hotdog.ngrok.app/dc ``` You should see something similar to the following: ```bash theme={null} curl: (16) Error in the HTTP2 framing layer ``` This is expected as the connection is closed immediately. ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. This action does not set any variables after it has been executed. # Compress Response Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/compress-response Compress HTTP response bodies before they are returned to the client. The Compress Response Traffic Policy action enables you to improve the performance of your web applications by compressing HTTP response bodies returned by your upstream service. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_response` ### Type `compress-response` ### Configuration fields A list of allowed compression algorithms to be considered during encoding type negotiation. Each element must be unique. `br` `compress` `deflate` `gzip` ## Behavior When an HTTP request includes an `Accept-Encoding` header, HTTP responses are automatically compressed, and a Content-Encoding header is added to the response. If the response is already compressed by your upstream service, no additional compression will be applied. ### Streaming compression When compression is applied, the response body is not buffered; it is compressed in real-time as it streams through the ngrok endpoint. ### Algorithm choice If a request specifies `Accept-Encoding` but none of the requested algorithms are supported, no compression is applied, and the upstream response is returned unmodified. ### Quality values Compression is negotiated based on the `Accept-Encoding` header as defined by the [RFC](https://datatracker.ietf.org/doc/html/rfc9110#name-accept-encoding), respecting q-values and selecting the supported algorithm with the highest q-value. For example: ```bash theme={null} Accept-Encoding: gzip;q=1.0, br; q=0.5, *;q=0 ``` The algorithm `gzip` would be selected because it has the highest q-value of `1.0`. ### Response headers When this action performs compression, the following changes are made to the HTTP response headers: * The `Content-Length` header is removed. * A `Content-Encoding` header is added, specifying the negotiated algorithm. * A `Vary: Accept-Encoding` header is added to indicate that the response varies based on the `Accept-Encoding` request header. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Compressing API responses based on URL path The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to set this up using the `compress-response` action with an expression to compress traffic on specific URL paths. #### Example Traffic Policy document ```yaml policy.yml highlight={3} theme={null} on_http_response: - expressions: - "req.url.path.startsWith('/api/')" actions: - type: "compress-response" config: algorithms: - "gzip" - "br" - "deflate" - "compress" ``` ```json policy.json highlight={5} theme={null} { "on_http_response": [ { "expressions": [ "req.url.path.startsWith('/api/')" ], "actions": [ { "type": "compress-response", "config": { "algorithms": [ "gzip", "br", "deflate", "compress" ] } } ] } ] } ``` For this example, assume that an ngrok agent is pointing at the upstream service [https://jsonplaceholder.typicode.com](https://jsonplaceholder.typicode.com). #### Example request ```bash theme={null} $ curl -i https://jsonplaceholder.ngrok.app/api/posts ``` ```http theme={null} HTTP/2 200 OK content-encoding: gzip content-type: application/json; charset=utf-8 ``` In this example, when a request is made to `/api/posts`, ngrok compresses the response using one of the specified algorithms (for example, `gzip`). The response includes the `content-encoding: gzip` header, and the body is compressed accordingly. This setup ensures that only API responses for paths starting with `/api/` are compressed, enhancing performance and responsiveness for only those endpoints. ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. Indicates whether the body was already compressed before the action was applied. Returns true if no further compression was performed. The compression algorithm selected and applied by the action, based on the client's request and action configuration. # Custom Response Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/custom-response Return a hard-coded response back to the client that made a request to your endpoint. The **Custom Response** Traffic Policy action enables you to return a hard-coded response back to the client that made a request to your endpoint. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request`, `on_http_response` ### Type `custom-response` ### Configuration fields

Status code of the custom response being sent back.

Body of the custom response being sent back.

Map of key-value headers of the custom response to be sent back. If `content-type` is not included in `headers`, this action will attempt to infer the correct `content-type`. Maximum properties `10`.

## Behavior If this action is executed, no subsequent actions in your Traffic Policy will be executed. ### `on_http_request` usage When used during the `on_http_request` phase, this action bypasses the upstream server and immediately returns the configured response to the caller. Keep in mind that because this action bypasses the upstream server, request bodies will not be forwarded and will not be visible within the ngrok [Traffic Inspector](/docs/obs/traffic-inspection/). When this policy is executed in the `on_http_request` phase, actions defined in the `on_http_response` phase will still be executed. ### `on_http_response` usage When used during the `on_http_response`, this action overwrites the response from the upstream server with the configured response. ### Inferring content-type If the `content-type` header is not explicitly specified in `headers`, this action will attempt to infer the correct `content-type` based on the provided content. ### Terminating action This is a **terminating action**. It will return a response and cause Traffic Policy processing to end without executing any further Actions. If this action is executed in the [on\_http\_request phase](#on_http_request-usage), the Traffic Policy will not terminate, but will continue to the [on\_http\_response phase](#on_http_response-usage), where all actions in that change will be executed normally. ## Examples ### Custom HTML maintenance page The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `custom-response` action to return a custom HTML maintenance page back for all requests to your endpoint. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: custom-response config: status_code: 503 body:

Service Unavailable

Our servers are currently down for maintenance. Please check back later.

headers: content-type: text/html ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "custom-response", "config": { "status_code": 503, "body": "

Service Unavailable

Our servers are currently down for maintenance. Please check back later.

", "headers": { "content-type": "text/html" } } } ] } ] } ```
#### Example request ```bash theme={null} $ curl -i https://example.ngrok.app/dashboard ``` ```http theme={null} HTTP/2 503 content-type: text/html

Service Unavailable

Our servers are currently down for maintenance. Please check back later.

``` In this example, when a request is made to any page on your endpoint, ngrok returns back the custom HTML maintenance page. This setup is useful for when you want to temporarily disable your endpoint. ### Custom response for internal endpoint downtime The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `custom-response` action to redirect to a URL if an internal endpoint is returning an error. #### Example Traffic Policy document ```yaml policy.yml {3-6} theme={null} on_http_request: - actions: - type: forward-internal config: url: https://endpoint-1.internal on_error: continue - type: custom-response config: status_code: 302 headers: location: https://www.example.com ``` ```json policy.json {5-11} theme={null} { "on_http_request": [ { "actions": [ { "type": "forward-internal", "config": { "url": "https://endpoint-1.internal", "on_error": "continue" } }, { "type": "custom-response", "config": { "status_code": 302, "headers": { "location": "https://www.example.com" } } } ] } ] } ``` ### Custom JSON API response with CEL interpolation The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `custom-response` action to return a JSON response with CEL Interpolation for the connection start time. #### Example Traffic Policy document ```yaml policy.yml {7} theme={null} on_http_request: - expressions: - req.url.path == '/api/example' actions: - type: custom-response config: body: '{"connection-start":"${conn.ts.start}"}' headers: content-type: application/json ``` ```json policy.json {11} theme={null} { "on_http_request": [ { "expressions": [ "req.url.path == '/api/example'" ], "actions": [ { "type": "custom-response", "config": { "body": "{\"connection-start\":\"${conn.ts.start}\"}", "headers": { "content-type": "application/json" } } } ] } ] } ``` #### Example request ```bash theme={null} $ curl https://example.ngrok.app/api/example ``` ```http theme={null} HTTP/2 200 OK content-type: application/json { "connection-start": "2024-06-24T15:30:00Z" } ``` In this example, when a request is made to `/api/example`, ngrok returns a custom JSON response with the default status code of `200`. The response includes a `content-type: application/json` header and a JSON body that uses CEL Interpolation to show the connection start time using the [`conn.ts.start` variable](/docs/gateway/traffic-policy/variables/connection/#conntsstart). ### Custom plaintext response with multiple CEL interpolations The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `custom-response` action to return a `text/plain` response with multiple CEL interpolations. #### Example Traffic Policy document ```yaml policy.yml {8} theme={null} on_http_request: - expressions: - req.url.path == '/api/example' actions: - type: custom-response config: status_code: 418 body: connection began at ${conn.ts.start}, now ${timestamp(time.now)} headers: content-type: text/plain ``` ```json policy.json {12} theme={null} { "on_http_request": [ { "expressions": [ "req.url.path == '/api/example'" ], "actions": [ { "type": "custom-response", "config": { "status_code": 418, "body": "connection began at ${conn.ts.start}, now ${timestamp(time.now)}", "headers": { "content-type": "text/plain" } } } ] } ] } ``` #### Example request ```bash theme={null} $ curl https://example.ngrok.app/api/example ``` ```http theme={null} HTTP/2 418 I'm a teapot content-type: text/plain connection began at 2024-06-24T15:30:00Z, now 2024-06-24T16:30:00Z ``` In this example, when a request is made to `/api/example`, ngrok returns a custom plain text response with a status code of `418`. The response includes a `content-type: text/plain` header and a body that uses multiple string interpolations to show the connection start time and the current time. ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. This action does not set any variables after it has been executed. # Deny Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/deny The Deny action enables you to reject incoming requests on your endpoints before they make it to your upstream service. The **Deny** Traffic Policy action enables you to reject incoming requests on your endpoints before they make it to your upstream service. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_tcp_connect`, `on_http_request` ### Type `deny` ### Configuration fields This action does not have any configuration fields for TLS / TCP endpoints. The response status code to return back to the client when the request is denied. ## Behavior When this action is executed, the upstream server is never reached and a response is immediately returned and no further actions or rules in the policy configuration will be executed. When this action is executed, the upstream server is never reached and a response is immediately returned and no further actions or rules in the policy configuration will be executed. ### Custom content The deny Traffic Policy action will not send back a response body to the client. To send back a response body, use the [`custom-response`](/docs/gateway/traffic-policy/actions/custom-response) action. ### Terminating action This is a **terminating action**. It will return a response and cause Traffic Policy processing to end without executing any further Actions. ## Examples ### Deny all traffic The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration will deny all inbound traffic on your endpoint. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_tcp_connect: - actions: - type: deny ``` ```json policy.json theme={null} { "on_tcp_connect": [ { "actions": [ { "type": "deny" } ] } ] } ``` #### Example request ```bash theme={null} $ telnet 5.tcp.ngrok.io 22984 Trying... Connected to 5.tcp.ngrok.io. Connection closed by foreign host. ``` In this example, a connection attempt to `5.tcp.ngrok.io:22984` using the `telnet` command and ngrok immediately closes the connection. ### Deny traffic with a 404 status code The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration will deny all inbound traffic with a `404` on your endpoint. #### Example Traffic Policy document ```yaml policy.yml {3-10} theme={null} on_http_request: - actions: - type: deny config: status_code: 404 ``` ```json policy.json {5-10} theme={null} { "on_http_request": [ { "actions": [ { "type": "deny", "config": { "status_code": 404 } } ] } ] } ``` #### Example request ```curl theme={null} $ curl https://example.ngrok.app ``` ```http theme={null} HTTP/2 404 Not Found ``` In this example, a connection attempt to `example.ngrok.app` using the `curl` command and ngrok immediately closes the connection with a `404` status code. ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. This action does not set any variables after it has been executed. # Forward Internal Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/forward-internal The Forward Internal action enables you to forward traffic from an endpoint to a internal endpoint within the same ngrok account. The **Forward Internal** Traffic Policy action enables you to forward traffic from an endpoint to a internal endpoint within the same ngrok account. This is useful for safely and securely routing traffic from your public endpoints to other services, giving you the ability to choose when and how your endpoints are made publicly available. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_tcp_connect`, `on_http_request` ### Type `forward-internal` ### Configuration fields

The endpoint to forward to, such as [http://my-internal-endpoint.internal:1234](http://my-internal-endpoint.internal:1234).

Whether or not further actions in the Traffic Policy should run if there is an error.

`halt` (default) `continue`
## Behavior This action forwards a request to an internal endpoint. Any Traffic Policy associated with the internal endpoint will also be applied to the request when it's forwarded. If the forwarding is successful, the response from the upstream for the internal endpoint will be sent back to the client making the original request. No further actions in the `inbound` phase will be executed and no traffic will be sent to the upstream for the public endpoint. If the forwarding is unsuccessful because the specified endpoint doesn't exist, is offline, or encounters another error, the action will return an error and follow the behavior that is specified by `on_error` (see [Managing Fallback Behavior](#on-error)). Even if you do not plan to send traffic to a local service when creating a forwarding endpoint, you will still need to specify a local port. This port will receive traffic if an expression causes only a subset of traffic to be forwarded or if there is an error forwarding traffic and `on_error` is set to `continue` without a subsequent terminating action. ### HTTP headers When forwarding HTTP requests to another endpoint, the `Host` header will be set to the hostname of the forwarding endpoint. For example, if `https://example.ngrok.app` is forwarding HTTP requests to `https://example.internal`, the `Host` header received by the upstream will be `example.ngrok.app`. The action will also set the `X-Forwarded-For`, `X-Forwarded-Host`, and `X-Forwarded-Proto` headers when making the upstream request. See [Upstream Headers](/docs/gateway/endpoints/http#upstream-headers) for more information. ### Managing fallback behavior (`on_error`) If `on_error` is set to `halt` (default) and the action encounters an error when forwarding traffic, the Traffic Policy chain will halt and no further actions will be executed. For example, if you have a `log` action after the `forward-internal` action and the `url` specified isn't an online endpoint, the `log` action will not be run and the error will be returned. However, if `on_error` is set to `continue`, actions that appear after the `forward-internal` action will still be executed even if the `forward-internal` action encounters an error. This can be used as a fallback to forward traffic to one of many endpoints depending which are online. ### Valid forward targets A request may only be forwarded to a internal endpoint on the same account as this endpoint. The target must be of the same protocol (that is, an HTTP Endpoint may only forward to an HTTP Internal Endpoint). In addition, the target's Traffic Policy may only specify policy for the current protocol, for example if `forward-internal` is used in the `on_http_request` phase, the internal endpoint may only have `on_http_request` and `on_http_response` sections. ### Terminating action This is a **terminating action**. It will return a response and cause Traffic Policy processing to end without executing any further Actions. This action is *not* terminating if `on_error` is set to `continue`.[Learn more](#managing-fallback-behavior-on-error). ## Examples ### Basic example This example configuration will set up a public endpoint (`forward-internal-example.ngrok.app`) forwarding all traffic it receives to a internal endpoint (`example.internal`) that forwards the request to port `80` on your local machine. Since it is forwarding all traffic to the internal endpoint, no traffic will be sent to `8080` which is the upstream port for the public endpoint. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://example.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "forward-internal", "config": { "url": "https://example.internal" } } ] } ] } ``` #### Start an internal endpoint ```bash theme={null} ngrok http 80 --url example.internal ``` #### Start public endpoint with Traffic Policy ```bash theme={null} ngrok http 8080 --url forward-internal-example.ngrok.app --traffic-policy-file /path/to/policy.yml ``` ### Example request ```bash theme={null} $ curl https://forward-internal-example.ngrok.app -v ... > GET / HTTP/2 > Host: forward-internal-example.ngrok.app > User-Agent: curl/[version] > Accept: */* ... ``` This request will be forwarded to the internal endpoint `https://example.internal` which will then forward the request to port `80` on your local machine. ```http theme={null} GET / HTTP/1.1 Host: forward-internal-example.ngrok.app User-Agent: curl/[version] Accept: */* X-Forwarded-For: [ngrok IP] X-Forwarded-Host: forward-internal-example.ngrok.app X-Forwarded-Proto: https Accept-Encoding: gzip ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. These variables are only populated when `on_error` is set to `continue` and the forward fails. The resolved target URL (`scheme://host:port`) that the action attempted to forward to. A machine-readable `ERR_NGROK_*` code describing the error that caused the forward to fail. A human-readable message providing details about the error that caused the forward to fail. # HTTP Request Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/http-request The HTTP Request action performs an outbound HTTP request to a target URL. The HTTP Request Traffic Policy action performs an outbound HTTP request to a target URL. You can use it to integrate with external services through internal endpoints or—if enabled—with external APIs. External requests are supported but disabled by default. [Contact ngrok support](https://ngrok.com/support) to enable external access. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request`, `on_http_response` ### Type `http-request` ### Configuration fields The destination URL for the HTTP request. The HTTP method to use for the request. `GET` (default) `PUT` `POST` `PATCH` `DELETE` `OPTIONS` A list of query parameters to append to the URL. Each item is an object with the following structure: ```yaml theme={null} - key: "parameter_name" value: "parameter_value" ``` Maximum: `32` entries. Key max length: `128` chars. Value max length: `8192` chars. A map of HTTP headers to include in the request. Keys are header names and values are header values. Maximum: `10` entries. The body of the HTTP request. Supported on methods like `POST`, `PUT`, or `PATCH`. The maximum number of HTTP redirects to follow. The minimum allowed is `0`. The maximum allowed is `100`. The maximum duration as a duration string to wait for the entire request (including retries and redirects). The minimum allowed is `1s`. The maximum allowed is `30s`. A CEL expression evaluated after each failed attempt. If `true`, the request is retried (up to `3` times). `attempts` (`int`): Total number of attempts so far `last_attempt.req`: The last request object `last_attempt.res`: The last response object (if any) `last_attempt.error`: The error string (if any) Determines how to proceed if the HTTP request fails. `continue` (default) – Proceed with remaining actions `halt` – Stop processing the policy ## Behavior The `http-request` action issues an outbound HTTP request during either the `on_http_request` or `on_http_response` phase of policy execution. It can include dynamic headers, query parameters, and a request body. Only specific HTTP methods are supported: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, and `OPTIONS`. If no method is specified, `GET` is used by default. ### Internal endpoint behavior By default, the `http-request` action only supports requests to internal ngrok endpoints. Requests to internal endpoints (those with `.internal` domains) are treated differently from public (or external) requests, these requests must resolve to an internal ngrok endpoint running on your account. These requests also use a direct connection over ngrok's control plane and do not rely on the public internet. If the request targets an internal Agent Endpoint (for example, your self-hosted ngrok agent), it will exit the control plane and traverse the public internet to reach your agent running locally. ### Retry logic You can use a condition to automatically retry failed requests. This is useful for handling transient errors, like a `500` response. The condition is written using an expression language and has access to: * `attempts`: number of attempts so far * `last_attempt.req`: the most recent request * `last_attempt.res`: the most recent response * `last_attempt.error`: any error that occurred ```js theme={null} last_attempt.res.statusCode == 500 ``` The request will retry up to **3 times**. ### Timeout behavior The `timeout` setting defines the **maximum total time** allowed for the entire `http-request` action, including **all** retry attempts. This prevents long-running or stalled requests from delaying policy evaluation. The default timeout is `3s`. You can configure any duration between **1 second** and **30 seconds** using standard duration formats like `5s` or `10s`. #### Timeout error handling Whether a timeout causes the policy to fail or continue depends on the `on_error` setting: * `halt` will treat the timeout as a hard failure. * `continue` will move forward even if the request timed out. ### Loop protection To prevent endless loops between services, ngrok tracks internal hops. If the same request loops more than 3 times internally, it will be stopped automatically. ### Redirect behavior By default, the `http-request` action does not follow HTTP redirects. You can enable redirect handling by setting the `max_redirects` field. The allowed range is `0` to `5`. By default it is set to `0`. If the number of redirects exceeds `max_redirects`, the action fails. Redirect handling only applies to `3xx` responses from the target server. ### Response body size limits The response body returned by the `http-request` action is limited to **256 KB**. If the body size exceeds this limit, the action fails and returns a `response size exceeded` error. This limit applies to the decoded body after any decompression and before retries are evaluated. Each retry is also subject to the **256 KB** response size limit. If the response body exceeds this limit, the attempt fails and the error is included in the `retry_condition` evaluation. ### Error handling Set `on_error` to control what happens if the request fails: * `continue`: Policy continues even if the request fails. * `halt`: Policy stops immediately on failure. This gives you control over how critical the request is to your policy logic. ### Automatically injected headers ngrok automatically injects some headers into your request to help with debugging, tracing, and abuse: | Header | Purpose | | - | - | | `Ngrok-Report-Abuse` | Static URL for reporting abuse | | `Ngrok-Req-Type` | Always set to `http-request` | | `Ngrok-Req-Id` | Unique request identifier | | `X-Forwarded-For` | Original client IP address | | `User-Agent` | Identifies the request as from `ngrok/cloud` | ### CEL interpolation Certain fields in the `http-request` action support CEL (Common Expression Language) interpolation, allowing dynamic values based on the request context. The following fields support CEL expressions: * `url` * `headers` (values only) * `query_params` (values only) * `body` Expressions must be wrapped in `${...}` and are evaluated at runtime using the current request data. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Make a GET request Performs a simple GET request to an internal endpoint. ```yaml policy.yml theme={null} on_http_request: - name: BasicInternalRequest actions: - type: http-request config: url: https://upstream-service.internal/ping ``` ```json policy.json theme={null} { "on_http_request": [ { "name": "BasicInternalRequest", "actions": [ { "type": "http-request", "config": { "url": "https://upstream-service.internal/ping" } } ] } ] } ``` #### Send query parameters This example sends a GET request with custom query parameters to an internal endpoint. To simulate this behavior, use [httpbin.org/get](https://httpbin.org/get), which echoes query parameters in the response. ##### Start an internal Agent endpoint ```bash theme={null} ngrok http https://httpbin.org --host-header rewrite --url https://httpbin.internal ``` ```yaml policy.yml {7-11} theme={null} on_http_request: - name: QueryParamsExample actions: - type: http-request config: url: https://httpbin.internal/get query_params: - key: trace value: 'true' - key: user value: req.headers["x-user-id"] ``` ```json policy.json {10-19} theme={null} { "on_http_request": [ { "name": "QueryParamsExample", "actions": [ { "type": "http-request", "config": { "url": "https://httpbin.internal/get", "query_params": [ { "key": "trace", "value": "true" }, { "key": "user", "value": "req.headers[\"x-user-id\"]" } ] } } ] } ] } ``` ### Make a POST request This example sends a POST request to an internal endpoint with a custom JSON body and headers. To simulate this behavior, use [httpbin.org/post](https://httpbin.org/post) behind an internal `.internal` endpoint. #### Start an Internal Agent Endpoint ```bash theme={null} ngrok http https://httpbin.org --host-header rewrite --url https://httpbin.internal ``` #### Example ```yaml policy.yml theme={null} on_http_request: - name: PostWithBody actions: - type: http-request config: url: https://httpbin.internal/post method: POST headers: content-type: application/json x-custom-header: abc123 body: '{ "user": "${req.headers[\"x-user-id\"]}" }' ``` ```json policy.json theme={null} { "on_http_request": [ { "name": "PostWithBody", "actions": [ { "type": "http-request", "config": { "url": "https://httpbin.internal/post", "method": "POST", "headers": { "content-type": "application/json", "x-custom-header": "abc123" }, "body": "{ \"user\": \"${req.headers[\\\"x-user-id\\\"]}\" }" } } ] } ] } ``` ### Follow redirects This example demonstrates how to follow up to 2 redirects using the `http-request` action. To simulate this behavior, use [httpbin.org/redirect](https://httpbin.org/redirect) behind an internal `.internal` endpoint. #### Start an Internal Agent Endpoint Run the following command to expose `https://httpbin.internal` as a simulated internal service: ```bash theme={null} ngrok http https://httpbin.org --host-header rewrite --url https://httpbin.internal ``` #### Example ```yaml policy.yml {8} theme={null} on_http_request: - name: FollowRedirects actions: - type: http-request config: url: https://httpbin.internal/redirect/2 method: GET max_redirects: 2 ``` ```json policy.json {11} theme={null} { "on_http_request": [ { "name": "FollowRedirects", "actions": [ { "type": "http-request", "config": { "url": "https://httpbin.internal/redirect/2", "method": "GET", "max_redirects": 2 } } ] } ] } ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. The ngrok error code for an error that occurred during the invocation of an action. The message for an error that occurred during the invocation of an action. A list of HTTP responses for each request attempt. The IP address of the host returning the response. A map of HTTP response headers. The HTTP Status code for the response attempt. Time it took to receive the response. The HTTP request. The HTTP method of the request. A map of HTTP request headers. The request url. The request body. The last attempted HTTP response. Unlike `actions.ngrok.attempts[i]` this variable also contains the response body. The IP address of the host returning the response. A map of HTTP response headers. The HTTP Status code for the response. Time it took to receive the response. The response body # Traffic Policy Actions Source: https://ngrok.com/docs/gateway/traffic-policy/actions/index Learn how to use Traffic Policy Actions to manage traffic to your endpoints. Traffic Policy Actions enable you to manipulate, route, and manage traffic on your endpoints in various ways. [Learn how actions work →](/docs/gateway/traffic-policy/how-it-works#actions) ## List of available actions The following actions are available for use on your endpoints: # JWT Validation Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/jwt-validation Validate JSON Web Tokens (JWT) on your endpoints before routing traffic to your upstream service. The **JWT Validation** Traffic Policy action enables you to validate JSON Web Tokens (JWT) on your endpoints before routing traffic to your upstream service. Guides for configuring this action with the following providers are available: * [Auth0](/docs/integrations/jwt-validation/auth0/) A useful tool for working with JWTs is provided at [jwt.io](https://jwt.io). ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request` ### Type `jwt-validation` ### Configuration fields Configuration object for the Issuer(s) of the JWTs. List of allowed issuers. Minimum `1`. Issuer URL. This can be found in the iss claim after decoding the JWT or from the /.well-known/openid-configuration endpoint in your Identity Provider. Configuration object for the Audience(s) of the JWTs. List of allowed audiences. Minimum `1`. Audience claim value. This can be found in the aud claim after decoding the JWT or from the request used to create the token in the first place. Configuration object for the HTTP requests containing JWTs. List of tokens to validate. Minimum `1`. Type of the JWT, which acts as a hint about how ngrok should parse the token. `id_token` `access_token` `at+jwt` `it+jwt` `plain` `jwt` Location in the request to expect the JWT. `header` `body` When choosing `header`, the content-type header must be set to either application/json or application/x-www-form-urlencoded. When choosing body, the method must be POST, PUT, or PATCH Including a token as a URL query parameter is not supported. Name of the header or body field where the JWT is expected (Example: "Authorization"). This is not case sensitive. Any prefix to strip from the header or body field before parsing the JWT (Example: "Bearer "). Configuration object for signed JWTs (JWS). List of allowed signing algorithms. The value none is not supported here because it is insecure. Minimum `1`. Configuration for the JWT signing keys. JWT metadata. List of claims present in this token. Supported values: \['kid'] Configuration for the key material used to verify the signed JWTs. List of URLs which serve the possible signing keys in JWKS format. These URLs are cached and refreshed roughly every 15 minutes. ## Behavior ### Request validation The request is allowed only if it has been correctly signed by the issuer and the defined claims match. For efficient validation of tokens, it is recommended to include the optional `kid` claim in your JWTs and JWK payload. Custom validation can be implemented in Traffic Policies via the [`actions.ngrok.jwt_validation.tokens`](#action-result-variables) Action Result Variable and its corresponding properties. ### Multiple issuers You can specify multiple issuers for JWT validation. A request is considered validated if it presents a JWT signed by any of the specified issuers. The issuer **must** exactly match the one provided in the JWT, including any trailing slashes (`/`) present in the `iss` claim. ### Multiple audience claims You can optionally specify one or more audience (`aud`) claims for JWT validation. If present, the `aud` claim **must** contain at least one of the specified audience claims and exactly match for validation to succeed. ### Multiple signing keys You can provide multiple JSON Web Key Set (JWKS) URLs and signing algorithms. During JWT validation the list of JWKS and algorithms provided will be used in an attempt to validate the JWT. The list will be tried in order and is cached for performance. The cache is refreshed roughly every 15 minutes. ### Multiple tokens If multiple tokens are defined within the HTTP configuration parameter, all tokens **must** be present in the request. If all tokens are not present, a `401 Unauthorized` status code will be returned. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Basic example The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration is an example configuration of the `jwt-validation` action. For a more real-world example, check out the [Auth0 guide](/docs/integrations/jwt-validation/auth0/). #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: jwt-validation config: issuer: allow_list: - value: https://example.com/issuer audience: allow_list: - value: urn:example:api http: tokens: - type: access_token method: header name: Authorization prefix: 'Bearer ' - type: it+jwt method: body name: _id_token jws: allowed_algorithms: - RS256 - ES256 keys: sources: additional_jkus: - https://example.com/issuer/jku ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "jwt-validation", "config": { "issuer": { "allow_list": [ { "value": "https://example.com/issuer" } ] }, "audience": { "allow_list": [ { "value": "urn:example:api" } ] }, "http": { "tokens": [ { "type": "access_token", "method": "header", "name": "Authorization", "prefix": "Bearer " }, { "type": "it+jwt", "method": "body", "name": "_id_token" } ] }, "jws": { "allowed_algorithms": [ "RS256", "ES256" ], "keys": { "sources": { "additional_jkus": [ "https://example.com/issuer/jku" ] } } } } } ] } ] } ``` #### Example request ```bash theme={null} $ curl --request GET \ --url http://example-api.ngrok.app/ \ --header 'authorization: Bearer ' ``` ```http theme={null} HTTP/2 200 OK content-type: text/html ... ``` In this example, a request is sent to the API with a valid JWT token in the `Authorization` header with the `Bearer` prefix and getting back a `200 OK` response. ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. The list of JSON Web Tokens (JWTs) processed by the action. The header portion of the JWT, parsed into a key-value map containing metadata about the token. The part of the HTTP request where the JWT was located (for example, header, body). The specific header or body field that contained the JWT (for example, "Authorization"). The payload portion of the JWT, parsed into a key-value map representing the claims. The signature portion of the JWT, encoded in base64, used for verifying token integrity. Indicates whether the token passed verification. Returns true if verified successfully. A machine-readable code describing an error that occurred during the action's execution. A human-readable message providing details about an error that occurred during the action's execution. # Log Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/log The Log action enables you to log metadata to ngrok's eventing system. The **Log** Traffic Policy action enables you to leverage ngrok's [eventing system](/docs/obs) to introduce observability into your Traffic Policy documents. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases * `on_tcp_connect` * `on_http_request` * `on_http_response` ### Action type `log` ### Configuration fields A key-value map of metadata that you would like to include in your events for this action. ## Behavior ### Event logs When this action is executed, the specified metadata will be collected on the [tcp\_connection\_closed.v0](/docs/obs/events/reference/#tcp-connection-closedv0) event under the `traffic_policy.logs` key. ### Event logs When this action is executed, the specified metadata will be collected on the [http\_request\_complete.v0](/docs/obs/events/reference/#http-request-completev0) event under `traffic_policy.logs` key. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Logging with CEL interpolation The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration will log a message with the endpoint identifier for every request. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: log config: metadata: message: Log action executed. endpoint_id: ${endpoint.id} ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "log", "config": { "metadata": { "message": "Log action executed.", "endpoint_id": "${endpoint.id}" } } } ] } ] } ``` #### Example request ```bash theme={null} $ curl https://example.ngrok.app ``` Once the request has completed, and you have properly configured an event destination, you should see an event appear with the following payload: ```jsx theme={null} { "event_id": "ev_25X3yFS6TDkig1KDJWIc4nnJO0c", "event_type": "http_request_complete.v0", "event_timestamp": "2024-06-23T23:44:16Z", "account_id": "ac_2OtNvAlhso10Gx6a7eupzX3F98q", "object": { "traffic_policy": { "logs": [ { "metadata": { "message": "Log action executed.", "endpoint_id": "ep_2bhsN2VP8W4pTkaMSrhyj0SRf8J" } } ] } } } ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. A key-value map containing metadata that was logged during the action. Each key represents a metadata attribute, and the value provides its corresponding details. # OAuth Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/oauth The OAuth action restricts access to only authorized users by enforcing OAuth through an identity provider of your choice. This page relates to requiring OAuth for visitors to access your endpoints. To configure OAuth and SSO providers for signing into the ngrok dashboard, see [the dashboard SSO documentation](/docs/iam/sso). The OAuth Traffic Policy action restricts access to your endpoints to only authorized users by enforcing OAuth through an identity provider of your choice. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request` ### Type `oauth` ### Configuration fields The name of the *OAuth* identity provider to be used for authentication. Unique authentication identifier for this provider. Used to scope the session cookie and to target this provider in login and logout paths. See [Special paths](#special-paths). Your OAuth app's client ID. Leave this empty if you want to use ngrok's managed application. Your OAuth app's client secret. Leave this empty if you want to use a managed application. A list of additional scopes to request when users authenticate with the identity provider. A map of additional URL parameters to apply to the authorization endpoint URL. Defines the maximum lifetime of a session regardless of activity. Defines the period of inactivity after which a user's session is automatically ended, requiring re-authentication. How often should ngrok refresh data about the authenticated user from the identity provider. Allow CORS preflight requests to bypass authentication checks. Enable this if the endpoint needs to be accessible via CORS. Sets the allowed domain for the auth cookie. ### Special paths #### Logging users in To start an OAuth authentication flow explicitly—for example, from a "Log in" button or link—redirect users to `/ngrok/login`. After a successful authentication, ngrok redirects the user to `/`. To send the user to a specific path after login, include the `redirect_path` parameter: ``` /ngrok/login?redirect_path=/dashboard ``` If you've configured `auth_id` to identify this provider, include it in the URL: ``` /ngrok/login?auth_id=my-provider&redirect_path=/dashboard ``` If the IdP supports it, ngrok instructs it to force re-authentication—users must re-enter their credentials even if they already have an active IdP session. #### Logging users out To end a user's session—for example, from a "Log out" button or link—redirect users to `/ngrok/logout`. This clears the ngrok session cookie. If you've configured `auth_id`, include it in the URL to target the correct provider: ``` /ngrok/logout?auth_id=my-provider ``` When using the Google OAuth 2.0/OIDC provider in Chrome with a [managed profile](https://support.google.com/chrome/a/answer/188446), `/ngrok/logout` only clears the ngrok session cookie; it does not end the Google/IdP session maintained by the browser. Users may be silently re-authenticated on the next request. To fully sign out, sign out of Chrome or Google (or use a non-managed profile or Incognito) in addition to calling `/ngrok/logout`. ### Events When this action is enabled, it populates the following fields in the [`http_request_complete.v0`](/docs/obs/events/reference/#http-request-complete) event: * `oauth.app_client_id` * `oauth.decision` * `oauth.user.id` * `oauth.user.name` ### Supported providers ngrok currently supports the following OAuth providers (see the Integration Guides for more details). In some instances, ngrok has a [managed application](#managed-applications) that allows you to configure OAuth without setting up your own application in your provider. This is useful for testing and development, but when you move into production, use your own custom application in your specific provider. | Provider | Provider Identifier | Managed App Available | Integration Guide | | - | - | - | - | | Amazon | `amazon` | no | [Documentation](/docs/integrations/oauth/oauth/) | | Facebook | `facebook` | no | [Documentation](/docs/integrations/oauth/facebook-oauth) | | GitHub | `github` | yes | [Documentation](/docs/integrations/oauth/github-oauth) | | GitLab | `gitlab` | yes | [Documentation](/docs/integrations/oauth/gitlab-oauth) | | Google | `google` | yes | [Documentation](/docs/integrations/oauth/google-oauth) | | LinkedIn | `linkedin` | yes | [Documentation](/docs/integrations/oauth/linkedin-oauth) | | Microsoft | `microsoft` | yes | [Documentation](/docs/integrations/oauth/microsoft-oauth) | | Twitch | `twitch` | yes | [Documentation](/docs/integrations/oauth/twitch-oauth) | ### Required scopes This is a list of the minimum required scopes for each provider. You can use this when configuring your identity provider. These are not required when using the ngrok managed applications. | Provider | Scopes | | - | - | | Amazon | `profile` | | Facebook | `email` | | Github | `read:org`, `read:user` | | Gitlab | `email`, `openid`, `profile` | | Google | `https://www.googleapis.com/auth/userinfo.email`, `https://www.googleapis.com/auth/userinfo.profile` | | LinkedIn | `r_emailaddress`, `r_liteprofile` | | Microsoft | `User.Read` | | Twitch | `user:read:email` | ## Try it out See the list of [supported providers](#supported-providers) for step-by-step integration guides. ## Behavior ### Callback URL When you create your own OAuth app, you must specify a callback (or redirect) URL to the OAuth provider. When using ngrok's OAuth action, the callback URL is always: ``` https://idp.ngrok.com/oauth2/callback ``` ### Authentication When an unauthenticated request is made to an OAuth-protected endpoint, it returns a redirect response that begins an authentication flow with the configured identity provider. The original URI path is saved so that users can be redirected to it if they successfully authenticate. **If the user fails to authenticate with the identity provider**, ngrok will display an error describing the failure returned by the identity provider and prompt them to try logging in again. **If the user successfully authenticates with the identity provider**, ngrok will take the following actions: * Sets a [session cookie](#cookies) to avoid repeating the authentication flow again. * Redirects the user to the original URI path they were attempting to access before the authentication flow began. If no such URI path was captured, they are redirected to `/`. * Continue processing the rest of the Traffic Policy actions. ### Continuous authorization When an authenticated user makes a request, ngrok will sometimes refresh a user's data from the identity provider (email, name, etc.) and re-evaluate authorization constraints. This refresh is executed as a back channel request to the identity provider; it is transparent to the user and they do not go through a re-authentication flow. The following circumstances trigger refresh and authorization re-evaluation: * On a periodic interval defined by the [`userinfo_refresh_interval`](/docs/gateway/traffic-policy/actions/oauth/#configuration-fields) parameter. * If you update the OAuth configuration of the endpoint either in the agent or through the dashboard. * If you update the OAuth configuration of the endpoint. If a previously authenticated user becomes unauthorized because their identity provider information changed or because the OAuth action configuration changed, they are presented an error and are prompted to try logging in again. ### Managed applications Managed applications allow you to use ngrok's OAuth action without setting up your own OAuth apps with the identity providers. More practically, this means you can use the OAuth action without configuring a client id and client secret. Managed applications are great for getting started but they have some limitations. * They are [only available for some identity providers](#supported-providers). * You may not pass custom scopes when using a managed application. * The [upstream headers](/docs/gateway/endpoints/http/#upstream-headers) `ngrok-auth-oauth-access-token` and `ngrok-auth-oauth-refresh-token` are not sent to your application. ### Traffic identities ngrok's [Traffic Identities](/docs/gateway/traffic-policy/identities/) feature can be used to observe all of the authenticated user activity across your account in the ngrok dashboard or via API. Whenever a user authenticates or accesses an endpoint with a configured OIDC action, their Traffic Identity record is created or updated. You may also use Traffic Identities to remotely log a user out by [revoking a session](/docs/gateway/traffic-policy/identities/#revoke-sessions). ### Cookies This action sets two cookies in its operation. Cookie values are opaque to the upstream service and must not be modified. | Cookie | Description | | - | - | | `session` | Used to track an authenticated user. | | `nonce` | Used to secure the authentication flow. | ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Using a managed provider The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration will provide your app with a Google authentication step. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: google ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google" } } ] } ] } ``` You can replace the `provider` value with any of the [supported providers](/docs/gateway/traffic-policy/actions/oauth/#supported-providers) that have a managed app available. ### Restricting access by email Use [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) to restrict access based on the authenticated user's email, allowing only users from a specific organization. ```yaml policy.yml theme={null} on_http_request: # Authenticate with Google OAuth - actions: - type: oauth config: provider: google # Only allow users with a company email address - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@your-company.com')" actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google" } } ] }, { "expressions": [ "!actions.ngrok.oauth.identity.email.endsWith('@your-company.com')" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` To allow only an explicit list of addresses instead of a whole domain, match against a list: ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: google # Only allow specific email addresses - expressions: - "!(actions.ngrok.oauth.identity.email in ['alice@example.com', 'bob@example.com'])" actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google" } } ] }, { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['alice@example.com', 'bob@example.com'])" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` For more ways to restrict access based on OAuth result variables, see [the Add Authentication example](/docs/gateway/traffic-policy/examples/add-authentication/#oauth). ### Using a Custom Provider If you need more control than what a managed provider can offord you then you can bring your own provider. #### Google Example ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: google client_id: '{your oauth client id}' client_secret: '{your oauth client secret}' scopes: - https://www.googleapis.com/auth/userinfo.profile - https://www.googleapis.com/auth/userinfo.email ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google", "client_id": "{your oauth client id}", "client_secret": "{your oauth client secret}", "scopes": [ "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email" ] } } ] } ] } ``` #### GitHub Example ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: github client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - read:user - read:org ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "github", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "read:user", "read:org" ] } } ] } ] } ``` #### GitLab Example ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: gitlab client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - openid - profile - email ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "gitlab", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "openid", "profile", "email" ] } } ] } ] } ``` #### LinkedIn Example ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: linkedin client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - r_emailaddress - r_liteprofile ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "linkedin", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "r_emailaddress", "r_liteprofile" ] } } ] } ] } ``` #### Microsoft Example ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: microsoft client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - openid - email - profile ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "microsoft", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "openid", "email", "profile" ] } } ] } ] } ``` #### Twitch Example ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: twitch client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - user:read:email - openid ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "twitch", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "user:read:email", "openid" ] } } ] } ] } ``` #### Amazon Example ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: amazon client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - profile ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "amazon", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "profile" ] } } ] } ] } ``` #### Facebook Example ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: facebook client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - email ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "facebook", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "email" ] } } ] } ] } ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. Code for an error that occurred during the invocation of an action. Message for an error that occurred during the invocation of an action. Unique identifier for the ngrok Identity entity. Email address of the authorized user from the provider. Name for the authorized user from the provider. Identifier for the authorized user from the provider. The current Identity session identifier for this request. The access token from the provider. The refresh token from the provider. Timestamp when the current session will expire. Returns true when the session timed out. Returns true when the current session reached the max duration. Returns true when ngrok updates the user information on the identity. # OpenID Connect Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/oidc The OpenID Connect (OIDC) action restricts access to only authorized users by enforcing OIDC through an identity provider of your choice. The **OpenID Connect** Traffic Policy action restricts access to only authorized users by enforcing **OIDC** through an identity provider of your choice. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request` ### Type `openid-connect` ### Configuration fields The base URL of the Open ID provider that serves an OpenID Provider Configuration Document at `/.well-known/openid-configuration`. Unique authentication identifier for this provider. Used to scope the session cookie and to target this provider in login and logout paths. See [Special paths](#special-paths). Your OpenID Connect app's client ID. Your OpenID Connect app's client secret. A list of additional scopes to request when users authenticate with the identity provider. A map of additional URL parameters to apply to the authorization endpoint URL. Defines the maximum lifetime of a session regardless of activity. Defines the period of inactivity after which a user's session is automatically ended, requiring re-authentication. How often should ngrok refresh data about the authenticated user from the identity provider. Allow CORS preflight requests to bypass authentication checks. Enable if the endpoint needs to be accessible via CORS. Sets the allowed domain for the auth cookie. ### Special paths #### Logging users in To start an OIDC authentication flow explicitly—for example, from a "Log in" button or link—redirect users to `/ngrok/login`. After a successful authentication, ngrok redirects the user to `/`. If you've configured `auth_id` to identify this provider, include it in the URL: ``` /ngrok/login?auth_id=my-provider ``` If the IdP supports it, ngrok instructs it to force re-authentication—users must re-enter their credentials even if they already have an active IdP session. #### Logging users out To end a user's session—for example, from a "Log out" button or link—redirect users to `/ngrok/logout`. This clears the ngrok session cookie. If you've configured `auth_id`, include it in the URL to target the correct provider: ``` /ngrok/logout?auth_id=my-provider ``` When using the Google OAuth 2.0/OIDC provider in Chrome with a [managed profile](https://support.google.com/chrome/a/answer/188446), `/ngrok/logout` only clears the ngrok session cookie; it does not end the Google/IdP session maintained by the browser. Users may be silently re-authenticated on the next request. To fully sign out, sign out of Chrome or Google (or use a non-managed profile or Incognito) in addition to calling `/ngrok/logout`. ### Events When this module is enabled, it populates the following fields in the [http\_request\_complete.v0](/docs/obs/events/reference/#http-request-complete) event: * `oauth.app_client_id` * `oauth.decision` * `oauth.user.id` * `oauth.user.name` ### Supported providers ngrok currently supports the following OAuth providers (see the Integration Guides for more details). In some instances, ngrok has a [managed application](/docs/gateway/traffic-policy/actions/oauth/#managed-applications) that allows you to configure OAuth without setting up your own application in your provider. This is useful for testing and development, but when you move into production, use your own custom application in your specific provider. | Provider | Provider Identifier | Managed App Available | Integration Guide | | - | - | - | - | | Amazon | `amazon` | no | [Documentation](/docs/integrations/oauth/oauth/) | | Facebook | `facebook` | no | [Documentation](/docs/integrations/oauth/facebook-oauth) | | GitHub | `github` | yes | [Documentation](/docs/integrations/oauth/github-oauth) | | GitLab | `gitlab` | yes | [Documentation](/docs/integrations/oauth/gitlab-oauth) | | Google | `google` | yes | [Documentation](/docs/integrations/oauth/google-oauth) | | LinkedIn | `linkedin` | yes | [Documentation](/docs/integrations/oauth/linkedin-oauth) | | Microsoft | `microsoft` | yes | [Documentation](/docs/integrations/oauth/microsoft-oauth) | | Twitch | `twitch` | yes | [Documentation](/docs/integrations/oauth/twitch-oauth) | ## Try it out Consult the list of [supported providers](#supported-providers) for step-by-step integration guides. ## Behavior ### Callback URL When you create your own OIDC app, you must specify a 'Callback URL' or 'Redirect URL' to the OIDC provider. When using ngrok's OIDC action, that Callback URL is always: ``` https://idp.ngrok.com/oauth2/callback ``` ### Authentication When an unauthenticated request is made to an OIDC-protected endpoint, it returns a redirect response that begins an authentication flow with the configured identity provider. The original URI path is saved so that users can be redirected to it if they successfully authenticate. **If the user fails to authenticate with the identity provider**, ngrok will display an error describing the failure returned by the identity provider and prompt them to try logging in again. **If the user successfully authenticates with the identity provider**, ngrok will take the following actions: * Check any authorization constraints you've defined (like allowed emails or allowed email domains). If the user is not authorized, ngrok renders an error and prompts them to try logging in again. * Sets a [session cookie](#cookies) to avoid repeating the authentication flow again. * Redirects the user to the original URI path they were attempting to access before the authentication flow began. If no such URI path was captured, they are redirected to `/`. ### Continuous authorization When an authenticated user makes a request, ngrok will sometimes refresh a user's data from the identity provider (email, name, etc) and re-evaluate authorization constraints. This refresh is executed as a back channel request to the identity provider; it is transparent to the user and they do not go through a re-authentication flow. The following circumstances trigger refresh and authorization re-evaluation: * On a periodic interval defined by the [`userinfo_refresh_interval`](/docs/gateway/traffic-policy/actions/oidc/#configuration-fields) parameter. * If you update the OIDC configuration of the endpoint by restarting your agent with a new configuration. * If you update the OIDC configuration of the endpoint. If a previously authenticated user becomes unauthorized because their identity provider information changed or because the OIDC action configuration changed, they are presented an error and are prompted to try logging in again. ### Traffic Identities ngrok's [Traffic Identities](/docs/gateway/traffic-policy/identities/) feature can be used to observe all of the authenticated user activity across your account in the ngrok dashboard or via API. Whenever a user authenticates or accesses an endpoint with a configured OIDC action, their Traffic Identity record is created or updated. You may also use Traffic Identities to remotely log a user out by [revoking a session](/docs/gateway/traffic-policy/identities/#revoke-sessions). ### Cookies This action sets two cookies in its operation. Cookies values are opaque to the upstream service and must not be modified. | Cookie | Description | | - | - | | `session` | Used to track an authenticated user. | | `nonce` | Used to secure the authentication flow. | ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Using a managed provider The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration will provide your app with an authentication step. ```yaml policy.yml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: '{your issuer url}' client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - openid - profile - email ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "openid-connect", "config": { "issuer_url": "{your issuer url}", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "openid", "profile", "email" ] } } ] } ] } ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. Code for an error that occurred during the invocation of an action. Message for an error that occurred during the invocation of an action. Unique identifier for the ngrok Identity entity Email address of the authorized user from the provider. Name for the authorized user from the provider. Identifier for the authorized user from the provider. The current Identity session identifier for this request. The identity token from the provider for the current user. The access token from the provider. The refresh token from the provider. Timestamp when the current session will expire. Returns true when the session timed out. Returns true when the current session reached the max duration. Returns true when ngrok updates the user information on the identity. # OWASP CRS Request Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/owasp-crs-request Block common web attacks with the `owasp-crs-request` action in Traffic Policy. The `owasp-crs-request` action enables rule processing on incoming HTTP requests to your endpoint. To use rule processing to block malicious HTTP *responses*, enable the [OWASP CRS Response action](/docs/gateway/traffic-policy/actions/owasp-crs-response/). **Use both OWASP CRS actions for comprehensive protection.** The Request action protects against malicious client requests (such as SQL injection and XSS attacks). The Response action protects against data leaks or vulnerabilities in your upstream service's responses (such as exposed SQL error messages and sensitive data). Together, they provide defense-in-depth security for both incoming and outgoing traffic. [OWASP](https://owasp.org/) stands for the Open Web Application Security Project, an online community that, among other things, maintains annual lists of the most critical web application security risks. The [OWASP Core Rule Set](https://owasp.org/www-project-modsecurity-core-rule-set/) (CRS) is a set of attack detection rules exposed for use in your Traffic Policies. It includes protections against attacks like SQL Injection, Cross Site Scripting, Local File Inclusion, and many others. ### Configuration reference This is the [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. #### Supported phases `on_http_request` #### Type `owasp-crs-request` #### Configuration fields

Behavior if there is an error processing rules. Must be one of either "continue" or "halt" (default "halt").

More information can be found in the [Managing Fallback Behavior](#managing-fallback-behavior-on-error) section.

If true, rules for the request body are evaluated. Default is false.

See [Body Processing](#body-processing) for details and limitations.

List of OWASP CRS rule IDs to exclude from evaluation.

The minimum value is 900000 and the maximum value is 999999.

## Behavior This action evaluates rules for request headers and body (when `process_body` is enabled), and each matching rule adds to the overall score of a request. If the score exceeds the set score threshold, the action will block the request. The tallying process is called Anomaly Scoring, and is detailed on [the CRS website.](https://coreruleset.org/docs/2-how-crs-works/2-1-anomaly_scoring/) This action costs **10 [Traffic Policy Units (TPUs)](/docs/pricing-limits/traffic-policy-unit-pricing/)** per request evaluated. ### Default behavior The default behavior for this action is based on the following [Coraza](https://coraza.io/) directives and rules from v4.14.0 of the CRS: * [coraza.conf-recommended](https://github.com/corazawaf/coraza/blob/main/coraza.conf-recommended) * [crs-setup.conf.example](https://github.com/corazawaf/coraza-coreruleset/blob/main/rules/%40crs-setup.conf.example) * [@owasp\_crs/\*.conf](https://github.com/corazawaf/coraza-coreruleset/tree/main/rules/%40owasp_crs) Included in these rules is an inbound anomaly score threshold of 5 and a [paranoia level](https://coreruleset.org/docs/2-how-crs-works/2-2-paranoia_levels/) of 1. ### Managing fallback behavior (`on_error`) If `on_error` is set to `halt` (default) and the action encounters an error while evaluating rules, the Traffic Policy chain will halt and no further actions will be executed. For example, if you have a `log` action after the `owasp-crs-request` action, the `log` action will not be run and the error will be returned. However, if `on_error` is set to `continue`, actions that appear after the `owasp-crs-request` action will still be executed even if the `owasp-crs-request` action encounters an error. ### Body processing When `process_body` is enabled, ngrok buffers and evaluates rules against the first 4KB of the body. If the body is larger than 4KB, the portion after the first 4KB is ignored. Enabling `process_body` buffers the request body in memory, which may increase latency for large payloads. ### Rule exclusion When `exclude_rule_ids` is configured, ngrok skips evaluation of the specified rule IDs. This allows you to disable specific OWASP CRS rules that may be causing false positives in your environment. ### Inbound anomaly score threshold exceeded If the anomaly score accumulated from matching rules exceeds the threshold, ngrok blocks the request with a `HTTP 403` response. The request does not make it to your upstream. ### Failure to process the body successfully If ngrok is unable to read the request body successfully, ngrok blocks the request with a `HTTP 500` response. The request does not make it to your upstream. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Running in block mode The following configuration demonstrates how to run the `owasp-crs-request` action in block mode. #### Example Traffic Policy document ```yaml policy.yml {5} theme={null} on_http_request: - actions: - type: owasp-crs-request config: on_error: halt ``` ```json policy.json {8} theme={null} { "on_http_request": [ { "actions": [ { "type": "owasp-crs-request", "config": { "on_error": "halt" } } ] } ] } ``` ### Example request that ngrok would block In this example, a connection attempt to `example.ngrok.app` using the `curl` command, and ngrok blocks the request returning back [ERR\_NGROK\_3700](/docs/errors/err_ngrok_3700/) ```curl theme={null} $ curl --http1.1 -X POST https://example.ngrok.app/\ -H "User-Agent: Mozilla/5.0" \ -H "Content-Type: application/json" \ -d '{"q": ""}' ``` ```html theme={null}
``` ### Running in test mode The following configuration demonstrates how to run the `owasp-crs-request` action in test mode where rules are evaluated but blocks are not enforced. #### Example Traffic Policy document ```yaml policy.yml {5} theme={null} on_http_request: - actions: - type: owasp-crs-request config: on_error: continue - type: log config: metadata: message: OWASP CRS request action would be ${actions.ngrok.owasp_crs_request.decision} for ${req.url} error_code: ${actions.ngrok.owasp_crs_request.error.code} error_message: ${actions.ngrok.owasp_crs_request.error.message} matched_rules_first_id: ${actions.ngrok.owasp_crs_request.matched_rules[0].id} matched_rules_first_message: ${actions.ngrok.owasp_crs_request.matched_rules[0].message} matched_rules_first_data: ${actions.ngrok.owasp_crs_request.matched_rules[0].data} ``` ```json policy.json {8} theme={null} { "on_http_request": [ { "actions": [ { "type": "owasp-crs-request", "config": { "on_error": "continue" } }, { "type": "log", "config": { "metadata": { "message": "OWASP CRS request action would be ${actions.ngrok.owasp_crs_request.decision} for ${req.url}", "error_code": "${actions.ngrok.owasp_crs_request.error.code}", "error_message": "${actions.ngrok.owasp_crs_request.error.message}", "matched_rules_first_id": "${actions.ngrok.owasp_crs_request.matched_rules[0].id}", "matched_rules_first_message": "${actions.ngrok.owasp_crs_request.matched_rules[0].message}", "matched_rules_first_data": "${actions.ngrok.owasp_crs_request.matched_rules[0].data}" } } } ] } ] } ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated.

The action taken for this request.

`allow` - If the request was permitted. `deny` - If the request was denied.

The total anomaly score for the request. If it equals to or exceeds the set threshold, it will block the request.

The total anomaly score threshold for the request. By default, it is set to 5.

The list of all rules matched by this request that have a non-zero score.

The ID of the rule.

The text message describing the rule.

The [severity](https://coraza.io/docs/seclang/actions/#severity) of the matched rule.

Any extra data about the rule and how it matched the request.

A machine-readable code describing an error that occurred during the action's execution.

A human-readable message providing details about an error that occurred during the action's execution.

# OWASP CRS Response Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/owasp-crs-response Block common web attacks with the `owasp-crs-response` action in Traffic Policy. The `owasp-crs-response` action enables rule processing on HTTP responses from your upstream service. To use rule processing to block malicious HTTP *requests*, enable the [OWASP CRS Request action](/docs/gateway/traffic-policy/actions/owasp-crs-request/). **Use both OWASP CRS actions for comprehensive protection.** The Response action protects against data leaks or vulnerabilities in your upstream service's responses (such as exposed SQL error messages and sensitive data). The Request action protects against malicious client requests (such as SQL injection and XSS attacks). Together, they provide defense-in-depth security for both incoming and outgoing traffic. [OWASP](https://owasp.org/) stands for the Open Web Application Security Project, an online community that, among other things, maintains annual lists of the most critical web application security risks. The [OWASP Core Rule Set](https://owasp.org/www-project-modsecurity-core-rule-set/) (CRS) is a set of attack detection rules exposed for use in your Traffic Policies. It includes protections against attacks like SQL Injection, Cross Site Scripting, Local File Inclusion, and many others. ### Configuration reference This is the [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. #### Supported phases `on_http_response` #### Type `owasp-crs-response` #### Configuration fields

Behavior if there is an error processing rules. Must be one of either "continue" or "halt" (default "halt").

More information can be found in the [Managing Fallback Behavior](#managing-fallback-behavior-on-error) section.

If true, rules for the response body are evaluated. Default is false.

See [Body Processing](#body-processing) for details and limitations.

List of OWASP CRS rule IDs to exclude from evaluation.

The minimum value is 900000 and the maximum value is 999999.

## Behavior This action evaluates rules for response headers and body (when `process_body` is enabled), and each matching rule adds to the overall score of a response. If the score exceeds the set score threshold, the action will block the response. The tallying process is called Anomaly Scoring, and is detailed on [the CRS website.](https://coreruleset.org/docs/2-how-crs-works/2-1-anomaly_scoring/) This action costs **10 [Traffic Policy Units (TPUs)](/docs/pricing-limits/traffic-policy-unit-pricing/)** per response evaluated. ### Default behavior The default behavior for this action is based on the following [Coraza](https://coraza.io/) directives and rules from v4.14.0 of the CRS: * [coraza.conf-recommended](https://github.com/corazawaf/coraza/blob/main/coraza.conf-recommended) * [crs-setup.conf.example](https://github.com/corazawaf/coraza-coreruleset/blob/main/rules/%40crs-setup.conf.example) * [@owasp\_crs/\*.conf](https://github.com/corazawaf/coraza-coreruleset/tree/main/rules/%40owasp_crs) Included in these rules is an outbound anomaly score threshold of 4 and a [paranoia level](https://coreruleset.org/docs/2-how-crs-works/2-2-paranoia_levels/) of 1. ### Managing fallback behavior (`on_error`) If `on_error` is set to `halt` (default) and the action encounters an error while evaluating rules, the Traffic Policy chain will halt and no further actions will be executed. For example, if you have a `log` action after the `owasp-crs-response` action, the `log` action will not be run and the error will be returned. However, if `on_error` is set to `continue`, actions that appear after the `owasp-crs-response` action will still be executed even if the `owasp-crs-response` action encounters an error. ### Body processing When `process_body` is enabled, ngrok buffers and evaluates rules against the first 4KB of the body. If the body is larger than 4KB, the portion after the first 4KB is ignored. Enabling `process_body` buffers the response body in memory, which may increase latency for large payloads. ### Rule exclusion When `exclude_rule_ids` is configured, ngrok skips evaluation of the specified rule IDs. This allows you to disable specific OWASP CRS rules that may be causing false positives in your environment. ### Outbound anomaly score threshold exceeded If the anomaly score accumulated from matching rules exceeds the threshold, ngrok blocks the request with a `HTTP 403` response. The response from your upstream does not make it to the client. ### Failure to process the body successfully If ngrok is unable to read the response body successfully, ngrok blocks the response with a `HTTP 500` response. The response from your upstream does not make it to the client. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Running in block mode The following configuration demonstrates how to run the `owasp-crs-response` action in block mode. #### Example Traffic Policy document ```yaml policy.yml {5} theme={null} on_http_response: - actions: - type: owasp-crs-response config: on_error: halt ``` ```json policy.json {8} theme={null} { "on_http_response": [ { "actions": [ { "type": "owasp-crs-response", "config": { "on_error": "halt" } } ] } ] } ``` ### Running in test mode The following configuration demonstrates how to run the `owasp-crs-response` action in test mode where rules are evaluated but blocks are not enforced. #### Example Traffic Policy document ```yaml policy.yml {5} theme={null} on_http_response: - actions: - type: owasp-crs-response config: on_error: continue - type: log config: metadata: message: OWASP CRS response action would be ${actions.ngrok.owasp_crs_response.decision} for ${req.url} error_code: ${actions.ngrok.owasp_crs_response.error.code} error_message: ${actions.ngrok.owasp_crs_response.error.message} matched_rules_first_id: ${actions.ngrok.owasp_crs_response.matched_rules[0].id} matched_rules_first_message: ${actions.ngrok.owasp_crs_response.matched_rules[0].message} matched_rules_first_data: ${actions.ngrok.owasp_crs_response.matched_rules[0].data} ``` ```json policy.json {8} theme={null} { "on_http_response": [ { "actions": [ { "type": "owasp-crs-response", "config": { "on_error": "continue" } }, { "type": "log", "config": { "metadata": { "message": "OWASP CRS response action would be ${actions.ngrok.owasp_crs_response.decision} for ${req.url}", "error_code": "${actions.ngrok.owasp_crs_response.error.code}", "error_message": "${actions.ngrok.owasp_crs_response.error.message}", "matched_rules_first_id": "${actions.ngrok.owasp_crs_response.matched_rules[0].id}", "matched_rules_first_message": "${actions.ngrok.owasp_crs_response.matched_rules[0].message}", "matched_rules_first_data": "${actions.ngrok.owasp_crs_response.matched_rules[0].data}" } } } ] } ] } ``` ### Example response from your upstream that ngrok would block A `text/plain` response like the following will result in ngrok blocking the response due to potential security leaks that is, SQL Injection vulnerabilities. ``` You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT * FROM users WHERE id = 'abc'' at line 1 ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated.

The action taken for this response.

`allow` - If the response was permitted. `deny` - If the response was denied.

The total anomaly score for the response. If it equals to or exceeds the set threshold, it will block the response.

The total anomaly score threshold for the response. By default, it is set to 4.

The list of all rules matched by this response that have a non-zero score.

The ID of the rule.

The text message describing the rule.

The [severity](https://coraza.io/docs/seclang/actions/#severity) of the matched rule.

Any extra data about the rule and how it matched the response.

A machine-readable code describing an error that occurred during the action's execution.

A human-readable message providing details about an error that occurred during the action's execution.

# Rate Limit Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/rate-limit Limit the rate of traffic that successfully reaches your endpoint. The **Rate Limit** Traffic Policy action enables you to configure thresholds that restrict the throughput of traffic that successfully reaches your endpoint. Traffic may be limited overall or by attributes of the incoming requests. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request` ### Type `rate-limit` ### Configuration fields

A name for this rate limit configuration. Must be less than 1024 characters.

Controls whether the rate limit is actively applied at runtime. When enabled, requests exceeding the limit are blocked or throttled as configured. When disabled, the system evaluates the rate limit but does not enforce it, allowing you to test configurations, gather metrics, or return a custom response.

The default value is true.

The rate limit algorithm to be used.

`sliding_window`

The maximum number of requests allowed to reach your upstream server.

The minimum capacity is 1 and the maximum capacity is 2,000,000,000.

The duration in which events may be limited based on the current capacity. Must be specified as a time duration that is a multiple of ten seconds (for example, "90s", "10m").

The minimum value is "60s" and the maximum value is "24h".

The elements of this collection define the unique key of a request to track the rate at which the capacity is being met.

Each bucket key is a CEL expression which includes all valid Traffic Policy [variables](/docs/gateway/traffic-policy/variables/) and [macros](/docs/gateway/traffic-policy/macros/).

`req.host` - The Host of the request. `conn.client_ip` - The client IP address. `getReqHeader('X-Example-Header-Name')` - The value for the specified header key, if it exists.

Up to ten bucket keys can be specified. For multiple buckets, the action will rate limit by each unique combination of buckets.

## Behavior ### Determining the rate limit bucket When this action is executed, information from the incoming HTTP request is used to determine which rate limit bucket the request falls into. Each bucket is defined by specific criteria through the `bucket_key` configuration field such as client IP, request host, or a header value. If the bucket has not exceeded its capacity, the request proceeds to the next action in your policy configuration. ## Multiple buckets If multiple `bucket_key` values are specified, the action will create a unique rate limit bucket for each combination of the specified keys. For example, if you have two `bucket_key` values, such as `req.host` and `conn.client_ip`, all incoming requests that have the exact same combination of `Host` header and client IP will be grouped into the same rate limit bucket. To rate limit separately with two different buckets, you can create multiple `rate-limit` actions instead. ### Rate limit exceeded If the identified bucket has received more events than its capacity over the specified duration: 1. The request is rejected with an `HTTP 429—Too Many Requests` status code. 2. The `retry-after` header is included in the response, indicating the number of seconds after which the request may be retried. ### Capacity per ingress server Currently, the `capacity` for each rate limit bucket is applied per ingress server. This means that each server independently tracks the number of requests and enforces the rate limits accordingly. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Rate limit by host header The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `rate-limit` action to rate limit all incoming requests by the `Host` header. #### Example Traffic Policy document ```yaml policy.yml {10} theme={null} on_http_request: - actions: - type: rate-limit config: name: Only allow 30 requests per minute algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - 'hasReqHeader(''host'') ? getReqHeader(''host'')[0] : ''unknown''' ``` ```json policy.json {13} theme={null} { "on_http_request": [ { "actions": [ { "type": "rate-limit", "config": { "name": "Only allow 30 requests per minute", "algorithm": "sliding_window", "capacity": 30, "rate": "60s", "bucket_key": [ "hasReqHeader('host') ? getReqHeader('host')[0] : 'unknown'" ] } } ] } ] } ``` For this example, assume that ngrok is pointing at the upstream service [https://httpbin.org](https://httpbin.org). #### Example request ```bash theme={null} $ curl https://httpbin.ngrok.app/get ``` ```http theme={null} HTTP/2 429 retry-after: 24 ``` In this example, a connection attempt to `httpbin.ngrok.app` using the `curl` command returns a `429` status code with a `retry-after` header indicating the number of seconds to wait before retrying the request. ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated.

The key used for bucketing requests. This is the key used to group and track requests in the rate-limiting process, ensuring that the same bucket is subject to the rate limit across multiple requests.

Indicates whether the request was limited by the rate limit. If `true`, the request was rate-limited based on the configured limits for the specified bucket.

A machine-readable code describing an error that occurred during the action's execution.

A human-readable message providing details about an error that occurred during the action's execution.

# Redirect Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/redirect Redirect incoming requests to new URLs by modifying the original URLs with regular expressions. The Redirect Traffic Policy Action enables users to send HTTP `3xx` responses by transforming the incoming URL using a regular expression and optional CEL interpolation. It can modify any part of the URL such as scheme, host, port, path, or query, and returns a redirect via the `Location` header. This action is different from the [URL Rewrite Action](/docs/gateway/traffic-policy/actions/url-rewrite/), which changes the request internally while keeping the client's URL unchanged. Redirects, by contrast, instruct the client to issue a new request to the new URL. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request` ### Type `redirect` ### Configuration fields A regular expression pattern used to match a part of the URL. A regular expression pattern used to replace the matched part of the URL. A 3xx status code used for redirecting. Map of key-value headers to be added to the response. Maximum 10 headers can be specified. ## Behavior The Redirect Action modifies the request URL and returns an HTTP redirect (`3xx`) response. It performs the following transformation in sequence: 1. Evaluate any CEL interpolation 2. Apply the regular expression match (`from`) 3. Replace the matched text or URL with the `to` value 4. Send the redirect response If the rewritten URL is identical to the original, ngrok skips the redirect. ### CEL interpolation CEL expressions in `${...}` are evaluated before the redirect rule is applied. Interpolations can reference request or connection data. For example: ```yaml theme={null} to: https://example.com?city=${conn.geo.city} ``` If the request originates from San Francisco, the final redirect target is: ``` https://example.com?city=San%20Francisco ``` ### Matching The `from` field is a **regular expression** evaluated against the entire URL (scheme, host, port, path, query, and fragment). Matching is not anchored by default; a substring match anywhere in the URL triggers the redirect. #### Example The following example redirects any request that contains `/api/v1/` to `/api/v2/`. The redirect is applied to the entire URL, not just the path. ```yaml theme={null} from: /api/v1/ to: /api/v2/ ``` Given the above, `https://example.com/foobar/api/v1/users` would be redirected to `https://example.com/foobar/api/v2/users`. #### Anchoring / path prefix Anchoring with `^/api/v1/` will not match paths that include the full scheme. To match only the path, use one of the following methods: ```yaml Regular Expressions theme={null} # Anchor the full URL, capture authority, and rewrite the path segment. from: ^((?:https?)://[^/]+)/api/v1/(.*)$ to: $1/api/v2/$2 ``` ```yaml CEL Interpolation theme={null} # Compose anchors with CEL to bind the scheme and authority before matching. from: ^${req.url.scheme}://${req.url.authority}/api/v1/ to: ${req.url.scheme}://${req.url.authority}/api/v2/ ``` ```yaml Phase Expressions theme={null} # Constrain by path in expressions, then use a relative replacement. on_http_request: - expressions: - req.url.path.startsWith('/api/v1/') actions: - type: redirect config: from: /api/v1/ to: /api/v2/ ``` ### Replacement behavior The `to` value replaces what was matched by `from`. The replacement is relative to the `from` match. When `from` is omitted, the redirect action replaces the entire URL. Capture groups (such as `$1`, `$2`) can be used to insert matched text into the replacement. When `from` matches a substring, only that segment changes: ```yaml theme={null} from: /v1/ to: /v2/ ``` Given the above, `https://example.com/api/v1/users` would be redirected to `https://example.com/api/v2/users`. #### Relative versus absolute replacements The `to` value is applied relative to the `from` match. When `from` matches part of the URL, only that segment is replaced. When `from` is omitted, ngrok treats the entire URL as matched and replaces it absolutely. **Relative example** ```yaml theme={null} from: /old to: https://new.example.com/blog ``` Given the above, `https://example.com/old/foo` would become `https://example.com/https://new.example.com/blog/foo`. **Absolute example** When a partial match exists, the replacement is relative; when no `from` match is defined, it is absolute: ```yaml theme={null} to: https://new.example.com/blog ``` Given the above, any request to `https://example.com` would be redirected to `https://new.example.com/blog`. **Redirecting to another host** To redirect cleanly to a new host, match the entire URL: ```yaml theme={null} from: ^https?://[^/]+/old/(.*)$ to: https://new.example.com/new/$1 ``` Given the above, `https://example.com/old/foo` would be redirected to `https://new.example.com/new/foo`. If the rewritten URL equals the original, ngrok skips the redirect. ### Redirect response After replacement, the redirect action sends an HTTP redirect with the computed `Location` header. The default status code is `302 Found`. | Code | Description | Behavior | | - | - | - | | **301** | Moved Permanently | Clients may cache the redirect. | | **302** | Found (default) | Temporary redirect; some clients change the request method to `GET`. | | **303** | See Other | Forces the next request to use `GET`. | | **307** | Temporary Redirect | Preserves the original request method and body. | | **308** | Permanent Redirect | Preserves the original request method and body. | You can also include up to 10 custom headers in the redirect response. **Example** ```yaml theme={null} from: ^(https?://[^/]+)/products(.*)$ to: $1/store/products$2 status_code: 301 headers: x-redirected: true ``` **Result** ``` HTTP/1.1 301 Moved Permanently location: https://example.ngrok.app/store/products x-redirected: true ``` Once the redirect is sent, the action ends the current phase and no further actions are executed. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Redirect using paths The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `redirect` action to redirect incoming requests from `/products` to `/store/products`. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: redirect config: from: ^(https?://[^/]+)/products(.*)$ to: $1/store/products$2 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "redirect", "config": { "from": "^(https?://[^/]+)/products(.*)$", "to": "$1/store/products$2" } } ] } ] } ``` This configuration will redirect any request from `/products` to `/store/products` with the default `302 Found` status code. #### Example request ```bash theme={null} $ curl -i https://example.ngrok.app/products ``` ```http theme={null} HTTP/1.1 302 Found location: https://example.ngrok.app/store/products ``` In this example, a request to `/products` will be redirected to `/store/products` with a `302 Found` status code, and the `Location` header will indicate the new URL. ### Redirect using regular expressions The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `redirect` action to redirects requests from an old API endpoint to a new one. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - expressions: - req.url.path.startsWith('/api/') actions: - type: redirect config: from: /api/v1/users?id=([0-9]+) to: /api/v2/users/$1/ status_code: 301 headers: x-redirected: 'true' ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.url.path.startsWith('/api/')" ], "actions": [ { "type": "redirect", "config": { "from": "/api/v1/users?id=([0-9]+)", "to": "/api/v2/users/$1/", "status_code": 301, "headers": { "x-redirected": "true" } } } ] } ] } ``` In this configuration, requests from `/api/v1/users?id=([0-9]+)` are matched and redirected to `/api/v2/users/$1/` with a `301` status code. Additionally, a custom header `x-redirected: true` is added to the response. #### Example request ```bash theme={null} $ curl -i https://example.ngrok.app/api/v1/users?id=123 ``` ```http theme={null} HTTP/1.1 301 Moved Permanently location: https://example.ngrok.app/api/v2/users/123/ x-redirected: true ``` The request will be redirected to the new URL `/api/v2/users/123/`, with a `301 Moved Permanently` status and a custom header. ### Redirect with CEL interpolation The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `redirect` action to redirect users while using CEL Interpolation. #### Example Traffic Policy document ```yaml policy.yml {7} theme={null} on_http_request: - expressions: - req.url.path in ['/api/v2/geo', '/api/v2/geo/'] actions: - type: redirect config: to: /api/v2/geo?city=${conn.geo.city} ``` ```json policy.json {11} theme={null} { "on_http_request": [ { "expressions": [ "req.url.path in ['/api/v2/geo', '/api/v2/geo/']" ], "actions": [ { "type": "redirect", "config": { "to": "/api/v2/geo?city=${conn.geo.city}" } } ] } ] } ``` This configuration will redirect any request from `/api/v2/geo` or `/api/v2/geo/` to `/api/v2/geo?city=${conn.geo.city}` using CEL Interpolation to insert the city from the connection's geolocation data. #### Example request ```bash theme={null} $ curl -i https://example.ngrok.app/api/v2/geo ``` ```http theme={null} HTTP/1.1 302 Found location: https://example.ngrok.app/api/v2/geo?city=San%20Francisco ``` In this example, a request to `https://example.ngrok.app/api/v2/geo` will be redirected to `https://example.ngrok.app/api/v2/geo?city=San Francisco`, with the `302 Found` status code and the `Location` header indicating the new URL that includes the city from the connection's geolocation data. ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. A list of elements that were matched during redirection. These represent the request components (for example, path or query parameters) that triggered the action and resulted in the redirect. The URL to which the traffic was redirected. This is the destination URL returned as part of the redirect response after the action was executed. A machine-readable code describing an error that occurred during the action's execution. A human-readable message providing details about an error that occurred during the action's execution. # Remove Headers Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/remove-headers Remove headers from an HTTP request or response before it is delivered upstream or back to the client. The **Remove Headers** Traffic Policy action enables you to remove headers from an HTTP request before it is sent upstream or from an HTTP response before it is sent back to the client. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request`, `on_http_response` ### Type `remove-headers` ### Configuration fields List of header keys to remove from the request or response. Minimum 1, Maximum 10. ## Behavior ### During `on_http_request` When executed during the `on_http_request` phase, this action will remove the specified headers from incoming HTTP requests before reaching the upstream server. ### During `on_http_response` When executed during the `on_http_response`, the configured headers are removed from HTTP responses returned from the upstream server before reaching the client. ### Removal only This action will only remove headers from the request or response. * If you want to add a header use the [`add-headers`](/docs/gateway/traffic-policy/actions/add-headers) action. * If you want to replace a header, first use this action *then* use the [`add-headers`](/docs/gateway/traffic-policy/actions/add-headers) action. ### Ordering Since actions are run in the order they are specified, to modify headers that have been added by other actions you must place this action after them in your Traffic Policy document. ### Special cases * You may not add or remove the `user-agent` header. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Removing headers from all requests If you're getting a lot of requests with `x-client-version` and `x-trace-id` and it is starting to overwhelm your logging system, remove these headers from all requests. The following example demonstrates how to remove these headers using the `remove-headers` action. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: remove-headers config: headers: - x-client-version - x-trace-id ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "remove-headers", "config": { "headers": [ "x-client-version", "x-trace-id" ] } } ] } ] } ``` For this example, assume that ngrok is pointing at the upstream service [https://httpbin.org](https://httpbin.org). #### Example request ```bash theme={null} $ curl -H "x-client-version: 3.1" -H "x-trace-id: abc123" https://httpbin.ngrok.app/get < HTTP/2 200 { ... "headers": { "Date": "2024-06-24T15:30:00Z", } ... } ``` In this example even though the `x-client-version` and `x-trace-id` headers were sent, they were not present on the request that was sent upstream. ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. A list of headers that were successfully removed by the action. # Request Body Find & Replace Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/request-body-find-replace Find and replace text patterns in HTTP request bodies using regular expressions. The Request Body Find & Replace action enables you to modify HTTP request body content by finding and replacing text patterns using regular expressions. This is useful for redacting sensitive information, transforming data formats, or modifying AI prompts before they reach upstream services. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request` ### Type `request-body-find-replace` ### Configuration fields Array of replacement rules to apply to the request body. Rules are applied in order. Minimum `1` replacement required. Regular expression pattern to match. Supports [RE2 syntax](https://github.com/google/re2/wiki/Syntax). CEL interpolation is supported for dynamic patterns. Replacement string. Use `$1`, `$2`, etc. to reference capture groups from the pattern. CEL interpolation is supported for dynamic replacements. If omitted or empty, matched text is deleted. ## Behavior When this action executes, it buffers the entire request body, applies all replacement rules in order, and updates the `Content-Length` header if present. ### Pattern matching The `from` field accepts [RE2 regular expressions](https://github.com/google/re2/wiki/Syntax). All matches in the body are replaced, not just the first occurrence. ### Capture groups You can use capture groups in your pattern and reference them in the replacement: ```yaml theme={null} replacements: - from: "api_key=([a-zA-Z0-9]+)" to: "api_key=REDACTED" ``` ### CEL expressions Both `from` and `to` fields support CEL interpolation for dynamic values: ```yaml theme={null} replacements: - from: "${vars.pattern_to_match}" to: "replaced with ${req.headers['x-replacement-value']}" ``` When using CEL in the `from` field, the resulting string must be a valid regular expression. ### Content-Length handling After replacements are applied, the action automatically updates the `Content-Length` header to reflect the new body size. ### Ordering Replacement rules are applied in the order they are specified. Later rules operate on the result of earlier rules. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Redacting sensitive data from requests The following configuration redacts API keys and email addresses from request bodies. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: request-body-find-replace config: replacements: - from: '"api_key":\s*"[^"]*"' to: '"api_key": "[REDACTED]"' - from: "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}" to: "[EMAIL REDACTED]" ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "request-body-find-replace", "config": { "replacements": [ { "from": "\"api_key\":\\s*\"[^\"]*\"", "to": "\"api_key\": \"[REDACTED]\"" }, { "from": "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "to": "[EMAIL REDACTED]" } ] } } ] } ] } ``` #### Example request Before (original request body): ```json theme={null} { "api_key": "sk-abc123secret", "user_email": "john.doe@example.com", "message": "Hello world" } ``` After (modified request body): ```json theme={null} { "api_key": "[REDACTED]", "user_email": "[EMAIL REDACTED]", "message": "Hello world" } ``` ### Modifying AI prompts Add system instructions to AI model requests: ```yaml policy.yml theme={null} on_http_request: - actions: - type: request-body-find-replace config: replacements: - from: '"role":\s*"system",\s*"content":\s*"([^"]*)"' to: '"role": "system", "content": "$1 Always respond in a professional tone."' ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "request-body-find-replace", "config": { "replacements": [ { "from": "\"role\":\\s*\"system\",\\s*\"content\":\\s*\"([^\"]*)\"", "to": "\"role\": \"system\", \"content\": \"$1 Always respond in a professional tone.\"" } ] } } ] } ] } ``` ### Removing unwanted content Delete specific patterns by using an empty `to` value: ```yaml policy.yml theme={null} on_http_request: - actions: - type: request-body-find-replace config: replacements: - from: "" to: "" ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "request-body-find-replace", "config": { "replacements": [ { "from": "", "to": "" } ] } } ] } ] } ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. Array of objects describing which replacement rules matched. The zero-based index of the replacement rule that matched. The first matched content from this replacement rule. ## Related actions * [Response Body Find & Replace](/docs/gateway/traffic-policy/actions/response-body-find-replace) - Modify response bodies * [SSE Find & Replace](/docs/gateway/traffic-policy/actions/sse-find-replace) - Modify Server-Sent Events streams # Response Body Find & Replace Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/response-body-find-replace Find and replace text patterns in HTTP response bodies using regular expressions. The Response Body Find & Replace action enables you to modify HTTP response body content by finding and replacing text patterns using regular expressions. This is useful for redacting sensitive information in responses, transforming data formats, or modifying AI model outputs before they reach clients. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_response` ### Type `response-body-find-replace` ### Configuration fields Array of replacement rules to apply to the response body. Rules are applied in order. Minimum `1` replacement required. Regular expression pattern to match. Supports [RE2 syntax](https://github.com/google/re2/wiki/Syntax). CEL interpolation is supported for dynamic patterns. Replacement string. Use `$1`, `$2`, etc. to reference capture groups from the pattern. CEL interpolation is supported for dynamic replacements. If omitted or empty, matched text is deleted. ## Behavior When this action executes, it buffers the entire response body, applies all replacement rules in order, and updates the `Content-Length` header if present. ### Pattern matching The `from` field accepts [RE2 regular expressions](https://github.com/google/re2/wiki/Syntax). All matches in the body are replaced, not just the first occurrence. ### Capture groups You can use capture groups in your pattern and reference them in the replacement: ```yaml theme={null} replacements: - from: "error_code: ([0-9]+)" to: "error_code: HIDDEN" ``` ### CEL expressions Both `from` and `to` fields support CEL interpolation for dynamic values: ```yaml theme={null} replacements: - from: "${vars.sensitive_pattern}" to: "[REDACTED by ${req.headers['x-request-id']}]" ``` When using CEL in the `from` field, the resulting string must be a valid regular expression. ### Content-Length handling After replacements are applied, the action automatically updates the `Content-Length` header to reflect the new body size. ### Ordering Replacement rules are applied in the order they are specified. Later rules operate on the result of earlier rules. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Redacting sensitive data from AI responses The following configuration redacts personal information from AI model responses. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_response: - actions: - type: response-body-find-replace config: replacements: # Redact SSN patterns - from: "\\b\\d{3}-\\d{2}-\\d{4}\\b" to: "[SSN REDACTED]" # Redact credit card numbers - from: "\\b\\d{4}[- ]?\\d{4}[- ]?\\d{4}[- ]?\\d{4}\\b" to: "[CARD REDACTED]" # Redact phone numbers - from: "\\b\\d{3}[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b" to: "[PHONE REDACTED]" ``` ```json policy.json theme={null} { "on_http_response": [ { "actions": [ { "type": "response-body-find-replace", "config": { "replacements": [ { "from": "\\b\\d{3}-\\d{2}-\\d{4}\\b", "to": "[SSN REDACTED]" }, { "from": "\\b\\d{4}[- ]?\\d{4}[- ]?\\d{4}[- ]?\\d{4}\\b", "to": "[CARD REDACTED]" }, { "from": "\\b\\d{3}[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b", "to": "[PHONE REDACTED]" } ] } } ] } ] } ``` #### Example response Before (original response body): ```text theme={null} Here's the customer information: - SSN: 123-45-6789 - Credit Card: 4111-1111-1111-1111 - Phone: 555-123-4567 ``` After (modified response body): ```text theme={null} Here's the customer information: - SSN: [SSN REDACTED] - Credit Card: [CARD REDACTED] - Phone: [PHONE REDACTED] ``` ### Adding disclaimers to AI responses Append legal disclaimers or warnings to AI-generated content: ```yaml policy.yml theme={null} on_http_response: - expressions: - "res.headers['content-type'].contains('application/json')" actions: - type: response-body-find-replace config: replacements: - from: '"content":\s*"([^"]*)"(\s*})' to: '"content": "$1\n\n---\nDisclaimer: This response was generated by AI and may contain errors."$2' ``` ```json policy.json theme={null} { "on_http_response": [ { "expressions": [ "res.headers['content-type'].contains('application/json')" ], "actions": [ { "type": "response-body-find-replace", "config": { "replacements": [ { "from": "\"content\":\\s*\"([^\"]*)\"(\\s*})", "to": "\"content\": \"$1\\n\\n---\\nDisclaimer: This response was generated by AI and may contain errors.\"$2" } ] } } ] } ] } ``` ### Sanitizing error messages Remove internal error details from responses before sending to clients: ```yaml policy.yml theme={null} on_http_response: - expressions: - "res.status_code >= 500" actions: - type: response-body-find-replace config: replacements: - from: '"stack_trace":\s*"[^"]*"' to: '"stack_trace": "[HIDDEN]"' - from: '"internal_error":\s*"[^"]*"' to: '"internal_error": "[HIDDEN]"' ``` ```json policy.json theme={null} { "on_http_response": [ { "expressions": [ "res.status_code >= 500" ], "actions": [ { "type": "response-body-find-replace", "config": { "replacements": [ { "from": "\"stack_trace\":\\s*\"[^\"]*\"", "to": "\"stack_trace\": \"[HIDDEN]\"" }, { "from": "\"internal_error\":\\s*\"[^\"]*\"", "to": "\"internal_error\": \"[HIDDEN]\"" } ] } } ] } ] } ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. Array of objects describing which replacement rules matched. The zero-based index of the replacement rule that matched. The first matched content from this replacement rule. ## Related actions * [Request Body Find & Replace](/docs/gateway/traffic-policy/actions/request-body-find-replace) - Modify request bodies * [SSE Find & Replace](/docs/gateway/traffic-policy/actions/sse-find-replace) - Modify Server-Sent Events streams # Restrict IPs Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/restrict-ips Allow or deny traffic based on the source IP address of connections to your ngrok endpoints. The **Restrict IPs** Traffic Policy action allows you to allow or deny traffic based on the source IP address of connections to your ngrok endpoints. You can define rules using either **Allow and Deny lists**, or **Reference IDs** to existing ngrok [IP Policies](/docs/api-reference/ippolicies/list). Need to allow or block traffic from a specific service (like GitHub, AWS, or OpenAI) without managing hundreds of CIDRs by hand? Use [IP Categories](/docs/gateway/traffic-policy/variables/ip-intel) with a Traffic Policy expression instead. ngrok maintains up-to-date IP ranges for [dozens of services and threat categories](/docs/gateway/traffic-policy/variables/ip-intel#ip-categories), so you can write rules like `'com.github' in conn.client_ip.categories` or `'proxy.anonymous.tor' in conn.client_ip.categories`. See [Filter Traffic by IP Category](/docs/gateway/traffic-policy/examples/filter-by-ip-category) for examples. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request`, `on_http_response`, `on_tcp_connect` ### Type `restrict-ips` ### Configuration fields Default true. If false, continue to the next action even if the IP is not permitted. A list of CIDRs that are allowed. A list of CIDRs that are denied. List of [IP Policy](/docs/api-reference/ippolicies/list) identifiers to be checked if the source IP is allowed access. ## Behavior ### Evaluation of rules This action evaluates the configured rules against the layer 4 source IP (`conn.client_ip`) of a connection. HTTP headers like `X-Forwarded-For` are never used. ### Allow and deny conditions A connection is allowed only if its source IP matches at least one of the allowed CIDRs and does not match any of the denied CIDRs. ### Building CIDR sets The set of allowed and denied CIDRs are built from: * The CIDRs specified in the `allow` and `deny` fields. * The CIDRs belonging to the ngrok [IP Policies](/docs/api-reference/ippolicies/list) specified in the `ip_policies` field. ### Denied connection handling If this action denies the connection: * When used on the `on_tcp_connect` phase: * The connection is closed. * When used on the `on_http_request` or `on_http_response` phase: * An `HTTP 403` response is returned. * The upstream server is never reached. * No further rules or actions in the Traffic Policy will be executed. ### IPv6 support This action supports IPv6 addresses for all IP rules. You may use standard abbreviated notations such as `"::/0"`. Don't forget to create IPv6 rules. It is easy to test with only IPv4 and then suddenly things don't work as expected because you forgot to create IPv6 rules. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples `on_tcp_connect` ### Restricting access with allow and deny lists The following [Traffic Policy](/docs/traffic-policy) configuration demonstrates how to restrict access to specific IP addresses using the `restrict-ips` action. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_tcp_connect: - actions: - type: restrict-ips config: enforce: true allow: - 1.1.1.1/32 deny: - 110.0.0.0/8 ``` ```json policy.json theme={null} { "on_tcp_connect": [ { "actions": [ { "type": "restrict-ips", "config": { "enforce": true, "allow": [ "1.1.1.1/32" ], "deny": [ "110.0.0.0/8" ] } } ] } ] } ``` This configuration will ensure that only requests from the IP `1.1.1.1` are allowed, while requests from the IP `110.0.0.0` are denied. #### Example request If the request comes from an allowed IP, the response will proceed as normal. If the request comes from a denied IP, the connection will be closed: ```bash theme={null} $ telnet 5.tcp.ngrok.io 22984 Trying... Connected to 5.tcp.ngrok.io. Connection closed by foreign host. ``` ### Restricting access with IP Policies The following [Traffic Policy](/docs/traffic-policy) configuration demonstrates how to restrict access using the `restrict-ips` action with [IP Policies](/docs/api-reference/ippolicies/list). #### Example Traffic Policy document ```yaml policy.yml theme={null} on_tcp_connect: - actions: - type: restrict-ips config: enforce: true ip_policies: - ipp_1yjqdrIBwgciY2I9zH2EelgBbJF ``` ```json policy.json theme={null} { "on_tcp_connect": [ { "actions": [ { "type": "restrict-ips", "config": { "enforce": true, "ip_policies": [ "ipp_1yjqdrIBwgciY2I9zH2EelgBbJF" ] } } ] } ] } ``` This configuration will ensure that the [IP Policies](/docs/api-reference/ippolicies/list) specified `ipp_1yjqdrIBwgciY2I9zH2EelgBbJF` are enforced against incoming traffic. #### Example request If the request comes from an allowed IP, the response will proceed as normal. If the request comes from a denied IP, you will receive an HTTP `403` response: ```bash theme={null} $ telnet 5.tcp.ngrok.io 22984 Trying... Connected to 5.tcp.ngrok.io. Connection closed by foreign host. ``` snippets/traffic-policy/actions/restrict-ips/tcp/examples/3-testing-traffic ## Examples `on_http_request` ### Restricting access with allow and deny lists The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to restrict access to specific IP addresses using the `restrict-ips` action. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - 1.1.1.1/32 deny: - e680:5791:be4c:5739:d959:7b94:6d54:d4b4/128 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "restrict-ips", "config": { "enforce": true, "allow": [ "1.1.1.1/32" ], "deny": [ "e680:5791:be4c:5739:d959:7b94:6d54:d4b4/128" ] } } ] } ] } ``` This configuration will ensure that only requests from the IP `1.1.1.1` are allowed, while requests from the IP `e680:5791:be4c:5739:d959:7b94:6d54:d4b4` are denied. #### Example request If the request comes from an allowed IP, the response will proceed as normal. If the request comes from a denied IP, you will receive an HTTP `403` response: ```bash theme={null} $ curl -i https://example.ngrok.app ``` ```http theme={null} HTTP/2 403 ``` ### Restricting access with IP Policies The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to restrict access using the `restrict-ips` action with [IP Policies](/docs/api-reference/ippolicies/list). #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: restrict-ips config: enforce: true ip_policies: - ipp_1yjqdrIBwgciY2I9zH2EelgBbJF ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "restrict-ips", "config": { "enforce": true, "ip_policies": [ "ipp_1yjqdrIBwgciY2I9zH2EelgBbJF" ] } } ] } ] } ``` This configuration will ensure that the [IP Policies](/docs/api-reference/ippolicies/list) specified `ipp_1yjqdrIBwgciY2I9zH2EelgBbJF` are enforced against incoming traffic. #### Example request If the request comes from an allowed IP, the response will proceed as normal. If the request comes from a denied IP, you will receive an HTTP `403` response: ```bash theme={null} $ curl -i https://example.ngrok.app ``` ```http theme={null} HTTP/2 403 ``` ### Test restricting IPs The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to test restricting IPs using the [`log`](/docs/gateway/traffic-policy/actions/log/) action with the `restrict-ips` action and [IP Policies](/docs/api-reference/ippolicies/list). #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: restrict-ips config: enforce: false ip_policies: - ipp_1yjqdrIBwgciY2I9zH2EelgBbJF - type: log config: metadata: message: Restrict IPs action would be ${actions.ngrok.restrict_ips.action} for ${conn.client_ip}. matched_cidr: ${actions.ngrok.restrict_ips.matched_cidr} error_code: ${actions.ngrok.restrict_ips.error.code} error_message: ${actions.ngrok.restrict_ips.error.message} ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "restrict-ips", "config": { "enforce": false, "ip_policies": [ "ipp_1yjqdrIBwgciY2I9zH2EelgBbJF" ] } }, { "type": "log", "config": { "metadata": { "message": "Restrict IPs action would be ${actions.ngrok.restrict_ips.action} for ${conn.client_ip}.", "matched_cidr": "${actions.ngrok.restrict_ips.matched_cidr}", "error_code": "${actions.ngrok.restrict_ips.error.code}", "error_message": "${actions.ngrok.restrict_ips.error.message}" } } } ] } ] } ``` This configuration will test the incoming client IP against the specified [IP Policy](/docs/api-reference/ippolicies/list) `ipp_1yjqdrIBwgciY2I9zH2EelgBbJF` without enforcing it, then log the result using the [`log`](/docs/gateway/traffic-policy/actions/log/) action. #### Example request The following request will be allowed and an event will be logged: ```bash theme={null} $ curl -i https://example.ngrok.app ``` ```http theme={null} HTTP/2 200 ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. The action taken for this request. `allow` - If the request was permitted. `deny` - If the request was denied. The CIDR block that matched the incoming client's IP address. If no match was found, this value will be empty. A machine-readable code describing an error that occurred during the action's execution. A human-readable message providing details about an error that occurred during the action's execution. # SAML Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/saml The SAML action restricts access to only authorized users by enforcing SAML through an identity provider of your choice. **Coming Soon** The SAML Traffic Policy action is not yet generally available, [request Early Access](https://dashboard.ngrok.com/early-access). # Set Vars Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/set-vars Define or update global variables in a Traffic Policy's runtime context. The **Set Vars** action is used to define or update global variables in a Traffic Policy's runtime context. Subsequent actions or expressions can reference these variables for conditional logic through expressions or dynamic interpolation. ## Configuration reference This is the [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request`, `on_http_response`, `on_tcp_connect` ### Type `set-vars` ### Configuration fields List of maps that have a key of string and a value of any valid CEL type Each map must be of exactly size `1`, and represents one variable where the key is the name of the variable: ```yaml theme={null} vars: - variable_a: value - variable_b: value ``` ## Behavior The `set-vars` action allows you to define your own global variables to be used in future actions or expressions, like this: ```yaml policy.yml {6-7, 14} theme={null} on_http_request: - actions: - type: set-vars config: vars: - variable_a: Hello, - variable_b: World! - actions: - type: custom-response config: status_code: 200 headers: content-type: text/plain body: ${vars.variable_a} ${vars.variable_b} ``` ```json policy.json {10,13,29} theme={null} { "on_http_request": [ { "actions": [ { "type": "set-vars", "config": { "vars": [ { "variable_a": "Hello," }, { "variable_b": "World!" } ] } } ] }, { "actions": [ { "type": "custom-response", "config": { "status_code": 200, "headers": { "content-type": "text/plain" }, "body": "${vars.variable_a} ${vars.variable_b}" } } ] } ] } ``` After being defined through the `set-vars` action, you can access them through CEL Interpolation or anywhere else that CEL is supported, like expressions. You can use any valid CEL type as a variable value, including strings, booleans, doubles, null, maps, and lists. ### CEL interpolation Variables also support CEL interpolation (`${}`) in values, enabling you to create dynamic values and types: ```yaml policy.yml {6} theme={null} on_http_request: - actions: - type: set-vars config: vars: - message: Your IP is ${conn.client_ip}! ``` ```json policy.json {10} theme={null} { "on_http_request": [ { "actions": [ { "type": "set-vars", "config": { "vars": [ { "message": "Your IP is ${conn.client_ip}!" } ] } } ] } ] } ``` CEL-interpolated strings that are exactly of the form `"${vars.}"` will resolve to the evaluated type. Strings that contain CEL-interpolated substrings within them such as `sample ${conn.tls.client.pem} here` will convert all CEL-interpolated substrings to strings ### Scoping Variables you define in the `set-vars` action are scoped to the Traffic Policy and are not accessible outside of it. Variables defined in each of the three phases (`on_http_request`, `on_http_response`, `on_tcp_connect`) are scoped to that phase, and cannot be accessed in other phases. ### Referencing other variables and macros CEL-interpolated strings may reference all other variables and macros available to Traffic Policies. This also includes variables previously defined in the same or previous `set-vars` actions. This allows for powerful dynamic behavior and customization of your Traffic Policy. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Basic example The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration is an example configuration of the `set-vars` action. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: set-vars config: vars: - sample_string: bar - sample_double: 1.5 - sample_bool: true - sample_null: null - sample_list: - 1 - 2 - 3 - sample_map: key: value - sample_nested_map: key: - value1 - value2 - expressions: - vars.sample_bool == true actions: - type: custom-response config: status_code: 200 headers: content-type: text/plain body: ${vars.sample_string} ${vars.sample_double} ${vars.sample_list[0]} ${vars.sample_map.key} ${vars.sample_nested_map.key[1]} ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "set-vars", "config": { "vars": [ { "sample_string": "bar" }, { "sample_double": 1.5 }, { "sample_bool": true }, { "sample_null": null }, { "sample_list": [ 1, 2, 3 ] }, { "sample_map": { "key": "value" } }, { "sample_nested_map": { "key": [ "value1", "value2" ] } } ] } } ] }, { "expressions": [ "vars.sample_bool == true" ], "actions": [ { "type": "custom-response", "config": { "status_code": 200, "headers": { "content-type": "text/plain" }, "body": "${vars.sample_string} ${vars.sample_double} ${vars.sample_list[0]} ${vars.sample_map.key} ${vars.sample_nested_map.key[1]}" } } ] } ] } ``` ### CEL interpolation example The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration is an example of using CEL-interpolation with the `set-vars` action. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: set-vars config: vars: - sample_string: 'x-foo header: ${request.headers[''x-foo'']}' - sample_uint: ${uint(1.5) + 10u} - sample_int: ${int(50.5)} - sample_map: pem: ${conn.tls.client.pem} ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "set-vars", "config": { "vars": [ { "sample_string": "x-foo header: ${request.headers['x-foo']}" }, { "sample_uint": "${uint(1.5) + 10u}" }, { "sample_int": "${int(50.5)}" }, { "sample_map": { "pem": "${conn.tls.client.pem}" } } ] } } ] } ] } ``` ### Setting vars based on previous vars example The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration is an example of setting vars based on previous vars with the `set-vars` action. Variables are determined in the order in which they are listed in the configuration list. #### Example Traffic Policy document ```yaml policy.yml {4-28} theme={null} on_http_request: - actions: - type: set-vars config: vars: - accepted_models: - openai/o1 - openai/4o - openai/4o-mini - models: ${actions.ngrok.http_request.res.body} - models: ${vars.models.filter(model, model.id in vars.accepted_models)} - models: ${vars.models.filter(model, model.latency < 100)} - models: ${vars.models.sort(x, y, x.latency < y.latency)} ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "set-vars", "config": { "vars": [ { "accepted_models": [ "openai/o1", "openai/4o", "openai/4o-mini" ] }, { "models": "${actions.ngrok.http_request.res.body}" }, { "models": "${vars.models.filter(model, model.id in vars.accepted_models)}" }, { "models": "${vars.models.filter(model, model.latency < 100)}" }, { "models": "${vars.models.sort(x, y, x.latency < y.latency)}" } ] } } ] } ] } ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "set-vars", "config": { "vars": [ { "accepted_models": [ "openai/o1", "openai/4o", "openai/4o-mini" ] }, { "models": "${actions.ngrok.http_request.res.body}" }, { "models": "${vars.models.filter(model, model.id in vars.accepted_models)}" }, { "models": "${vars.models.filter(model, model.latency < 100)}" }, { "models": "${vars.models.sort(x, y, x.latency < y.latency)}" } ] } } ] } ] } ``` # SSE Find & Replace Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/sse-find-replace Find and replace text patterns in Server-Sent Events (SSE) streams. The SSE Find & Replace action enables you to modify Server-Sent Events stream content by finding and replacing text patterns using regular expressions. This is particularly useful for modifying streaming AI model responses in real-time, such as redacting sensitive information or transforming content as it streams to clients. ## Configuration reference The [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_event_stream_message` ### Type `sse-find-replace` ### Configuration fields Array of replacement rules to apply to SSE messages. Rules are applied in order. Minimum `1` replacement required. The SSE field to apply the replacement to. Valid values are `data` and `retry`. Defaults to `data` if not specified. CEL interpolation is supported. Regular expression pattern to match. Supports [RE2 syntax](https://github.com/google/re2/wiki/Syntax). CEL interpolation is supported for dynamic patterns. Replacement string. Use `$1`, `$2`, etc. to reference capture groups from the pattern. CEL interpolation is supported for dynamic replacements. If omitted or empty, matched text is deleted. ## Behavior When this action executes, it processes each SSE message as it streams through ngrok, applying replacement rules to the specified fields. ### SSE message structure Server-Sent Events consist of messages with fields like `data`, `event`, `id`, and `retry`. This action currently supports modifying the `data` and `retry` fields. ### Pattern matching The `from` field accepts [RE2 regular expressions](https://github.com/google/re2/wiki/Syntax). All matches within the specified field are replaced, not just the first occurrence. ### Streaming behavior Unlike the request/response body find & replace actions, this action processes messages in real-time as they stream through. Each SSE message is processed independently. ### Capture groups You can use capture groups in your pattern and reference them in the replacement: ```yaml theme={null} replacements: - field: data from: '"sensitive_id":\s*"([^"]+)"' to: '"sensitive_id": "[REDACTED]"' ``` ### CEL expressions The `field`, `from`, and `to` parameters all support CEL interpolation for dynamic values: ```yaml theme={null} replacements: - field: "${vars.target_field}" from: "${vars.pattern}" to: "${vars.replacement}" ``` When using CEL in the `from` field, the resulting string must be a valid regular expression. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Redacting sensitive data from streaming AI responses When using streaming AI responses (like OpenAI's chat completions with `stream: true`), you can redact sensitive information in real-time. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_event_stream_message: - actions: - type: sse-find-replace config: replacements: # Redact SSN patterns in streaming content - field: data from: "\\d{3}-\\d{2}-\\d{4}" to: "[SSN REDACTED]" # Redact email addresses - field: data from: "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}" to: "[EMAIL REDACTED]" ``` ```json policy.json theme={null} { "on_event_stream_message": [ { "actions": [ { "type": "sse-find-replace", "config": { "replacements": [ { "field": "data", "from": "\\d{3}-\\d{2}-\\d{4}", "to": "[SSN REDACTED]" }, { "field": "data", "from": "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "to": "[EMAIL REDACTED]" } ] } } ] } ] } ``` ### Modifying streaming AI output format Transform streaming content as it's delivered to clients: ```yaml policy.yml theme={null} on_event_stream_message: - actions: - type: sse-find-replace config: replacements: # Add emphasis to certain keywords - field: data from: "\\b(IMPORTANT|WARNING|NOTE)\\b" to: "**$1**" ``` ```json policy.json theme={null} { "on_event_stream_message": [ { "actions": [ { "type": "sse-find-replace", "config": { "replacements": [ { "field": "data", "from": "\\b(IMPORTANT|WARNING|NOTE)\\b", "to": "**$1**" } ] } } ] } ] } ``` ### Filtering profanity from streaming responses Remove or replace inappropriate content from AI responses in real-time: ```yaml policy.yml theme={null} on_event_stream_message: - actions: - type: sse-find-replace config: replacements: - field: data from: "\\b(badword1|badword2|badword3)\\b" to: "[filtered]" ``` ```json policy.json theme={null} { "on_event_stream_message": [ { "actions": [ { "type": "sse-find-replace", "config": { "replacements": [ { "field": "data", "from": "\\b(badword1|badword2|badword3)\\b", "to": "[filtered]" } ] } } ] } ] } ``` ### Modifying retry behavior Change the retry interval in SSE streams: ```yaml policy.yml theme={null} on_event_stream_message: - actions: - type: sse-find-replace config: replacements: - field: retry from: "\\d+" to: "5000" ``` ```json policy.json theme={null} { "on_event_stream_message": [ { "actions": [ { "type": "sse-find-replace", "config": { "replacements": [ { "field": "retry", "from": "\\d+", "to": "5000" } ] } } ] } ] } ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. Array of objects describing which replacement rules matched. The zero-based index of the replacement rule that matched. The first matched content from this replacement rule. ## Related actions * [Request Body Find & Replace](/docs/gateway/traffic-policy/actions/request-body-find-replace) - Modify request bodies * [Response Body Find & Replace](/docs/gateway/traffic-policy/actions/response-body-find-replace) - Modify response bodies # Terminate TLS Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/terminate-tls Control how TLS traffic is terminated by ngrok, including custom certificates and mutual TLS. The **Terminate TLS** Traffic Policy action allows you to control how TLS traffic is terminated by ngrok. This action is useful for when you need to specify a custom certificate, control which TLS versions are supported, or enable mutual TLS authentication. ## Configuration reference This is the [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Action type `terminate-tls` ### Configuration fields The minimum TLS version to support. Must be one of `1.2` or `1.3`. The maximum TLS version to support. Must be one of `1.2` or `1.3`. The PEM-encoded private key for the server if using a custom certificate (must be specified with `server_certificate`). The PEM-encoded certificate for the server if using a custom certificate (must be specified with `server_private_key`). A list of PEM-encoded Certificate Authority certificates and/or Certificate Authority IDs that are trusted for mutual TLS authentication. The strategy to use for mutual TLS verification. Require and verify Require any Request ### Supported phases * `on_tcp_connect` ### Supported schemes * `https` * `tls` ## Behavior For HTTPS endpoints, ngrok will already terminate TLS connections for you even if you do not explicitly use this action in your Traffic Policy. If you specify this action in your Traffic Policy without any configuration, you will see no change in behavior for your endpoints. For TLS endpoints, ngrok will not terminate the TLS connection by default and it is up to you to handle TLS termination in your upstream service. ### Changing supported TLS versions You can use this action to specify the minimum and maximum TLS versions that your endpoint will support for incoming connections. By default, your endpoint will support TLS versions 1.2 and 1.3. If you want to support different versions of TLS, you can use the `min_version` and `max_version` fields in the configuration. Clients using an unsupported version of TLS will receive a handshake error. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Minimum and maximum TLS versions This example sets the minimum and maximum TLS versions that the endpoint will support for incoming connections to TLS version 1.3. Clients can then only connect to the endpoint using TLS version 1.3 and will receive a handshake error if they attempt to connect using a different version. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_tcp_connect: - actions: - type: terminate-tls config: min_version: '1.3' max_version: '1.3' ``` ```json policy.json theme={null} { "on_tcp_connect": [ { "actions": [ { "type": "terminate-tls", "config": { "min_version": "1.3", "max_version": "1.3" } } ] } ] } ``` #### Start an endpoint with Traffic Policy ```bash theme={null} ngrok http 8080 --url https://terminate-tls-example.ngrok.app --traffic-policy-file /path/to/policy.yml ``` #### Make a request ```bash theme={null} curl https://terminate-tls-example.ngrok.app ``` If your curl was built with a version of OpenSSL that supports TLS 1.3, the request will succeed. However, you can verify that the endpoint only supports TLS 1.3 by telling curl to use a different version of TLS: ```bash theme={null} curl https://terminate-tls-example.ngrok.app --tlsv1.2 --tls-max 1.2 ``` You should receive a "alert protocol version" error indicating that the endpoint only supports TLS 1.3. ### Using a custom certificate If you want to specify a custom certificate for your endpoints instead of having ngrok manage the certificate for you, you can use the `server_private_key` and `server_certificate` fields in the configuration. This will allow you to have full control over which certificate is used for your endpoint. #### Generate certificates Create a new Certificate Authority (CA) that will be used to sign the server certificate. This allows you to generate multiple server certificates that are trusted by the CA if needed. Note that this generates a self-signed certificate, so if you would like to use a custom certificate that is trusted by modern operating systems and browsers you will need to use a trusted CA (for example, LetsEncrypt). ```bash theme={null} openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:2048 ``` ```bash theme={null} openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=ExampleCA" ``` ```bash theme={null} openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:2048 ``` ```bash theme={null} openssl req -new -key server.key -out server.csr -subj "/CN=terminate-tls-example.ngrok.app" ``` Change `terminate-tls-example.ngrok.app` to the domain you are using with your endpoint ```bash theme={null} openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 -sha256 ``` These commands will result in five new files: `ca.key`, `ca.crt`, `server.key`, `server.csr`, and `server.key`. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_tcp_connect: - actions: - type: terminate-tls config: server_private_key: |- -----BEGIN PRIVATE KEY----- ... private key ... -----END PRIVATE KEY----- server_certificate: |- -----BEGIN CERTIFICATE----- ... certificate ... -----END CERTIFICATE----- ``` ```json policy.json theme={null} { "on_tcp_connect": [ { "actions": [ { "type": "terminate-tls", "config": { "server_private_key": "-----BEGIN PRIVATE KEY-----\n... private key ...\n-----END PRIVATE KEY-----", "server_certificate": "-----BEGIN CERTIFICATE-----\n... certificate ...\n-----END CERTIFICATE-----" } } ] } ] } ``` Replace the contents of `server_private_key` with the contents of `server.key` and `server_certificate` with `server.crt` respectively in the Traffic Policy to enable your custom certificate on your endpoint and `ca.crt` to trust the certificate when using `curl`. #### Start an endpoint with Traffic Policy ```bash theme={null} ngrok http 8080 --url https://terminate-tls-example.ngrok.app --traffic-policy-file /path/to/policy.yml ``` #### Make a request Now you can make a request to the endpoint with the `--cacert` flag to specify the CA certificate that was generated. ```bash theme={null} curl --cacert ca.crt https://terminate-tls-example.ngrok.app ``` If you have a service running on port `8080`, your request will be forwarded to that service. If you don't have any service running on that port, ngrok will return an error page but the certificate setup will have worked. ### Enabling mutual TLS This example demonstrates how to use mutual TLS (mTLS) with this action. mTLS requires both the client and server to present certificates to each other to establish a secure connection. This example will show you how to generate a custom Certificate Authority (CA) and client certificate to use. #### Generate certificates Create a new CA that will be used to sign the client certificate. This allows you to generate multiple client certificates that are trusted by the CA if needed. ```bash theme={null} # 1. Generate CA private key (ca.key) openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:2048 # 2. Generate CA certificate (ca.crt) openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=ExampleCA" # 3. Generate client private key (client.key) openssl genpkey -algorithm RSA -out client.key -pkeyopt rsa_keygen_bits:2048 # 4. Generate client certificate signing request (CSR) (client.csr) openssl req -new -key client.key -out client.csr -subj "/CN=ExampleClient" # 5. Generate client certificate (client.crt) openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256 ``` These commands will result in five new files: `ca.key`, `ca.crt`, `client.key`, `client.csr`, and `client.key`. You will use the contents of `ca.crt` in your Traffic Policy to validate requests via `curl` which will use `client.key` and `client.crt`. #### Example Traffic Policy document Using the CA certificate generated above, you can specify the `mutual_tls_certificate_authorities` field in the Traffic Policy to trust the CA that issued the client certificate. ```yaml policy.yml theme={null} on_tcp_connect: - actions: - type: terminate-tls config: mutual_tls_certificate_authorities: - |- -----BEGIN CERTIFICATE----- ... certificate ... -----END CERTIFICATE----- ``` ```json policy.json theme={null} { "on_tcp_connect": [ { "actions": [ { "type": "terminate-tls", "config": { "mutual_tls_certificate_authorities": [ "-----BEGIN CERTIFICATE-----\n... certificate ...\n-----END CERTIFICATE-----" ] } } ] } ] } ``` You may also optionally [upload the CA certificate](https://dashboard.ngrok.com/tls-cert-authorities) in the ngrok dashboard and use its ID the `mutual_tls_certificate_authorities` array. #### Start an endpoint with Traffic Policy ```bash theme={null} ngrok http 8080 --url terminate-tls-example.ngrok.app --traffic-policy-file /path/to/policy.yml ``` #### Make a request Now you can make a request to the endpoint with the `--cert` and `--key` flags to specify the client certificate and private key. ```bash theme={null} curl --cert client.crt --key client.key https://terminate-tls-example.ngrok.app ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. | Name | Type | Description | | - | - | - | | `actions.ngrok.terminate_tls.cipher_suite` | `string` | The cipher suite selected during the TLS handshake. | | `actions.ngrok.terminate_tls.client.extensions[i].critical` | `bool` | True if the extension is critical. | | `actions.ngrok.terminate_tls.client.extensions[i].id` | `string` | The identifier (OID) that specifies the type of extension. | | `actions.ngrok.terminate_tls.client.extensions[i].value` | `[]byte` | The data for the extension. | | `actions.ngrok.terminate_tls.client.extensions` | `[]Extension` | Additional information added to the certificate. | | `actions.ngrok.terminate_tls.client.issuer.common_name` | `string` | Common name of the issuing authority, usually the domain name. | | `actions.ngrok.terminate_tls.client.issuer.country` | `[]string` | Country names where the issuing authority is located. | | `actions.ngrok.terminate_tls.client.issuer.locality` | `[]string` | Locality or city of the issuing authority. | | `actions.ngrok.terminate_tls.client.issuer.organization` | `[]string` | Name of the organization that issued the certificate. | | `actions.ngrok.terminate_tls.client.issuer.organizational_unit` | `[]string` | Division of the organization responsible for the certificate. | | `actions.ngrok.terminate_tls.client.issuer.postal_code` | `[]string` | Postal code of the issuing authority. | | `actions.ngrok.terminate_tls.client.issuer.province` | `[]string` | Province or state of the issuing authority. | | `actions.ngrok.terminate_tls.client.issuer.street_address` | `[]string` | Street address of the issuing authority. | | `actions.ngrok.terminate_tls.client.issuer` | `string` | The issuing authority of the certificate as a string roughly following the RFC 2253 Distinguished Names syntax. | | `actions.ngrok.terminate_tls.client.san.dns_names` | `[]string` | DNS names in the subject alternative names. | | `actions.ngrok.terminate_tls.client.san.email_addresses` | `[]string` | Email addresses in the subject alternative names. | | `actions.ngrok.terminate_tls.client.san.ip_addresses` | `[]string` | IP addresses in the subject alternative names. | | `actions.ngrok.terminate_tls.client.san.uris` | `[]string` | URIs in the subject alternative names. | | `actions.ngrok.terminate_tls.client.san` | `string` | Subject alternative names of the client certificate. | | `actions.ngrok.terminate_tls.client.serial_number` | `string` | Unique identifier for the certificate. | | `actions.ngrok.terminate_tls.client.signature_algorithm` | `string` | Algorithm used to sign the certificate. | | `actions.ngrok.terminate_tls.client.subject.common_name` | `string` | Common name of the subject, usually the domain name. | | `actions.ngrok.terminate_tls.client.subject.country` | `[]string` | Country names where the subject of the certificate is located. | | `actions.ngrok.terminate_tls.client.subject.locality` | `[]string` | Locality or city where the subject is located. | | `actions.ngrok.terminate_tls.client.subject.organization` | `[]string` | Name of the organization to which the subject belongs. | | `actions.ngrok.terminate_tls.client.subject.organizational_unit` | `[]string` | Division of the organization to which the subject belongs. | | `actions.ngrok.terminate_tls.client.subject.postal_code` | `[]string` | Postal code where the subject is located. | | `actions.ngrok.terminate_tls.client.subject.province` | `[]string` | Province or state where the subject is located. | | `actions.ngrok.terminate_tls.client.subject.street_address` | `[]string` | Street address where the subject is located. | | `actions.ngrok.terminate_tls.client.subject` | `string` | The entity to whom the certificate is issued as a string roughly following the RFC 2253 Distinguished Names syntax. | | `actions.ngrok.terminate_tls.client.validity.not_after` | `timestamp` | Expiration date and time when the certificate is no longer valid. | | `actions.ngrok.terminate_tls.client.validity.not_before` | `timestamp` | Start date and time when the certificate becomes valid. | | `actions.ngrok.terminate_tls.error.code` | `string` | The error code if the action encountered any errors during its execution. | | `actions.ngrok.terminate_tls.error.message` | `string` | The error message if the action encountered any errors during its execution. | | `actions.ngrok.terminate_tls.server.extensions[i].critical` | `bool` | True if the extension is critical. | | `actions.ngrok.terminate_tls.server.extensions[i].id` | `string` | The identifier that specifies the type of extension. | | `actions.ngrok.terminate_tls.server.extensions[i].value` | `[]byte` | The data for the extension. | | `actions.ngrok.terminate_tls.server.extensions` | `[]Extension` | Additional information added to the certificate. | | `actions.ngrok.terminate_tls.server.issuer.common_name` | `string` | Common name of the issuing authority, usually the domain name. | | `actions.ngrok.terminate_tls.server.issuer.country` | `[]string` | Country names where the issuing authority is located. | | `actions.ngrok.terminate_tls.server.issuer.locality` | `[]string` | Locality or city of the issuing authority. | | `actions.ngrok.terminate_tls.server.issuer.organization` | `[]string` | Name of the organization that issued the certificate. | | `actions.ngrok.terminate_tls.server.issuer.organizational_unit` | `[]string` | Division of the organization responsible for the certificate. | | `actions.ngrok.terminate_tls.server.issuer.postal_code` | `[]string` | Postal code of the issuing authority. | | `actions.ngrok.terminate_tls.server.issuer.province` | `[]string` | Province or state of the issuing authority. | | `actions.ngrok.terminate_tls.server.issuer.street_address` | `[]string` | Street address of the issuing authority. | | `actions.ngrok.terminate_tls.server.issuer` | `string` | The issuing authority of the certificate as a string roughly following the RFC 2253 Distinguished Names syntax. | | `actions.ngrok.terminate_tls.server.san.dns_names` | `[]string` | DNS names in the subject alternative names of the ngrok server's leaf TLS certificate. | | `actions.ngrok.terminate_tls.server.san.email_addresses` | `[]string` | Email addresses in the subject alternative names of the ngrok server's leaf TLS certificate. | | `actions.ngrok.terminate_tls.server.san.ip_addresses` | `[]string` | IP addresses in the subject alternative names of the ngrok server's leaf TLS certificate. | | `actions.ngrok.terminate_tls.server.san.uris` | `[]string` | URIs in the subject alternative names of the ngrok server's leaf TLS certificate. | | `actions.ngrok.terminate_tls.server.san` | `string` | Subject alternative names of the ngrok server's leaf TLS certificate. | | `actions.ngrok.terminate_tls.server.serial_number` | `string` | Unique identifier for the certificate. | | `actions.ngrok.terminate_tls.server.signature_algorithm` | `string` | Algorithm used to sign the certificate. | | `actions.ngrok.terminate_tls.server.subject.common_name` | `string` | Common name of the subject, usually the domain name. | | `actions.ngrok.terminate_tls.server.subject.country` | `[]string` | Country names where the subject of the certificate is located. | | `actions.ngrok.terminate_tls.server.subject.locality` | `[]string` | Locality or city where the subject is located. | | `actions.ngrok.terminate_tls.server.subject.organization` | `[]string` | Name of the organization to which the subject belongs. | | `actions.ngrok.terminate_tls.server.subject.organizational_unit` | `[]string` | Division of the organization to which the subject belongs. | | `actions.ngrok.terminate_tls.server.subject.postal_code` | `[]string` | Postal code where the subject is located. | | `actions.ngrok.terminate_tls.server.subject.province` | `[]string` | Province or state where the subject is located. | | `actions.ngrok.terminate_tls.server.subject.street_address` | `[]string` | Street address where the subject is located. | | `actions.ngrok.terminate_tls.server.subject` | `string` | The entity to whom the certificate is issued as a string roughly following the RFC 2253 Distinguished Names syntax. | | `actions.ngrok.terminate_tls.server.validity.not_after` | `timestamp` | Expiration date and time when the certificate is no longer valid. | | `actions.ngrok.terminate_tls.server.validity.not_before` | `timestamp` | Start date and time when the certificate becomes valid. | | `actions.ngrok.terminate_tls.sni` | `string` | The hostname included in the `ClientHello` message via the SNI extension. | | `actions.ngrok.terminate_tls.version` | `string` | The version of the TLS protocol used between the client and the ngrok edge. | | `actions.ngrok.terminate_tls.warnings` | `[]string` | A list of any warnings encountered during the parsing of Certificate Authority IDs. | These variables are also available on the `conn` object (see [HTTP Variables](/docs/gateway/traffic-policy/variables) for examples). # URL Rewrite Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/url-rewrite Modify the incoming request URL using regular expressions before it reaches the upstream server, while keeping the URL seen by the client unchanged. The **URL Rewrite** Traffic Policy action allows you to modify the incoming request URL using regular expressions before it reaches the upstream server, while keeping the URL seen by the client unchanged. This action is particularly useful for routing users without exposing internal system details. ## Configuration reference This is the [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported phases `on_http_request` ### Type `url-rewrite` ### Configuration fields

A regular expression pattern used to match a part of the URL.

Supports [CEL Interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation).

A regular expression pattern used to replace the matched part of the URL.

Supports [CEL Interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation).

## Behavior This action replaces all occurrences of the `from` regular expression in the request URL with the `to` replacement value. ### Matching Matching is performed against the entire URL, including the scheme, userinfo, host, and port, not just the path. To match requests that begin with `/products`, use [CEL Interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation) and write `^${req.url.authority}/products`, or use an expression like `req.url.path.startsWith('/products')` in the phase rule's `expressions` field. ### Note about CEL interpolation All CEL Interpolation will occur **before** regular expressions are evaluated. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Examples ### Rewrite using paths The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `url-rewrite` action to transparently rewrite the request URL from `/products` to `/products.php`. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - expressions: - req.url.path == '/products' actions: - type: url-rewrite config: from: /products to: /products.php ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.url.path == '/products'" ], "actions": [ { "type": "url-rewrite", "config": { "from": "/products", "to": "/products.php" } } ] } ] } ``` This configuration will rewrite any request to `/products` to `/products.php` without changing the URL seen by the client. This is useful for hiding away the underlying implementation details of your service. #### Example request ```bash theme={null} $ curl -i https://example.ngrok.app/products ``` ```http theme={null} HTTP/2 200 OK ``` ##### Example server logs ```http theme={null} GET /product.php 200 OK ``` #### Conclusion In this example, a request to `/products` is internally routed to `/products.php`, and the response is served from `products.php` while the client remains unaware of the URL rewrite. ### Rewrite using regular expressions The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `url-rewrite` action to transparently rewrite the request URL from `/products/*` to `/products.php`. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - expressions: - req.url.path.startsWith('/products') actions: - type: url-rewrite config: from: /products/?([.*]+)? to: /products.php?query=$1 ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.url.path.startsWith('/products')" ], "actions": [ { "type": "url-rewrite", "config": { "from": "/products/?([.*]+)?", "to": "/products.php?query=$1" } } ] } ] } ``` This configuration will rewrite any request to `/products/*` to `/products.php?query=$1` without changing the URL seen by the client. This is useful for hiding away the underlying implementation details of your service. #### Example request ```bash theme={null} $ curl -i https://example.ngrok.app/products/123 ``` ```http theme={null} HTTP/2 200 OK ``` ##### Example server logs ```http theme={null} GET /product.php?query=123 200 OK ``` #### Conclusion In this example, a request to `/products` is internally routed to `/products.php?query=123`, and the response is served from `products.php` while the client remains unaware of the URL rewrite. ### Rewrite using CEL interpolation The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration demonstrates how to use the `url-rewrite` action to transparently rewrite the request URL from `/products/*` to `/products.php` using a global policy rule (no expression) by leveraging [CEL Interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation). #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: url-rewrite config: from: ${req.url.authority}/products/?([.*]+)? to: /products.php?query=$1 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "url-rewrite", "config": { "from": "${req.url.authority}/products/?([.*]+)?", "to": "/products.php?query=$1" } } ] } ] } ``` This configuration will rewrite any request urls that start with `/products/*` to `/products.php?query=$1` without changing the URL seen by the client. It does this by leveraging [CEL Interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation) to replace the `from` value with the request URL authority. This is useful for hiding away the underlying implementation details of your service. #### Example request ```bash theme={null} $ curl -i https://example.ngrok.app/products/123 ``` ```http theme={null} HTTP/2 200 OK ``` ##### Example server logs ```http theme={null} GET /product.php?query=123 200 OK ``` #### Conclusion In this example, a request to `/products` is internally routed to `/products.php?query=123`, and the response is served from `products.php` while the client remains unaware of the URL rewrite. ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated.

List of elements that matched the URL before the rewrite action was applied. These can be specific parts of the URL, such as the domain, path, or query parameters, that were matched based on the action configuration.

The final URL after the rewrite action has been applied. This is the new URL to which the original request is redirected after the specified modifications have been made.

A machine-readable code describing an error that occurred during the action's execution.

A human-readable message providing details about an error that occurred during the action's execution.

# Verify Webhook Action Source: https://ngrok.com/docs/gateway/traffic-policy/actions/verify-webhook Validate incoming webhook signatures against a known secret to ensure authenticity. The **Verify Webhook** Traffic Policy action enables you to validate incoming webhook signatures against a known secret to ensure authenticity. Depending on the verification result, it either forwards the request to the next action or rejects it, safeguarding your endpoints from unauthorized or tampered webhooks. ## Configuration reference This is the [Traffic Policy](/docs/gateway/traffic-policy/) configuration reference for this action. ### Supported directions * `on_http_request` ### Type `verify-webhook` ### Configuration fields

The name of the provider to verify webhook requests from.

Value must be a [supported provider](#supported-providers) identifier. Don't see your provider? [Request support for it here](https://dashboard.ngrok.com/early-access#webhook-providers).

The secret key used to validate webhook requests from the specified provider.

Supports [CEL Interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation).

When `true`, the request will be halted if the webhook is not valid and no further actions will run. However when `false`, subsequent actions will run even if the webhook was not valid.

Default true.

## Behavior The **verify-webhook** action ensures the authenticity of incoming webhook requests by validating their signatures against a known secret. Upon receiving a request, the action performs the signature verification. If verification succeeds, the request proceeds through the action chain. If it fails, the request is terminated with a `403 Forbidden` response, unless `enforce` is set to `false`, in which case the request proceeds without termination. ### Verification process * **Signature Validation**: The action validates incoming webhook signature to confirm the request originates from the configured provider and that the payload has not been tampered with. * **Request Handling**: If the webhook verification is successful, the request is forwarded to the next action. If the verification fails, the request chain is terminated with a `403` response. * **Configurable Enforcement**: By default, verification failures result in termination. However, setting `enforce: false` allows unverified requests to proceed, while logging the verification result. This option is useful for debugging, testing, and crafting your own custom responses with action result variables. ### Endpoint verification Some webhook providers require an initial endpoint verification challenge to validate that your application is legitimate before sending webhook events. The **verify-webhook** action automatically handles endpoint verification challenges for supported providers. * Supported providers: * Twitter * Worldline * Xero * Zoom #### Replay prevention with timestamp tolerance To prevent replay attacks, ngrok verifies that the webhook’s timestamp falls within an acceptable range. #### Secret handling and encryption All secrets used for webhook verification are encrypted at config validation. When ngrok processes a requests the secret is decrypted. ### Non-terminating action This is a **Non-terminating action**. It does not return a response, and will allow Traffic Policy processing to continue to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. ## Supported providers Currently, these integration guides refer to modules. | Provider | Provider Identifier | Integration Guide | | - | - | - | | AfterShip | `aftership` | [Documentation](/docs/integrations/webhooks/aftership-webhooks) | | Airship | `airship` | [Documentation](/docs/integrations/webhooks/airship-webhooks) | | Alchemy | `alchemy` | [Documentation](https://docs.alchemy.com/reference/notify-api-quickstart) | | Amazon SNS | `sns` | [Documentation](/docs/integrations/webhooks/amazon-sns-webhooks) | | Autodesk Platform Services | `autodesk` | [Documentation](/docs/integrations/webhooks/autodesk-webhooks) | | Bitbucket | `bitbucket` | [Documentation](/docs/integrations/webhooks/bitbucket-webhooks) | | Bolt | `bolt` | [Documentation](https://help.bolt.com/developers/guides/webhooks/hook-verification/) | | Box | `box` | [Documentation](/docs/integrations/webhooks/box-webhooks) | | Brex | `brex` | [Documentation](/docs/integrations/webhooks/brex-webhooks) | | Buildkite | `buildkite` | [Documentation](/docs/integrations/webhooks/buildkite-webhooks) | | Calendly | `calendly` | [Documentation](/docs/integrations/webhooks/calendly-webhooks) | | Castle | `castle` | [Documentation](/docs/integrations/webhooks/castle-webhooks) | | Chargify | `chargify` | [Documentation](/docs/integrations/webhooks/chargify-webhooks) | | CircleCI | `circleci` | [Documentation](/docs/integrations/webhooks/circleci-webhooks) | | Clearbit | `clearbit` | Provider no longer available | | Clerk | `clerk` | [Documentation](/docs/integrations/webhooks/clerk-webhooks) | | Coinbase | `coinbase` | [Documentation](/docs/integrations/webhooks/coinbase-webhooks) | | Contentful | `contentful` | [Documentation](/docs/integrations/webhooks/contentful-webhooks) | | DocuSign | `docusign` | [Documentation](/docs/integrations/webhooks/docusign-webhooks) | | Dropbox | `dropbox` | [Documentation](/docs/integrations/webhooks/dropbox-webhooks) | | Facebook Graph API | `facebook_graph_api` | [Documentation](/docs/integrations/webhooks/facebook-webhooks) | | Facebook Messenger | `facebook_messenger` | [Documentation](/docs/integrations/webhooks/facebook-messenger-webhooks) | | Frame.io | `frameio` | [Documentation](/docs/integrations/webhooks/frameio-webhooks) | | GitHub | `github` | [Documentation](/docs/integrations/webhooks/github-webhooks) | | GitLab | `gitlab` | [Documentation](/docs/integrations/webhooks/gitlab-webhooks) | | Go1 | `go1` | [Documentation](https://www.go1.com/developers/partners/concepts/webhook-signature-authentification) | | Heroku | `heroku` | [Documentation](/docs/integrations/webhooks/heroku-webhooks) | | Hosted Hooks | `hostedhooks` | [Documentation](/docs/integrations/webhooks/hostedhooks-webhooks) | | HubSpot | `hubspot` | [Documentation](/docs/integrations/webhooks/hubspot-webhooks) | | Hygraph (Formerly GraphCMS) | `graphcms` | [Documentation](/docs/integrations/webhooks/hygraph-webhooks) | | Instagram | `instagram` | [Documentation](/docs/integrations/webhooks/instagram-webhooks) | | Intercom | `intercom` | [Documentation](/docs/integrations/webhooks/intercom-webhooks) | | Jira | `jira` | [Documentation](/docs/integrations/webhooks/jira-webhooks) | | Launch Darkly | `launch_darkly` | [Documentation](/docs/integrations/webhooks/launchdarkly-webhooks) | | Linear | `linear` | [Documentation](/docs/integrations/webhooks/linear-webhooks) | | Mailchimp | `mailchimp` | [Documentation](/docs/integrations/webhooks/mailchimp-webhooks) | | Mailgun | `mailgun` | [Documentation](/docs/integrations/webhooks/mailgun-webhooks) | | Microsoft Teams | `microsoft_teams` | [Documentation](/docs/integrations/webhooks/teams-webhooks) | | Modern Treasury | `modern_treasury` | [Documentation](/docs/integrations/webhooks/modern-treasury-webhooks) | | MongoDB | `mongodb` | [Documentation](/docs/integrations/webhooks/mongodb-webhooks) | | Mux | `mux` | [Documentation](/docs/integrations/webhooks/mux-webhooks) | | Orb | `orb` | [Documentation](https://developer.withorb.com/docs/orb-docs/webhooks) | | Orbit | `orbit` | Provider no longer available | | PagerDuty | `pagerduty` | [Documentation](/docs/integrations/webhooks/pagerduty-webhooks) | | Pinwheel | `pinwheel` | [Documentation](/docs/integrations/webhooks/pinwheel-webhooks) | | Plivo | `plivo` | [Documentation](/docs/integrations/webhooks/plivo-webhooks) | | Pusher | `pusher` | [Documentation](/docs/integrations/webhooks/pusher-webhooks) | | SendGrid | `sendgrid` | [Documentation](/docs/integrations/webhooks/sendgrid-webhooks) | | Sentry | `sentry` | [Documentation](/docs/integrations/webhooks/sentry-webhooks) | | Shopify | `shopify` | [Documentation](/docs/integrations/webhooks/shopify-webhooks) | | Signal Sciences | `signal_sciences` | [Documentation](/docs/integrations/webhooks/signalsciences-webhooks) | | Slack | `slack` | [Documentation](/docs/integrations/webhooks/slack-webhooks) | | Sonatype Nexus | `sonatype` | [Documentation](/docs/integrations/webhooks/sonatype-nexus-webhooks) | | Square | `square` | [Documentation](/docs/integrations/webhooks/square-webhooks) | | Stripe | `stripe` | [Documentation](/docs/integrations/webhooks/stripe-webhooks) | | Svix | `svix` | [Documentation](/docs/integrations/webhooks/svix-webhooks) | | Terraform | `terraform` | [Documentation](/docs/integrations/webhooks/terraform-webhooks) | | TikTok | `tiktok` | [Documentation](/docs/integrations/webhooks/tiktok-webhooks) | | Trend Micro Conformity | `trendmicro_conformity` | [Documentation](/docs/integrations/webhooks/trendmicro-webhooks) | | Twilio | `twilio` | [Documentation](/docs/integrations/webhooks/twilio-webhooks) | | Twitter | `twitter` | [Documentation](/docs/integrations/webhooks/twitter-webhooks) | | Typeform | `typeform` | [Documentation](/docs/integrations/webhooks/typeform-webhooks) | | VMware Workspace | `vmware` | [Documentation](/docs/integrations/webhooks/vmware-webhooks) | | Webex | `webex` | [Documentation](/docs/integrations/webhooks/webex-webhooks) | | WhatsApp | `whatsapp` | [Documentation](/docs/integrations/webhooks/whatsapp-webhooks) | | Worldline | `worldline` | [Documentation](/docs/integrations/webhooks/worldline-webhooks) | | Xero | `xero` | [Documentation](/docs/integrations/webhooks/xero-webhooks) | | Zendesk | `zendesk` | [Documentation](/docs/integrations/webhooks/zendesk-webhooks) | | Zoom | `zoom` | [Documentation](/docs/integrations/webhooks/zoom-webhooks) | If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). ## Examples ### Basic example This example configuration sets up an endpoint (`gitlab-webhook-example.ngrok.app`) that receives webhook requests from GitLab. The **Verify Webhook** action checks the authenticity of the request using a shared secret. If the request is verified, a custom response is sent back with a status `200 OK` and a plain text confirmation message. #### Example Traffic Policy document ```yaml policy.yml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: gitlab secret: secret! - type: custom-response config: status_code: 200 headers: content-type: text/plain body: GitLab webhook verified ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "verify-webhook", "config": { "provider": "gitlab", "secret": "secret!" } }, { "type": "custom-response", "config": { "status_code": 200, "headers": { "content-type": "text/plain" }, "body": "GitLab webhook verified" } } ] } ] } ``` #### Start endpoint with Traffic Policy ```bash theme={null} ngrok http 8080 --url gitlab-webhook-example.ngrok.app --traffic-policy-file /path/to/policy.yml ``` ```bash theme={null} $ curl --location --request POST 'https://gitlab-webhook-example.ngrok.app/' \ --header 'X-Gitlab-Token: secret!' > POST / HTTP/2 > Host: gitlab-webhook-example.ngrok.app > User-Agent: curl/[version] > Accept: */* > X-Gitlab-Token: secret! ... ``` This request will first be processed by the Verify Webhook action. If the GitLab webhook verification is successful, ngrok will return a `200 OK` response with the message GitLab webhook verified. ```bash theme={null} HTTP/2 200 OK content-type: text/plain GitLab webhook verified ``` ## Action result variables The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated.

Indicates whether or not the request was successfully verified.

Code for an error that occurred during the invocation of an action.

Message for an error that occurred during the invocation of an action.

# Actions Source: https://ngrok.com/docs/gateway/traffic-policy/concepts/actions Learn about Traffic Policy Actions that define what happens when rules are matched, including traffic manipulation and routing Actions. In Traffic Policy, Actions define what happens when a [Rule](/docs/gateway/traffic-policy/how-it-works#phase-rules) on a [Phase](/docs/gateway/traffic-policy/how-it-works#phases) conditions are met. Actions allow you to manipulate, route, and manage traffic in various ways. [View a list of available Actions →](/docs/gateway/traffic-policy/actions) ## Non-terminating actions Some Actions can short-circuit a request and immediately return a response. These are called **terminating actions**. **Non-terminating actions** do not return a response, but instead go to the next action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. The list of terminating Traffic Policy Actions includes: * [`close-connection`](/docs/gateway/traffic-policy/actions/close-connection) * [`custom-response`](/docs/gateway/traffic-policy/actions/custom-response) * [`deny`](/docs/gateway/traffic-policy/actions/deny) * [`forward-internal`](/docs/gateway/traffic-policy/actions/forward-internal) # Expression Examples Source: https://ngrok.com/docs/gateway/traffic-policy/concepts/expressions Learn how to write CEL expressions in Traffic Policy to define conditions for triggering actions based on traffic attributes. Expressions are a powerful way to define the conditions under which specific actions in a [Traffic Policy](/docs/gateway/traffic-policy/how-it-works#phase-rules) are triggered. Expressions are written in the [Common Expression Language (CEL)](https://github.com/google/cel-spec) and allow you to evaluate various traffic attributes, such as headers, paths, IP addresses, and more. Each expression must evaluate to `true` for the corresponding actions in a rule to be executed. If a rule contains no expressions, it will always evaluate to `true`, causing its actions to be applied to all matching traffic. ## Available elements Expressions in Traffic Policies can reference a variety of elements, including: * **[Traffic Policy Variables](/docs/gateway/traffic-policy/variables)**: These allow you to access metadata about the connection or request, such as certificate details, headers, client IP, geo-location and more. * **[Traffic Policy Macros](/docs/gateway/traffic-policy/macros)**: Macros simplify complex conditions, enabling reusable patterns across expressions and policies. * **[Action Variables](/docs/gateway/traffic-policy/actions)**: Some actions may provide additional variables, enabling deeper customization based on specific action outcomes. ## Use cases Expressions give you the flexibility to: * **Filter traffic**: Evaluate headers, IP addresses, or other attributes to decide if traffic should match a given rule. * **Route traffic**: Forward traffic based on factors such as geolocation, protocol, or path. * **Control flow**: Implement more complex routing decisions based on dynamic conditions, ensuring optimal traffic handling. ## Common examples ### Match based on header ```yaml theme={null} expressions: - req.headers["authorization"][0] == "Bearer my-token" ``` ### Match requests to a specific path ```yaml theme={null} expressions: - req.url.path.startsWith("/api/v1/") ``` ### Match by country ```yaml theme={null} expressions: - conn.client_ip.geo.location.country_code == "US" ``` ## Writing expressions ### Basic syntax Expressions are written in CEL and are similar to expressions in languages like C, JavaScript, or Python. They can include variables, literals, and operators. ### Variables Variables refer to values. For example, `identity` might refer to a user object. ```yaml theme={null} expressions: - identity.age >= 18 // Checks if the user's age is 18 or more. ``` [View list of available variables](/docs/gateway/traffic-policy/variables) ### Literals CEL supports several literal types: * **Boolean**: `true`, `false` * **Integer**: `42`, `-7` * **String**: `"hello"`, `'world'` * **List**: `[1, 2, 3]` * **Map**: `{"key1": "value1", "key2": "value2"}` ### Operators CEL provides a rich set of operators for arithmetic, comparison, and logical operations: * **Arithmetic**: `+`, `-`, `*`, `/`, `%` * **Comparison**: `==`, `!=`, `<`, `<=`, `>`, `>=` * **Logical**: `&&`, `||`, `!` You must wrap expressions containing `!` in quotes for them to be parsed correctly. ❌ This expression does not work: ```yaml theme={null} on_tcp_connect: - expressions: - "!(conn.client_ip in ['2001:4860:7:f0e::f4', '98.35.32.113'])" actions: - type: deny ``` ✅ Wrapping the expression in quotes ensures that it'll be parsed correctly: ```yaml theme={null} on_tcp_connect: - expressions: - "!(conn.client_ip in ['2001:4860:7:f0e::f4', '98.35.32.113'])" actions: - type: deny ``` #### Using arithmetic ```go theme={null} 5 * (3 - 1) // Evaluates to 10 ``` #### Using comparison and logical operators ```go theme={null} // Checks if the user is 18 or older and is in the U.S. identity.age >= 18 && identity.country == "US" ``` ### Strings #### String concatenation To combine strings, use the `+` Operator: ```go theme={null} "Hello, " + "world!" // Results in "Hello, world!" ``` #### String functions CEL provides several built-in functions to work with strings, enabling you to perform transformations, query information, or compare strings. ##### `size()` Returns the number of characters in the string: ```go theme={null} size("Hello") // Evaluates to 5 ``` ##### `startsWith()` Checks if the string starts with a specified substring: ```go theme={null} "Hello, world".startsWith("Hello") // Evaluates to true ``` ##### `endsWith()` Checks if the string ends with a specified substring: ```go theme={null} "Hello, world".endsWith("world") // Evaluates to true ``` ##### `matches()` Determines if the string matches a regular expression pattern: ```go theme={null} "Hello, world".matches("H.*d") // Evaluates to true ``` ##### `contains()` To check if a string contains another string: ```go theme={null} "Hello, world!".contains("world") // Evaluates to true ``` #### Advanced string manipulation ##### Regular expressions CEL's `matches()` function allows you to use regular expressions for pattern matching. This can be powerful for validation or extracting parts of strings: ```go theme={null} // Evaluates to true for a valid email format "user@example.com".matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$") ``` ##### Concatenation and Interpolation While direct string interpolation isn't a feature in CEL, concatenation can be used to dynamically construct strings: ```go theme={null} // Interpolates number to string "Your order number is: " + string(order_number) ``` ##### Working with Unicode Strings in CEL are Unicode and can handle a wide range of characters: ```go theme={null} // Evaluates to 5, as there are 5 characters in this Japanese greeting size("こんにちは") ``` ##### String comparison String comparison in CEL is case-sensitive and uses the standard comparison operators: ```go theme={null} "apple" == "Apple" // Evaluates to false due to case difference "apple" < "banana" // Alphabetically compares strings, evaluates to true ``` ### Lists A list in CEL is an ordered collection of elements. You can perform various operations on lists, including checking if an item is contained within a list, accessing elements by their index, and iterating over elements. #### Creating a list ```go theme={null} var my_list = [1, 2, 3, 4] ``` #### Checking for Membership To check if a value exists in a list, use the `in` Operator: ```go theme={null} 2 in my_list; // Evaluates to true 5 in my_list; // Evaluates to false ``` #### Accessing elements Access elements by their index (0-based): ```go theme={null} // Accesses the second element, evaluates to 2 my_list[1]; ``` #### Counting elements Returns the number of elements in the list: ```go theme={null} size(my_list) // Evaluates to 4 ``` #### Iterating over a list Use a comprehension to iterate over elements in a list and apply logic: ```go theme={null} // Doubles each value in the list, resulting in [2, 4, 6, 8] [value * 2 for value in my_list] ``` #### Filtering a list Filter a list to include only certain elements: ```go theme={null} // Keeps only values greater than 2, resulting in [3, 4] [value for value in my_list if value > 2] ``` #### Membership in lists The `in` Operator is versatile and can be used to check for membership in both lists: ```go theme={null} // For maps (checks keys) "apple" in { apple: 1, banana: 2 } // true ``` ### Maps A map in CEL is a collection of key-value pairs. Keys are unique, and each key maps to exactly one value. You can check for the presence of keys, access values by their keys, and iterate over keys or values. #### Creating a map ```go theme={null} var my_map = { a: 1, b: 2, c: 3 } ``` #### Checking for key presence To check if a key is present in a map, use the `in` Operator: ```go theme={null} "a" in my_map; // Evaluates to true "d" in my_map; // Evaluates to false ``` #### Accessing values Access values by their keys: ```go theme={null} // Accesses the value for the key "a", evaluates to 1 my_map["a"] ``` #### Counting elements Returns the number of key-value pairs present in the map: ```go theme={null} size(my_map) // Evaluates to 3 ``` #### Iterating over a map You can iterate over the keys or values of a map using a comprehension: ```go theme={null} // Extracts all keys, resulting in ["a", "b", "c"] [key for key, value in my_map] // Extracts all values, resulting in [1, 2, 3] [value for key, value in my_map] ``` #### Checking for a Condition in a Map Use `exists` or `all` macros to check if any or all elements in a collection meet a condition: ```go theme={null} // Checks if any key in the map starts with "a" my_map.exists(key, key.startsWith("a")) // Evaluates to true // Checks if all values in the map are greater than 0 my_map.all(value, value > 0) // Evaluates to true ``` #### Membership in maps The `in` Operator is versatile and can be used to check for membership in maps: ```go theme={null} // For maps (checks keys) "apple" in { apple: 1, banana: 2 } // true ``` # IP Policies Source: https://ngrok.com/docs/gateway/traffic-policy/concepts/ip-policies Use rules to allow or deny traffic and dashboard access from specific IPs and CIDRs. IP Policies are reusable groups of rules for allowing or denying traffic and ngrok dashboard access from specific IPs and CIDRs. Each IP Policy contains one or more rules that specify an IP or CIDR range and whether to `allow` or `deny` traffic from that range. You can manage IP Policies in [the ngrok dashboard](https://dashboard.ngrok.com/ip-policies) or via the [IP Policies API](/docs/api-reference/ippolicies/list). ## Creating and managing IP Policies ### In the dashboard 1. Go to [**Security**, then **IP Policies**](https://dashboard.ngrok.com/ip-policies) in the ngrok dashboard. 2. Click **New IP Policy** to create a policy. 3. Add rules to your policy by specifying a CIDR range (such as `203.0.113.0/24`) and choosing `allow` or `deny`. 4. Save your policy. You'll get a policy ID (for example, `ipp_...`) that you can reference in your Traffic Policy. ### Via the API You can also create and manage IP Policies using the [ngrok API](/docs/api-reference/ippolicies/list). Refer to the [IP Policy Rules API](/docs/api-reference/ippolicyrules/list) to add or remove rules from existing policies. ## Applying IP Policies to Endpoints To enforce an IP Policy on your endpoints, use the [`restrict-ips`](/docs/gateway/traffic-policy/actions/restrict-ips) Traffic Policy action. This action lets you reference one or more IP Policy IDs, and ngrok will allow or deny traffic based on the rules in those policies. There are two ways to apply a Traffic Policy to an Agent Endpoint: * **Standalone policy file**: Create a separate `policy.yml` or `policy.json` file and pass it to ngrok with the `--traffic-policy-file` flag. The policy phases (such as `on_http_request`) are at the top level of the file. * **Agent config file**: Define the policy inline inside your [ngrok agent config file](/docs/gateway/agent/config/v3) (for example, `ngrok.yml`). In this case, the policy phases must be nested under the `traffic_policy:` key inside the endpoint configuration. When embedding a Traffic Policy in your agent config file, the `on_http_request` (and other phase) blocks must be placed under `traffic_policy:` inside the endpoint definition—not at the top level of the config file. ### Using a standalone policy file Create a `policy.yml` or `policy.json` file with the following contents: ```yaml policy.yml theme={null} on_http_request: # Only allow requests from trusted IPs - actions: - type: restrict-ips config: enforce: true allow: - 203.0.113.0/24 - 198.51.100.42/32 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "restrict-ips", "config": { "enforce": true, "allow": [ "203.0.113.0/24", "198.51.100.42/32" ] } } ] } ] } ``` Then start your Agent Endpoint using the `--traffic-policy-file` flag: ```bash theme={null} ngrok http $YOUR_PORT --url $YOUR_DOMAIN --traffic-policy-file /path/to/policy.yml ``` ### Using the agent config file To embed the same policy in your [ngrok agent config file](/docs/gateway/agent/config/v3), nest the policy block under `traffic_policy:` inside the endpoint definition: ```yaml ngrok.yml theme={null} endpoints: - name: my-endpoint url: https://example.ngrok.app upstream: url: 8080 traffic_policy: on_http_request: # Only allow requests from trusted IPs - actions: - type: restrict-ips config: enforce: true allow: - 203.0.113.0/24 - 198.51.100.42/32 ``` ### Using a Cloud Endpoint To use a Cloud Endpoint, [visit the **Endpoints** section in the ngrok dashboard](https://dashboard.ngrok.com/endpoints) and select the **Cloud Endpoint**. You'll be taken to the **Traffic Policy** editor where you can enter the policy directly. The editor uses the standalone policy format. Enter the phase blocks (such as `on_http_request`) at the top level: ```yaml theme={null} on_http_request: # Only allow requests from trusted IPs - actions: - type: restrict-ips config: enforce: true allow: - 203.0.113.0/24 - 198.51.100.42/32 ``` Save your changes in the dashboard when done. ### Referencing an existing IP Policy by ID You can also reference an existing [IP Policy](/docs/api-reference/ippolicies/list) by its ID instead of listing CIDRs directly: ```yaml policy.yml theme={null} on_http_request: - actions: - type: restrict-ips config: enforce: true ip_policies: - ipp_1yjqdrIBwgciY2I9zH2EelgBbJF ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "restrict-ips", "config": { "enforce": true, "ip_policies": [ "ipp_1yjqdrIBwgciY2I9zH2EelgBbJF" ] } } ] } ] } ``` The same placement rules apply: use the top-level format in a standalone policy file or in the Cloud Endpoint editor, and nest under `traffic_policy:` when using the agent config file. For a full reference of all configuration options, behavior, and examples for the `restrict-ips` action, see the [Restrict IPs action reference](/docs/gateway/traffic-policy/actions/restrict-ips). ## Applying account-wide IP Policies To apply account-wide IP Policies, you can use [the **IP Restrictions** feature in the ngrok dashboard](https://dashboard.ngrok.com/ip-restrictions). In the dashboard UI, you can apply [IP Restrictions](/docs/gateway/traffic-policy/examples/add-authentication#ip-restrictions) to users trying to sign in to your ngrok dashboard, traffic trying to access your API or Endpoints, and source IPs trying to start Agent Endpoints on your account. You can define the IP Policies that make up your restrictions in the dashboard UI. ## Related resources * [Restrict IPs Traffic Policy action](/docs/gateway/traffic-policy/actions/restrict-ips): Full reference for the `restrict-ips` action, including all configuration options and examples. * [IP Policies API reference](/docs/api-reference/ippolicies/list): Manage IP Policies programmatically via the ngrok API. * [IP Policy Rules API reference](/docs/api-reference/ippolicyrules/list): Add or remove rules from your IP Policies via the ngrok API. # How to run an A/B test Source: https://ngrok.com/docs/gateway/traffic-policy/examples/a-b-tests Learn how to run A/B tests using Traffic Policy by routing traffic to different endpoints based on random distribution. With at least two endpoints and Traffic Policy, you can run simple A/B tests with using the [`rand.double()` macro](/docs/gateway/traffic-policy/macros/#randdouble---double). ## Basic example This rule: 1. Uses `rand.double()` to generate a random `double` between 0 and 1 when traffic reaches the endpoint 2. Checks if the value is less than or equal to 0.5 3. If so, it routes the traffic to a different [internal Agent Endpoint](/docs/gateway/endpoints/internal-endpoints/). ```yaml policy.yml theme={null} on_http_request: - expressions: - rand.double() <= 0.5 actions: - type: forward-internal config: url: https://a.internal - actions: - type: forward-internal config: url: https://b.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "rand.double() <= 0.5" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://a.internal" } } ] }, { "actions": [ { "type": "forward-internal", "config": { "url": "https://b.internal" } } ] } ] } ``` You can change the value the random number is checked against to split the traffic in different ratios. For example,`>=0.8` would send 80% of the traffic to one endpoint and 20% to the other. See [the `forward-internal` Traffic Policy action docs](/docs/gateway/traffic-policy/actions/forward-internal/) for more information. ## Using rewrites You can also send the traffic to a different route using URL rewrites. ```yaml policy.yml theme={null} on_http_request: - expressions: - rand.double() <= 0.5 actions: - type: url-rewrite config: from: "/path/to/test" to: "/path/to/test-b" - actions: - type: forward-internal config: url: https://b.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "rand.double() <= 0.5" ], "actions": [ { "type": "url-rewrite", "config": { "from": "/path/to/test", "to": "/path/to/test-b" } } ] }, { "actions": [ { "type": "forward-internal", "config": { "url": "https://b.internal" } } ] } ] } ``` See [the URL Rewrite Example](/docs/gateway/traffic-policy/examples/url-rewrites) for another example using rewrites. # Add and Remove Headers Source: https://ngrok.com/docs/gateway/traffic-policy/examples/add-and-remove-headers Learn how to add and remove HTTP headers in Traffic Policy to enrich requests or remove sensitive information before forwarding. With Traffic Policy, you can add and remove headers from requests to provide more context to your upstream service or withhold sensitive information. This page demonstrates a few example rules that do so. ## Enrich your upstream service Add new headers to requests to give your upstream service more context about the consumer, which in turn allows for richer functionality, such as localized languages and pricing. This rule adds multiple headers to the request, including the client's IP address, the endpoint ID, and the client's location. ```yaml policy.yml theme={null} on_http_request: - actions: - type: add-headers config: headers: x-is-ngrok: '1' x-endpoint-id: "${endpoint.id}" x-client-ip: "${conn.client_ip}" x-client-conn-start: "${conn.ts.start}" x-client-loc: "${conn.geo.city}, ${conn.geo.country}" x-client-path: "${req.url.path}" ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "add-headers", "config": { "headers": { "x-is-ngrok": "1", "x-endpoint-id": "${endpoint.id}", "x-client-ip": "${conn.client_ip}", "x-client-conn-start": "${conn.ts.start}", "x-client-loc": "${conn.geo.city}, ${conn.geo.country}", "x-client-path": "${req.url.path}" } } } ] } ] } ``` See [the `add-headers` Traffic Policy action docs](/docs/gateway/traffic-policy/actions/add-headers/) for more information. ## Remove service details from response headers Some frameworks, like [Express](https://expressjs.com/), add headers like `X-Powered-By: Express` to responses, which you may not want to reveal to your users. This rule removes the `X-Powered-By` header. ```yaml policy.yml theme={null} on_http_request: - actions: - type: remove-headers config: headers: - X-Powered-By ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "remove-headers", "config": { "headers": [ "X-Powered-By" ] } } ] } ] } ``` See [the `remove-headers` Traffic Policy action docs](/docs/gateway/traffic-policy/actions/remove-headers/) for more information. ## Deprecate an API version By including an `X-Api-Version` header in your API reference or developer documentation, you can quickly return a helpful error message, which encourages them to explore usage of the new version. This rule: 1. Checks the request's `X-Api-Version` 2. If its value is `2`, it returns a `400 Bad Request` response with a custom error message ```yaml policy.yml theme={null} on_http_request: - expressions: - "'2' in req.headers['X-Api-Version']" name: Deprecate API v2 actions: - type: custom-response config: status_code: 400 body: '{"error":{"message":"Version 2 of the API is no longer supported. Use Version 3 instead."}}' ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": ["'2' in req.headers['X-Api-Version']"], "name": "Deprecate API v2", "actions": [ { "type": "custom-response", "config": { "status_code": 400, "body": "{\"error\":{\"message\":\"Version 2 of the API is no longer supported. Use Version 3 instead.\"}}" } } ] } ] } ``` See [the `custom-response` Traffic Policy action docs](/docs/gateway/traffic-policy/actions/custom-response/) for more information. # Add Authentication Source: https://ngrok.com/docs/gateway/traffic-policy/examples/add-authentication Learn how to add authentication to your endpoints using Traffic Policy with Basic Auth, OAuth, OIDC, JWT validation, and more. Traffic Policy gives you powerful options for adding authentication to your endpoints—without touching your application code. Whether you need a quick login prompt for a demo, OAuth for production apps, or JWT validation for APIs, ngrok has you covered. For a deeper dive into authentication concepts and when to use each method, see the [Authentication with ngrok](https://ngrok.com/blog/authentication-with-ngrok) blog post. ## Authentication and access control methods | Method | Best for | | - | - | | [Basic Auth](#basic-auth) | Quick demos, internal tools | | [OAuth](#oauth) | User-facing apps with social login | | [OpenID Connect (OIDC)](#openid-connect-oidc) | Enterprise apps with corporate IdPs | | [JWT validation](#jwt-validation) | APIs with token-based auth | | [IP restrictions](#ip-restrictions) | Network-level access control | | [Mutual TLS (mTLS)](#mutual-tls-mtls) | Machine-to-machine, zero trust | *** ## Basic Auth HTTP Basic Auth is the simplest form of authentication—it prompts visitors for a username and password with zero setup. Use it for one-off demos or sharing local development work. ```yaml policy.yml theme={null} on_http_request: # Prompt for username and password with up to 10 credential pairs - actions: - type: basic-auth config: credentials: - username: user1 password: password123 - username: user2 password: securepass456 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "basic-auth", "config": { "credentials": [ { "username": "user1", "password": "password123" }, { "username": "user2", "password": "securepass456" } ] } } ] } ] } ``` See [the `basic-auth` action docs](/docs/gateway/traffic-policy/actions/basic-auth/) for more information. *** ## OAuth OAuth lets you leverage existing identity providers like Google, GitHub, or Microsoft for authentication. Users sign in with their existing accounts—no password management required. ngrok supports these OAuth providers out of the box with managed applications: * Google * GitHub * GitLab * LinkedIn * Microsoft * Twitch ### Simple OAuth setup ```yaml policy.yml theme={null} on_http_request: # Redirect unauthenticated users to Google sign-in - actions: - type: oauth config: provider: google ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google" } } ] } ] } ``` ### Restrict access by email domain Use [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) to restrict access based on the authenticated user's email, allowing only users from a specific organization. ```yaml policy.yml theme={null} on_http_request: # Authenticate with Google OAuth - actions: - type: oauth config: provider: google # Only allow users with a company email address - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@your-company.com')" actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google" } } ] }, { "expressions": [ "!actions.ngrok.oauth.identity.email.endsWith('@your-company.com')" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` To allow only an explicit list of addresses instead of a whole domain, match against a list: ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: google # Only allow specific email addresses - expressions: - "!(actions.ngrok.oauth.identity.email in ['alice@example.com', 'bob@example.com'])" actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google" } } ] }, { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['alice@example.com', 'bob@example.com'])" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` For more OAuth examples—including session controls, multiple email domains, custom OAuth apps, and login/logout flows—see the [`oauth` action docs](/docs/gateway/traffic-policy/actions/oauth/) and the [OAuth protection guide](/docs/gateway/traffic-policy/examples/oauth-protection/). *** ## OpenID Connect (OIDC) OIDC connects to managed corporate identity providers like Okta, Azure AD, or any OIDC-compliant IdP. This gives you fine-grained control over authentication while leveraging your existing identity infrastructure. ### Basic OIDC setup ```yaml policy.yml theme={null} on_http_request: # Authenticate with your OIDC provider - actions: - type: openid-connect config: issuer_url: https://accounts.google.com client_id: "" client_secret: "" scopes: - openid - profile - email ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "openid-connect", "config": { "issuer_url": "https://accounts.google.com", "client_id": "", "client_secret": "", "scopes": [ "openid", "profile", "email" ] } } ] } ] } ``` ### Pass identity tokens to your service Forward the OIDC identity token to your upstream service via headers for further authorization logic. ```yaml policy.yml theme={null} on_http_request: # Authenticate with OIDC - actions: - type: openid-connect config: issuer_url: https://accounts.google.com client_id: "" client_secret: "" scopes: - openid - profile - email # Forward identity token and email to upstream service - actions: - type: add-headers config: headers: x-identity-token: "${actions.ngrok.oidc.identity_token}" x-user-email: "${actions.ngrok.oidc.identity.email}" ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "openid-connect", "config": { "issuer_url": "https://accounts.google.com", "client_id": "", "client_secret": "", "scopes": [ "openid", "profile", "email" ] } } ] }, { "actions": [ { "type": "add-headers", "config": { "headers": { "x-identity-token": "${actions.ngrok.oidc.identity_token}", "x-user-email": "${actions.ngrok.oidc.identity.email}" } } } ] } ] } ``` See [the `openid-connect` action docs](/docs/gateway/traffic-policy/actions/oidc/) for more information. *** ## JWT validation For APIs, JWT validation lets you verify tokens without managing user sessions. Combine it with rate limiting for robust API protection. ### Basic JWT validation ```yaml policy.yml theme={null} on_http_request: # Validate JWT from Authorization header using JWKS endpoint - actions: - type: jwt-validation config: issuer: allow_list: - value: https://your-auth-provider.com audience: allow_list: - value: "your-api-audience" http: tokens: - type: jwt method: header name: Authorization prefix: 'Bearer ' jws: allowed_algorithms: - RS256 keys: sources: additional_jkus: - https://your-auth-provider.com/.well-known/jwks.json ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "jwt-validation", "config": { "issuer": { "allow_list": [ { "value": "https://your-auth-provider.com" } ] }, "audience": { "allow_list": [ { "value": "your-api-audience" } ] }, "http": { "tokens": [ { "type": "jwt", "method": "header", "name": "Authorization", "prefix": "Bearer " } ] }, "jws": { "allowed_algorithms": [ "RS256" ], "keys": { "sources": { "additional_jkus": [ "https://your-auth-provider.com/.well-known/jwks.json" ] } } } } } ] } ] } ``` ### JWT validation with rate limiting Combine authentication with key-based rate limiting for complete API protection. ```yaml policy.yml theme={null} on_http_request: # Rate limit by API key, then validate JWT - actions: - type: rate-limit config: name: 30 requests per minute per API key algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - req.headers['x-api-key'] - type: jwt-validation config: issuer: allow_list: - value: https:// audience: allow_list: - value: "" http: tokens: - type: jwt method: header name: Authorization prefix: 'Bearer ' jws: allowed_algorithms: - RS256 keys: sources: additional_jkus: - https:///.well-known/jwks.json ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "rate-limit", "config": { "name": "30 requests per minute per API key", "algorithm": "sliding_window", "capacity": 30, "rate": "60s", "bucket_key": [ "req.headers['x-api-key']" ] } }, { "type": "jwt-validation", "config": { "issuer": { "allow_list": [ { "value": "https://" } ] }, "audience": { "allow_list": [ { "value": "" } ] }, "http": { "tokens": [ { "type": "jwt", "method": "header", "name": "Authorization", "prefix": "Bearer " } ] }, "jws": { "allowed_algorithms": [ "RS256" ], "keys": { "sources": { "additional_jkus": [ "https:///.well-known/jwks.json" ] } } } } } ] } ] } ``` See [the `jwt-validation` action docs](/docs/gateway/traffic-policy/actions/jwt-validation/) and the [Auth0 integration guide](/docs/integrations/jwt-validation/auth0) for more information. *** ## IP restrictions Restrict access to specific IP addresses or CIDR ranges. Use this for network-level access control, such as allowing only office IPs or blocking known bad actors. ```yaml policy.yml theme={null} on_http_request: # Only allow requests from trusted IPs - actions: - type: restrict-ips config: allow: - 203.0.113.0/24 - 198.51.100.42/32 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "restrict-ips", "config": { "allow": [ "203.0.113.0/24", "198.51.100.42/32" ] } } ] } ] } ``` You can also combine IP restrictions with other authentication methods. See [the IP restrictions + Basic Auth example](/docs/gateway/examples/ip-restrictions-basic-auth/) for a pattern that allows users to authenticate with one method OR another. See [the `restrict-ips` action docs](/docs/gateway/traffic-policy/actions/restrict-ips/) for more information. *** ## Mutual TLS (mTLS) Mutual TLS requires clients to present a valid certificate, enabling strong machine-to-machine authentication. Use this for zero-trust architectures or when you need to verify client identity at the transport layer. mTLS is configured at the endpoint level rather than through Traffic Policy actions. See the [Mutual TLS documentation](/docs/gateway/domains/tls-termination/#mutual-tls-mtls) for setup instructions. Once mTLS is enabled, you can use Traffic Policy to make routing or access decisions based on client certificate details. See the [routing by client certificate](/docs/gateway/traffic-policy/examples/route-requests/#by-client-certificate) example. *** ## Enforce authentication Use Traffic Policy to require authentication or apply different rate limits based on authentication status. ### Reject unauthenticated requests For APIs that require authentication, immediately reject requests that don't include an `Authorization` header. ```yaml policy.yml theme={null} on_http_request: # Return 401 if no Authorization header is present - expressions: - "!('authorization' in req.headers)" actions: - type: custom-response config: status_code: 401 body: '{"error": "Authorization header required"}' headers: content-type: application/json ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!('authorization' in req.headers)" ], "actions": [ { "type": "custom-response", "config": { "status_code": 401, "body": "{\"error\": \"Authorization header required\"}", "headers": { "content-type": "application/json" } } } ] } ] } ``` ### Tiered rate limits Create different rate limits based on authentication status—unauthenticated users get a lower limit while authenticated users get higher capacity. ```yaml policy.yml theme={null} on_http_request: # Unauthenticated requests get 10 req/min by IP - expressions: - "!('Authorization' in req.headers)" actions: - type: rate-limit config: name: Free tier - 10 requests per minute algorithm: sliding_window capacity: 10 rate: 60s bucket_key: - conn.client_ip # Authenticated requests get 100 req/min by token - expressions: - "'Authorization' in req.headers" actions: - type: rate-limit config: name: Authenticated - 100 requests per minute algorithm: sliding_window capacity: 100 rate: 60s bucket_key: - req.headers['authorization'] ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!('Authorization' in req.headers)" ], "actions": [ { "type": "rate-limit", "config": { "name": "Free tier - 10 requests per minute", "algorithm": "sliding_window", "capacity": 10, "rate": "60s", "bucket_key": [ "conn.client_ip" ] } } ] }, { "expressions": [ "'Authorization' in req.headers" ], "actions": [ { "type": "rate-limit", "config": { "name": "Authenticated - 100 requests per minute", "algorithm": "sliding_window", "capacity": 100, "rate": "60s", "bucket_key": [ "req.headers['authorization']" ] } } ] } ] } ``` *** ## Related resources * [Traffic Policy overview](/docs/gateway/traffic-policy/) * [`oauth` action reference](/docs/gateway/traffic-policy/actions/oauth/) * [OAuth protection guide](/docs/gateway/traffic-policy/examples/oauth-protection/) * [Block unwanted requests](/docs/gateway/traffic-policy/examples/block-unwanted-requests/) * [Rate limit requests](/docs/gateway/traffic-policy/examples/rate-limit-requests/) * [IP restrictions + Basic Auth example](/docs/gateway/examples/ip-restrictions-basic-auth/) # Block Unwanted Requests Source: https://ngrok.com/docs/gateway/traffic-policy/examples/block-unwanted-requests Learn how to block unwanted or malicious requests using Traffic Policy including examples for blocking Tor, bots, and specific IPs. With Traffic Policy, you can block unwanted requests to your endpoints. This page demonstrates a few example rules that do so. See the following Traffic Policy action docs for more information: * [`deny`](/docs/gateway/traffic-policy/actions/deny/) * [`custom-response`](/docs/gateway/traffic-policy/actions/custom-response/) ## How to deny traffic from Tor This rule uses the [connection variables](/docs/gateway/traffic-policy/variables/connection/) available in [IP Intelligence](/docs/gateway/traffic-policy/variables/ip-intel) to block [Tor](https://en.wikipedia.org/wiki/Tor_\(network\)) exit node IPs. ```yaml policy.yml theme={null} on_http_request: - expressions: - ('proxy.anonymous.tor' in conn.client_ip.categories) actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "('proxy.anonymous.tor' in conn.client_ip.categories)" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` ## How to deny traffic from bots and crawlers with a `robots.txt` This rule returns a custom response with a [`robots.txt` file](https://developers.google.com/search/docs/crawling-indexing/robots/intro) to deny search engine or [AI crawlers](https://platform.openai.com/docs/bots/) on all paths. ```yaml policy.yml theme={null} on_http_request: - expressions: - "!req.url.path.contains('/robots.txt')" actions: - type: forward-internal config: url: - expressions: - "req.url.path.contains('/robots.txt')" actions: - type: custom-response config: body: "User-agent: *\r\nDisallow: /" headers: content-type: text/plain status_code: 200 ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!req.url.path.contains('/robots.txt')" ], "actions": [ { "type": "forward-internal", "config": { "url": "" } } ] }, { "expressions": [ "req.url.path.contains('/robots.txt')" ], "actions": [ { "type": "custom-response", "config": { "body": "User-agent: *\r\nDisallow: /", "headers": { "content-type": "text/plain" }, "status_code": 200 } } ] } ] } ``` You can extend this example to create specific rules for crawlers based on their user agent strings, like [`ChatGPT-User` and `GPTBot`](https://platform.openai.com/docs/bots). ```yaml policy.yml theme={null} on_http_request: - name: Add `robots.txt` to deny specific bots and crawlers expressions: - req.url.contains('/robots.txt') actions: - type: custom-response config: status_code: 200 body: "User-agent: ChatGPT-User\r\nDisallow: /" headers: content-type: text/plain ``` ```json policy.json theme={null} { "on_http_request": [ { "name": "Add `robots.txt` to deny specific bots and crawlers", "expressions": [ "req.url.contains('/robots.txt')" ], "actions": [ { "type": "custom-response", "config": { "status_code": 200, "body": "User-agent: ChatGPT-User\\r\\nDisallow: /", "headers": { "content-type": "text/plain" } } } ] } ] } ``` ## How to block traffic from bots and crawlers by user agent You can also take action on incoming requests that contain specific strings in [the `req.user_agent` request variable](/docs/gateway/traffic-policy/variables/req/#requser-agent). ```yaml policy.yml theme={null} on_http_request: - name: Block specific bots by user agent expressions: - req.user_agent.name in ['ChatGPT-User', 'GPTBot', 'OAI-SearchBot'] actions: - type: deny config: status_code: 404 ``` ```json policy.json theme={null} { "on_http_request": [ { "name": "Block specific bots by user agent", "expressions": [ "req.user_agent.name in ['ChatGPT-User', 'GPTBot', 'OAI-SearchBot']" ], "actions": [ { "type": "deny", "config": { "status_code": 404 } } ] } ] } ``` You can expand the expression to include additional user agents by adding them to the list: ```bash theme={null} ['ChatGPT-User', 'GPTBot', 'anthropic', 'claude'] ``` ## How to block traffic from bots and crawlers by IP Address You can also use IP Intelligence variables to block AI Bots by IP Address. ```json policy.json theme={null} { "on_http_request": [ { "name": "Block specific AI Bots with IP Intelligence", "expressions": [ "('com.anthropic' in conn.client_ip.categories) || ('com.openai' in conn.client_ip.categories) || ('com.perplexity' in conn.client_ip.categories)" ], "actions": [ { "type": "deny", "config": { "status_code": 404 } } ] } ] } ``` ```yaml policy.yml theme={null} on_http_request: - name: Block specific AI Bots with IP Intelligence expressions: - ('com.anthropic' in conn.client_ip.categories) || ('com.openai' in conn.client_ip.categories) || ('com.perplexity' in conn.client_ip.categories) actions: - type: deny config: status_code: 404 ``` ## Deny non-GET requests This rule denies all inbound traffic that is not a GET request. ```yaml policy.yml theme={null} on_http_request: - expressions: - req.method != 'GET' actions: - type: deny ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.method != 'GET'" ], "actions": [ { "type": "deny" } ] } ] } ``` ## Custom response for unauthorized requests This rule sends a custom response with status code `401` and body `Unauthorized` for requests without an Authorization header. ```yaml policy.yml theme={null} on_http_request: - expressions: - "!('authorization' in req.headers)" actions: - type: custom-response config: status_code: 401 body: Unauthorized ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!('authorization' in req.headers)" ], "actions": [ { "type": "custom-response", "config": { "status_code": 401, "body": "Unauthorized" } } ] } ] } ``` ## How to block traffic from specific countries Sometimes you may need to block requests originating from one or more countries to remain compliant with data regulations or sanctions. This rule blocks requests based on the origin country using [ISO country codes](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) with the following steps: 1. Check if the request is from an array of countries you can define 2. If so, return a `401` status code with an error message. ```yaml policy.yml theme={null} on_http_request: - expressions: - conn.geo.country_code in ['', ''] name: Block traffic from unwanted countries actions: - type: custom-response config: status_code: 401 body: "Unauthorized request due to country of origin." ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "conn.geo.country_code in ['', '']" ], "name": "Block traffic from unwanted countries", "actions": [ { "type": "custom-response", "config": { "status_code": 401, "body": "Unauthorized request due to country of origin." } } ] } ] } ``` ## Limit request sizes This rule demonstrates how to prevent excessively large user uploads, like text or images, that might cause performance or availability issues for your upstream service with the following steps: 1. Check if the request is `POST` or \`PUT 2. Check if the request's content is 1MB or larger. 3. If both conditions are met, return a `400` status code with an error message. ```yaml policy.yml theme={null} on_http_request: - name: Block large POST/PUT requests. expressions: - req.method == 'POST' || req.method == 'PUT' - req.content_length >= 1000 actions: - type: custom-response config: status_code: 400 body: "Error: You can't upload content larger than 1MB." ``` ```json policy.json theme={null} { "on_http_request": [ { "name": "Block large POST/PUT requests.", "expressions": [ "req.method == 'POST' || req.method == 'PUT'", "req.content_length >= 1000" ], "actions": [ { "type": "custom-response", "config": { "status_code": 400, "body": "Error: You can't upload content larger than 1MB." } } ] } ] } ``` ## Exempt specific traffic from rate limits In this example, the Algolia web crawler is exempted from any rate limiting configured on your site. See [the IP Intelligence docs](/docs/gateway/traffic-policy/variables/ip-intel/#ip-categories) for other bots and crawlers that are available. ```yaml policy.yml theme={null} on_http_request: - expressions: - "!('com.algolia.crawler' in conn.client_ip.categories)" actions: - type: rate-limit config: name: Only allow 30 requests per minute algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - conn.client_ip ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!('com.algolia.crawler' in conn.client_ip.categories)" ], "actions": [ { "type": "rate-limit", "config": { "name": "Only allow 30 requests per minute", "algorithm": "sliding_window", "capacity": 30, "rate": "60s", "bucket_key": [ "conn.client_ip" ] } } ] } ] } ``` # Compress JSON Responses Example Source: https://ngrok.com/docs/gateway/traffic-policy/examples/compress-json-responses Learn how to compress JSON responses using Traffic Policy to improve performance and reduce bandwidth usage. With Traffic Policy, you can ensure all JSON responses returned by your upstream services are [compressed](/docs/gateway/traffic-policy/actions/compress-response) en route to the API consumer. This rule compresses JSON responses using the `gzip`, `br`, `deflate`, and `compress` algorithms. If your upstream service already handles compression, ngrok skips this step. ```yaml policy.yml theme={null} on_http_response: - name: Add compression actions: - type: compress-response config: algorithms: - gzip - br - deflate - compress ``` ```json policy.json theme={null} { "on_http_response": [ { "name": "Add compression", "actions": [ { "type": "compress-response", "config": { "algorithms": [ "gzip", "br", "deflate", "compress" ] } } ] } ] } ``` See [the `compress-response` Traffic Policy action docs](/docs/gateway/traffic-policy/actions/compress-response/) for more information. # Enforce TLS Example Source: https://ngrok.com/docs/gateway/traffic-policy/examples/enforce-tls Learn how to enforce minimum TLS version requirements using Traffic Policy to reject connections using outdated TLS versions. With Traffic Policy, you can prevent obsolete and potentially vulnerable browsers, SDKs, or CLI tools like `curl` from attempting to access your API. This rule: 1. Uses [the `conn.tls.version` connection variable](/docs/gateway/traffic-policy/variables/connection/#conntlsversion) to check the incoming request's TLS version. 2. Rejects versions below `1.3` with a `401 Unauthorized` response. ```yaml policy.yml theme={null} on_http_request: - name: Reject requests using old TLS versions expressions: - conn.tls.version < '1.3' actions: - type: custom-response config: status_code: 401 body: 'Unauthorized: TLS version too old' ``` ```json policy.json theme={null} { "on_http_request": [ { "name": "Reject requests using old TLS versions", "expressions": [ "conn.tls.version < '1.3'" ], "actions": [ { "type": "custom-response", "config": { "status_code": 401, "body": "Unauthorized: TLS version too old" } } ] } ] } ``` See [the `custom-response` Traffic Policy action docs](/docs/gateway/traffic-policy/actions/custom-response/) for more information. # Event Logging Example Source: https://ngrok.com/docs/gateway/traffic-policy/examples/event-logging Learn how to configure event logging in Traffic Policy to export logs to external services like Datadog for monitoring and troubleshooting. With Traffic Policy, you can connect your API to ngrok's [log exporting system](/docs/obs/) for smarter troubleshooting, testing, and integration of logging services like Datadog for your API gateway and upstream services. This rule: 1. Checks if the response status code is not in the 200 range 2. Logs the unsuccessful request with a custom message and metadata ```yaml policy.yml theme={null} on_http_request: - actions: - type: custom-response config: status_code: 503 body: "

Service Unavailable

Our servers are currently down for maintenance. Please check back later.

" headers: content-type: text/html on_http_response: - name: Log unsuccessful requests expressions: - res.status_code < '200' && res.status_code >= '300' actions: - type: log config: metadata: message: Unsuccessful request edge_id: "" success: false ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "custom-response", "config": { "status_code": 503, "body": "

Service Unavailable

Our servers are currently down for maintenance. Please check back later.

", "headers": { "content-type": "text/html" } } } ] } ], "on_http_response": [ { "name": "Log unsuccessful requests", "expressions": [ "res.status_code < '200' && res.status_code >= '300'" ], "actions": [ { "type": "log", "config": { "metadata": { "message": "Unsuccessful request", "edge_id": "", "success": false } } } ] } ] } ```
See [the `log` Traffic Policy action docs](/docs/gateway/traffic-policy/actions/log/) for more information. # Filter Traffic by IP Category Source: https://ngrok.com/docs/gateway/traffic-policy/examples/filter-by-ip-category Learn how to allow or block traffic from specific services, bots, cloud providers, and threat categories using ngrok's built-in IP Intelligence categories. ngrok maintains up-to-date IP ranges for dozens of well-known services, cloud providers, bots, and threat categories under [IP Intelligence](/docs/gateway/traffic-policy/variables/ip-intel). You can reference these in Traffic Policy [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) to allow or block traffic without maintaining lists of CIDRs by hand. All category lookups use the `conn.client_ip.categories` variable. See the [full list of available categories](/docs/gateway/traffic-policy/variables/ip-intel#ip-categories). ## How to block AI crawlers and bots Use IP Intelligence categories to deny traffic from AI services that crawl your site. ```yaml policy.yml theme={null} on_http_request: - name: Block AI crawlers by IP expressions: - >- 'com.anthropic.api' in conn.client_ip.categories || 'com.openai.gptbot.ipv4' in conn.client_ip.categories || 'ai.perplexity.bot.ipv4' in conn.client_ip.categories actions: - type: deny config: status_code: 404 ``` ```json policy.json theme={null} { "on_http_request": [ { "name": "Block AI crawlers by IP", "expressions": [ "'com.anthropic.api' in conn.client_ip.categories || 'com.openai.gptbot.ipv4' in conn.client_ip.categories || 'ai.perplexity.bot.ipv4' in conn.client_ip.categories" ], "actions": [ { "type": "deny", "config": { "status_code": 404 } } ] } ] } ``` You can add as many categories as you need to the expression. See the [full list of AI and crawler categories](/docs/gateway/traffic-policy/variables/ip-intel#crawlers--bots). ## How to allow traffic only from a specific cloud provider Use an IP category in an expression to restrict access to IPs from a single provider, such as AWS or GitHub. ```yaml policy.yml theme={null} on_http_request: - name: Allow only GitHub IPs expressions: - "!('com.github' in conn.client_ip.categories)" actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "name": "Allow only GitHub IPs", "expressions": [ "!('com.github' in conn.client_ip.categories)" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` Other common provider categories include `com.aws`, `com.google.ipv4`, `com.microsoft.azure`, and `com.cloudflare`. See the [full list of cloud provider categories](/docs/gateway/traffic-policy/variables/ip-intel#cloud-providers). ## How to block Tor exit nodes and anonymous proxies ```yaml policy.yml theme={null} on_http_request: - name: Block Tor and anonymous proxies expressions: - "'proxy.anonymous' in conn.client_ip.categories" actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "name": "Block Tor and anonymous proxies", "expressions": [ "'proxy.anonymous' in conn.client_ip.categories" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` Categories are hierarchical. Using `proxy.anonymous` blocks all anonymous proxies, including `proxy.anonymous.tor`. To block only Tor exit nodes, use `proxy.anonymous.tor` instead. ## How to block IPs on known threat blocklists ngrok integrates with several third-party threat intelligence feeds. You can block IPs listed on these feeds using their category names. ```yaml policy.yml theme={null} on_http_request: - name: Block known malicious IPs expressions: - >- 'blocklist.org.spamhaus.drop.ipv4' in conn.client_ip.categories || 'blocklist.firehol_org.level_1' in conn.client_ip.categories || 'blocklist.blocklist_de' in conn.client_ip.categories actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "name": "Block known malicious IPs", "expressions": [ "'blocklist.org.spamhaus.drop.ipv4' in conn.client_ip.categories || 'blocklist.firehol_org.level_1' in conn.client_ip.categories || 'blocklist.blocklist_de' in conn.client_ip.categories" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` See the [full list of blocklist categories](/docs/gateway/traffic-policy/variables/ip-intel#blocklists) for all available threat feeds. ## How to exempt a trusted service from rate limiting You can use IP categories to exclude known-good services from rules like rate limiting. For example, to exempt Algolia's crawler: ```yaml policy.yml theme={null} on_http_request: - expressions: - "!('com.algolia.crawer' in conn.client_ip.categories)" actions: - type: rate-limit config: name: Only allow 30 requests per minute algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - conn.client_ip ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!('com.algolia.crawer' in conn.client_ip.categories)" ], "actions": [ { "type": "rate-limit", "config": { "name": "Only allow 30 requests per minute", "algorithm": "sliding_window", "capacity": 30, "rate": "60s", "bucket_key": [ "conn.client_ip" ] } } ] } ] } ``` ## Related * [IP Intelligence variable reference](/docs/gateway/traffic-policy/variables/ip-intel): Full list of variables and IP categories * [Restrict IPs action](/docs/gateway/traffic-policy/actions/restrict-ips): Allow or deny by CIDR or IP Policy * [Block Unwanted Requests](/docs/gateway/traffic-policy/examples/block-unwanted-requests): More examples for blocking bots, countries, and bad actors # Traffic Policy Examples Source: https://ngrok.com/docs/gateway/traffic-policy/examples/index Example Traffic Policies to help you get started. Whether you need an API gateway, multicloud failover, or want to integrate ngrok with your stack, these examples help you compose endpoints and Traffic Policy to orchestrate traffic for common jobs to be done and problems to be solve. Wire it up, secure it, and *ship it already*. See [the Traffic Policy pricing docs](/docs/pricing-limits/traffic-policy-unit-pricing) for a detailed pricing breakdown. # Using OAuth Traffic Policy on Endpoints Source: https://ngrok.com/docs/gateway/traffic-policy/examples/oauth-protection Learn how to add OAuth authentication to your ngrok endpoints using Traffic Policy. Sometimes you don't want everyone to be able to access the app at your [Endpoints](/docs/endpoints). This guide will walk you through protecting your apps with OAuth authentication via [Traffic Policy](/docs/traffic-policy). All you need is [the ngrok agent CLI](/docs/gateway/agent/cli). ## 1. Pick your provider Decide which OAuth identity provider you want to use. If you don't have an OAuth application configured yet, you can use any of the following providers out of the box: * Google * GitHub * GitLab * LinkedIn * Microsoft * Twitch This guide will use Google OAuth. If you use any of the above providers without configuring your own OAuth application, your endpoint will use ngrok's managed OAuth application. This means you won't be able to customize the provider's authentication behavior. ## 2. Create a Traffic Policy file The following is an example Traffic Policy file that executes the ['oauth' Traffic Policy action](/docs/gateway/traffic-policy/actions/oauth/) on every request to your endpoint. Create this Traffic Policy file in the same directory where you run your ngrok agent, or add its contents to your `ngrok.yml` agent config file as shown below. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: google ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google" } } ] } ] } ``` ## 3. Restart your endpoint with the policy attached If you saved your Traffic Policy in a local file like `oauth.yml`, add the `--traffic-policy-file` flag to your ngrok command when starting your agent as shown in the following example. ```bash theme={null} ngrok http 3000 --traffic-policy-file oauth.yml ``` If you've added the Traffic Policy to your agent config, run the following command in your terminal. ```bash theme={null} ngrok start endpoint-name-here ``` ## 4. Verify in a browser Open your app's URL (for example `https://myapp.ngrok.app`). You should be immediately redirected to the Google sign-in page, and after authenticating you'll land back on your app. ## Restrict access by email Authenticating users is often only half of what you want: a common next step is to allow only users from your own organization, so that anyone with a Google account can't get in. Use [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) to restrict access based on the authenticated user's email, allowing only users from a specific organization. ```yaml policy.yml theme={null} on_http_request: # Authenticate with Google OAuth - actions: - type: oauth config: provider: google # Only allow users with a company email address - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@your-company.com')" actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google" } } ] }, { "expressions": [ "!actions.ngrok.oauth.identity.email.endsWith('@your-company.com')" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` To allow only an explicit list of addresses instead of a whole domain, match against a list: ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: google # Only allow specific email addresses - expressions: - "!(actions.ngrok.oauth.identity.email in ['alice@example.com', 'bob@example.com'])" actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google" } } ] }, { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['alice@example.com', 'bob@example.com'])" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` ## Explore more and fine-tune with Traffic Policy [Traffic Policy](/docs/gateway/traffic-policy/) is a composable CEL-based configuration language that lets you match, filter, and control traffic to your endpoints without touching your app code. [Learn more about the building blocks of Traffic Policy](/docs/gateway/traffic-policy/#traffic-policy-building-blocks). Now that you've added auth to your endpoint, here are some optional ways to fine tune other parts of your auth setup aside from strictly how to handle logins. Depending on your goal, you’ll either configure session behavior in your Traffic Policy YAML, or trigger runtime behavior in your app using special `/ngrok/*` URLs. See the following tables for details. **Table 1: Configurable Properties (traffic\_policy settings)** | Behavior | Traffic Policy Config to Add | | - | - | | Shorten session timeout | `idle_session_timeout: "15m"` | | Limit total session time | `max_session_duration: "1h"` | | Periodically recheck identity | `userinfo_refresh_interval: "10m"` | | Use your own OAuth app | Set `client_id`, `client_secret`, `scopes` + register `https://idp.ngrok.com/oauth2/callback` with your provider | | Namespace sessions | `auth_id: "my-login"` (used in cookies & login/logout routing) | **Note**: When configuring a custom OAuth app, while specifying `client_id` and `client_secret`, ensure that the redirect URI [https://idp.ngrok.com/oauth2/callback](https://idp.ngrok.com/oauth2/callback) is registered with your OAuth provider. This is essential for the OAuth flow to function correctly. **Table 2: Runtime Interactions (handled in your app logic via URL routes)** | Action | What to do in your app | | - | - | | Force a user to re-login | Redirect to `/ngrok/login?auth_id=my-login` | | Log a user out | Redirect to `/ngrok/logout?auth_id=my-login` | | Start session from scratch | Chain logout → login: redirect to `/ngrok/logout?auth_id=my-login`, then to `/ngrok/login?auth_id=my-login` | ### Traffic Policy examples: This section includes two examples of how to apply these additional auth lifecycle options, depending on your Traffic Policy setup. **What these examples do:** * If a user visits `/ngrok/logout`, ngrok first destroys their session (built-in behavior), then your redirect action sends them straight to the login URL. * Any session inactive for 15 minutes or longer will auto-expire and trigger a re-login flow when the user returns. * After an hour (regardless of activity), users are forced to re-authenticate (`max_session_duration`). ```yaml policy.yml theme={null} on_http_request: - expressions: - req.url.path == '/ngrok/logout' actions: - type: redirect config: location: "/ngrok/login?auth_id=my-login" - actions: - type: oauth config: provider: google auth_id: my-login idle_session_timeout: 15m max_session_duration: 1h ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.url.path == '/ngrok/logout'" ], "actions": [ { "type": "redirect", "config": { "location": "/ngrok/login?auth_id=my-login" } } ] }, { "actions": [ { "type": "oauth", "config": { "provider": "google", "auth_id": "my-login", "idle_session_timeout": "15m", "max_session_duration": "1h" } } ] } ] } ``` Now you can restart your endpoint with the updated Traffic Policy file or config. If you saved your Traffic Policy in a local file like `oauth.yml`, add the `--traffic-policy-file` flag to your ngrok command when starting your agent as shown in the following example. ```bash theme={null} ngrok http 3000 --traffic-policy-file oauth.yml ``` If you've added the Traffic Policy to your agent config, run the following command in your terminal. ```bash theme={null} ngrok start endpoint-name-here ``` If you need assistance with specific configurations or further customization, contact Support at [support@ngrok.com](mailto:support@ngrok.com). ## Bring your own OAuth application Though this guide uses ngrok's managed Google OAuth application, you can also use your own OAuth application. See the following guides to get started with your preferred provider: * [Google](/docs/integrations/oauth/google-oauth) * [GitHub](/docs/integrations/oauth/github-oauth) * [GitLab](/docs/integrations/oauth/gitlab-oauth) * [LinkedIn](/docs/integrations/oauth/linkedin-oauth) * [Microsoft](/docs/integrations/oauth/microsoft-oauth) * [Twitch](/docs/integrations/oauth/twitch-oauth) # How to Rate Limit Traffic Source: https://ngrok.com/docs/gateway/traffic-policy/examples/rate-limit-requests Learn how to rate limit requests using Traffic Policy with examples for endpoint-based, IP-based, and key-based rate limiting. With Traffic Policy, you can rate limit requests to your endpoints based on a variety of criteria. This page demonstrates a few example rules that do so. See [the `rate-limit` Traffic Policy action docs](/docs/gateway/traffic-policy/actions/rate-limit/) for more information. ## By endpoint This rule applies rate limiting of 30 requests per minute to the endpoint `/api/videos`. ```yaml policy.yml theme={null} on_http_request: - expressions: - req.url.contains('/api/specific_endpoint') actions: - type: rate-limit config: name: Only allow 30 requests per minute algorithm: sliding_window capacity: 30 rate: 60s bucket_key: - conn.client_ip ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.url.contains('/api/specific_endpoint')" ], "actions": [ { "type": "rate-limit", "config": { "name": "Only allow 30 requests per minute", "algorithm": "sliding_window", "capacity": 30, "rate": "60s", "bucket_key": [ "conn.client_ip" ] } } ] } ] } ``` ## Based on authentication status This rule creates a lower rate limit for unauthenticated (likely free) users, while allowing authenticated users a higher level of capacity. ```yaml policy.yml theme={null} on_http_request: - expressions: - "!('Authorization' in req.headers)" name: Unauthorized rate limiting tier actions: - type: rate-limit config: name: Allow 10 requests per minute algorithm: sliding_window capacity: 10 rate: 60s bucket_key: - conn.client_ip - expressions: - "('Authorization' in req.headers)" name: Authorized rate limiting tier actions: - type: rate-limit config: name: Allow 100 requests per minute algorithm: sliding_window capacity: 100 rate: 60s bucket_key: - conn.client_ip ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!('Authorization' in req.headers)" ], "name": "Unauthorized rate limiting tier", "actions": [ { "type": "rate-limit", "config": { "name": "Allow 10 requests per minute", "algorithm": "sliding_window", "capacity": 10, "rate": "60s", "bucket_key": [ "conn.client_ip" ] } } ] }, { "expressions": [ "('Authorization' in req.headers)" ], "name": "Authorized rate limiting tier", "actions": [ { "type": "rate-limit", "config": { "name": "Allow 100 requests per minute", "algorithm": "sliding_window", "capacity": 100, "rate": "60s", "bucket_key": [ "conn.client_ip" ] } } ] } ] } ``` ## Based on pricing tiers Using a naming scheme in your upstream servers with API calls using a `tier` header, you can quickly customize access to your API based on any number of pricing tiers. This rule checks for a `Tier` header in the request and applies rate limiting based on the value of that header. ```yaml policy.yml theme={null} on_http_request: - expressions: - "!('Tier' in req.headers)" name: Free rate limiting tier actions: - type: rate-limit config: name: Allow 10 requests per minute algorithm: sliding_window capacity: 10 rate: 60s bucket_key: - conn.client_ip - expressions: - getReqHeader('tier').exists(v, v.matches('(?i)bronze')) name: Bronze rate limiting tier actions: - type: rate-limit config: name: Allow 100 requests per minute algorithm: sliding_window capacity: 100 rate: 60s bucket_key: - conn.client_ip - expressions: - getReqHeader('tier').exists(v, v.matches('(?i)silver')) name: Silver rate limiting tier actions: - type: rate-limit config: name: Allow 1000 requests per minute algorithm: sliding_window capacity: 1000 rate: 60s bucket_key: - conn.client_ip - expressions: - getReqHeader('tier').exists(v, v.matches('(?i)gold')) name: Gold rate limiting tier actions: - type: rate-limit config: name: Allow 10000 requests per minute algorithm: sliding_window capacity: 10000 rate: 60s bucket_key: - conn.client_ip ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!('Tier' in req.headers)" ], "name": "Free rate limiting tier", "actions": [ { "type": "rate-limit", "config": { "name": "Allow 10 requests per minute", "algorithm": "sliding_window", "capacity": 10, "rate": "60s", "bucket_key": [ "conn.client_ip" ] } } ] }, { "expressions": [ "getReqHeader('tier').exists(v, v.matches('(?i)bronze'))" ], "name": "Bronze rate limiting tier", "actions": [ { "type": "rate-limit", "config": { "name": "Allow 100 requests per minute", "algorithm": "sliding_window", "capacity": 100, "rate": "60s", "bucket_key": [ "conn.client_ip" ] } } ] }, { "expressions": [ "getReqHeader('tier').exists(v, v.matches('(?i)silver'))" ], "name": "Silver rate limiting tier", "actions": [ { "type": "rate-limit", "config": { "name": "Allow 1000 requests per minute", "algorithm": "sliding_window", "capacity": 1000, "rate": "60s", "bucket_key": [ "conn.client_ip" ] } } ] }, { "expressions": [ "getReqHeader('tier').exists(v, v.matches('(?i)gold'))" ], "name": "Gold rate limiting tier", "actions": [ { "type": "rate-limit", "config": { "name": "Allow 10000 requests per minute", "algorithm": "sliding_window", "capacity": 10000, "rate": "60s", "bucket_key": [ "conn.client_ip" ] } } ] } ] } ``` # Route Requests Source: https://ngrok.com/docs/gateway/traffic-policy/examples/route-requests Learn how to route requests to different internal endpoints using Traffic Policy based on subdomain, path, header, cookie, query parameters, IP intelligence, and more. Traffic Policy uses [CEL expressions](/docs/gateway/traffic-policy/how-it-works#expressions) and [interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation) to dynamically route requests to different internal endpoints. Route based on virtually any request attribute—subdomain, path, headers, cookies, query parameters, geographic location, IP intelligence, and more. All routing examples use the [`forward-internal` action](/docs/gateway/traffic-policy/actions/forward-internal/) to send traffic to internal endpoints. See the action reference for configuration details. ## Routing methods | Method | Use case | | - | - | | [By subdomain](#by-subdomain) | Multi-tenant apps, customer-specific routing | | [By path](#by-path) | API versioning, microservice routing | | [By header](#by-header) | Customer routing, feature flags, A/B testing | | [By cookie](#by-cookie) | Session-based routing, sticky sessions | | [By query parameter](#by-query-parameter) | Debug routing, testing environments | | [By geographic location](#by-geographic-location) | Regional routing, compliance | | [By IP intelligence](#by-ip-intelligence) | Bot detection, threat-based routing | | [By device type](#by-device-type) | Mobile vs desktop experiences | | [By client certificate](#by-client-certificate) | mTLS-based service routing | *** ## By subdomain Route requests from `https://*.example.com` to corresponding internal services. ### Extract subdomain dynamically ```yaml policy.yml theme={null} on_http_request: # Extract subdomain and route to matching internal service - actions: - type: forward-internal config: url: https://${req.host.split('.example.com')[0]}.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "forward-internal", "config": { "url": "https://${req.host.split('.example.com')[0]}.internal" } } ] } ] } ``` ### Route specific subdomains ```yaml policy.yml theme={null} on_http_request: # Route api.* to API service - expressions: - req.host.startsWith('api.') actions: - type: forward-internal config: url: https://api-service.internal # Route admin.* to admin service - expressions: - req.host.startsWith('admin.') actions: - type: forward-internal config: url: https://admin-service.internal # All other subdomains go to main app - actions: - type: forward-internal config: url: https://main-app.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.host.startsWith('api.')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://api-service.internal" } } ] }, { "expressions": [ "req.host.startsWith('admin.')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://admin-service.internal" } } ] }, { "actions": [ { "type": "forward-internal", "config": { "url": "https://main-app.internal" } } ] } ] } ``` ## By path Route requests to different services based on URL path patterns. ### API versioning ```yaml policy.yml theme={null} on_http_request: # Route /api/v1/* to legacy API - expressions: - req.url.path.startsWith('/api/v1') actions: - type: forward-internal config: url: https://api-v1.internal # Route /api/v2/* to current API - expressions: - req.url.path.startsWith('/api/v2') actions: - type: forward-internal config: url: https://api-v2.internal # Unversioned /api/* defaults to latest - expressions: - req.url.path.startsWith('/api') actions: - type: forward-internal config: url: https://api-v2.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.url.path.startsWith('/api/v1')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://api-v1.internal" } } ] }, { "expressions": [ "req.url.path.startsWith('/api/v2')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://api-v2.internal" } } ] }, { "expressions": [ "req.url.path.startsWith('/api')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://api-v2.internal" } } ] } ] } ``` ### Microservice routing ```yaml policy.yml theme={null} on_http_request: # Route /users/* to users microservice - expressions: - req.url.path.startsWith('/users') actions: - type: forward-internal config: url: https://users-service.internal # Route /orders/* to orders microservice - expressions: - req.url.path.startsWith('/orders') actions: - type: forward-internal config: url: https://orders-service.internal # Route /payments/* to payments microservice - expressions: - req.url.path.startsWith('/payments') actions: - type: forward-internal config: url: https://payments-service.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.url.path.startsWith('/users')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://users-service.internal" } } ] }, { "expressions": [ "req.url.path.startsWith('/orders')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://orders-service.internal" } } ] }, { "expressions": [ "req.url.path.startsWith('/payments')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://payments-service.internal" } } ] } ] } ``` ## By header Route requests based on custom header values. Use this for customer-specific routing, feature flags, or A/B testing. ### Customer-specific routing ```yaml policy.yml theme={null} on_http_request: # Route to customer-specific service based on X-Customer-ID header - actions: - type: forward-internal config: url: https://${getReqHeader('X-Customer-ID')[0]}.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "forward-internal", "config": { "url": "https://${getReqHeader('X-Customer-ID')[0]}.internal" } } ] } ] } ``` ### Feature flag routing ```yaml policy.yml theme={null} on_http_request: # Route users with beta flag to new service - expressions: - "'beta' in req.headers['x-feature-flags']" actions: - type: forward-internal config: url: https://new-feature-service.internal # Everyone else gets the stable service - actions: - type: forward-internal config: url: https://stable-service.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "'beta' in req.headers['x-feature-flags']" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://new-feature-service.internal" } } ] }, { "actions": [ { "type": "forward-internal", "config": { "url": "https://stable-service.internal" } } ] } ] } ``` ## By cookie Route based on cookie values for session-based routing or sticky sessions. ### Route by session cookie ```yaml policy.yml theme={null} on_http_request: # Premium users get dedicated infrastructure - expressions: - "'session' in req.cookies && req.cookies['session'][0].value.contains('premium')" actions: - type: forward-internal config: url: https://premium-service.internal # Free tier users - actions: - type: forward-internal config: url: https://free-service.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "'session' in req.cookies && req.cookies['session'][0].value.contains('premium')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://premium-service.internal" } } ] }, { "actions": [ { "type": "forward-internal", "config": { "url": "https://free-service.internal" } } ] } ] } ``` ### A/B testing with cookies ```yaml policy.yml theme={null} on_http_request: # Experiment group A - expressions: - "'ab_test' in req.cookies && req.cookies['ab_test'][0].value == 'A'" actions: - type: forward-internal config: url: https://experiment-a.internal # Experiment group B - expressions: - "'ab_test' in req.cookies && req.cookies['ab_test'][0].value == 'B'" actions: - type: forward-internal config: url: https://experiment-b.internal # Control group (no cookie or other values) - actions: - type: forward-internal config: url: https://control.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "'ab_test' in req.cookies && req.cookies['ab_test'][0].value == 'A'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://experiment-a.internal" } } ] }, { "expressions": [ "'ab_test' in req.cookies && req.cookies['ab_test'][0].value == 'B'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://experiment-b.internal" } } ] }, { "actions": [ { "type": "forward-internal", "config": { "url": "https://control.internal" } } ] } ] } ``` ## By query parameter Route based on query string parameters. Use this for debug modes or testing environments. ### Debug routing ```yaml policy.yml theme={null} on_http_request: # Route ?debug=true to debug service with verbose logging - expressions: - "'debug' in req.url.query_params && req.url.query_params['debug'][0] == 'true'" actions: - type: forward-internal config: url: https://debug-service.internal # Normal production routing - actions: - type: forward-internal config: url: https://production-service.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "'debug' in req.url.query_params && req.url.query_params['debug'][0] == 'true'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://debug-service.internal" } } ] }, { "actions": [ { "type": "forward-internal", "config": { "url": "https://production-service.internal" } } ] } ] } ``` ### Environment routing ```yaml policy.yml theme={null} on_http_request: # Route ?env=staging to staging environment - expressions: - "'env' in req.url.query_params && req.url.query_params['env'][0] == 'staging'" actions: - type: forward-internal config: url: https://staging-service.internal # Route ?env=canary to canary deployment - expressions: - "'env' in req.url.query_params && req.url.query_params['env'][0] == 'canary'" actions: - type: forward-internal config: url: https://canary-service.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "'env' in req.url.query_params && req.url.query_params['env'][0] == 'staging'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://staging-service.internal" } } ] }, { "expressions": [ "'env' in req.url.query_params && req.url.query_params['env'][0] == 'canary'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://canary-service.internal" } } ] } ] } ``` ## By geographic location Route requests based on the geographic location of the client IP. Use this for compliance requirements or serving region-specific content. See [connection variables](/docs/gateway/traffic-policy/variables/connection/) for available geo fields. ### Route by country ```yaml policy.yml theme={null} on_http_request: # EU traffic stays in EU region for GDPR compliance - expressions: - conn.client_ip.geo.location.is_eu == true actions: - type: forward-internal config: url: https://eu-service.internal # US traffic routes to US region - expressions: - conn.client_ip.geo.location.country_code == 'US' actions: - type: forward-internal config: url: https://us-service.internal # All other traffic goes to global service - actions: - type: forward-internal config: url: https://global-service.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "conn.client_ip.geo.location.is_eu == true" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://eu-service.internal" } } ] }, { "expressions": [ "conn.client_ip.geo.location.country_code == 'US'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://us-service.internal" } } ] }, { "actions": [ { "type": "forward-internal", "config": { "url": "https://global-service.internal" } } ] } ] } ``` ### Route by continent ```yaml policy.yml theme={null} on_http_request: # Asia Pacific region - expressions: - conn.client_ip.geo.location.continent == 'Asia' actions: - type: forward-internal config: url: https://apac-service.internal # Europe, Middle East, Africa region - expressions: - conn.client_ip.geo.location.continent == 'Europe' actions: - type: forward-internal config: url: https://emea-service.internal # Americas region - expressions: - conn.client_ip.geo.location.continent in ['North America', 'South America'] actions: - type: forward-internal config: url: https://americas-service.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "conn.client_ip.geo.location.continent == 'Asia'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://apac-service.internal" } } ] }, { "expressions": [ "conn.client_ip.geo.location.continent == 'Europe'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://emea-service.internal" } } ] }, { "expressions": [ "conn.client_ip.geo.location.continent in ['North America', 'South America']" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://americas-service.internal" } } ] } ] } ``` ## By IP Intelligence variables Use [IP Intelligence](/docs/gateway/traffic-policy/variables/ip-intel/) to route traffic based on IP categories, reputation, or autonomous system information. This is useful for threat detection, bot management, and compliance. ### Route by autonomous system Route traffic based on the network it originates from. Use this for treating cloud provider traffic differently. ```yaml policy.yml theme={null} on_http_request: # Traffic from AWS networks - expressions: - conn.client_ip.as.organization.contains('AMAZON') actions: - type: forward-internal config: url: https://aws-optimized-service.internal # Traffic from GCP networks - expressions: - conn.client_ip.as.organization.contains('GOOGLE') actions: - type: forward-internal config: url: https://gcp-optimized-service.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "conn.client_ip.as.organization.contains('AMAZON')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://aws-optimized-service.internal" } } ] }, { "expressions": [ "conn.client_ip.as.organization.contains('GOOGLE')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://gcp-optimized-service.internal" } } ] } ] } ``` ### Route anonymous proxy traffic ```yaml policy.yml theme={null} on_http_request: # Tor exit nodes get captcha challenge - expressions: - "'proxy.anonymous.tor' in conn.client_ip.categories" actions: - type: forward-internal config: url: https://captcha-challenge.internal # VPN users get additional verification - expressions: - "'proxy.anonymous.vpn' in conn.client_ip.categories" actions: - type: forward-internal config: url: https://verification-service.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "'proxy.anonymous.tor' in conn.client_ip.categories" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://captcha-challenge.internal" } } ] }, { "expressions": [ "'proxy.anonymous.vpn' in conn.client_ip.categories" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://verification-service.internal" } } ] } ] } ``` ## By device type Route mobile users to a mobile-optimized service using [user agent variables](/docs/gateway/traffic-policy/variables/req/#requser-agent). ```yaml policy.yml theme={null} on_http_request: # Mobile devices get mobile-optimized experience - expressions: - req.user_agent.is_mobile == true actions: - type: forward-internal config: url: https://mobile-service.internal # Tablets get tablet-optimized experience - expressions: - req.user_agent.is_tablet == true actions: - type: forward-internal config: url: https://tablet-service.internal # Desktop users get full experience - actions: - type: forward-internal config: url: https://desktop-service.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.user_agent.is_mobile == true" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://mobile-service.internal" } } ] }, { "expressions": [ "req.user_agent.is_tablet == true" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://tablet-service.internal" } } ] }, { "actions": [ { "type": "forward-internal", "config": { "url": "https://desktop-service.internal" } } ] } ] } ``` ## By client certificate When [Mutual TLS (mTLS)](/docs/gateway/domains/tls-termination/#mutual-tls-mtls) is enabled, route requests based on client certificate details like the common name. ```yaml policy.yml theme={null} on_http_request: # Route based on client certificate common name - expressions: - conn.tls.client.subject.common_name == 'service-a' actions: - type: forward-internal config: url: https://service-a.internal - expressions: - conn.tls.client.subject.common_name == 'service-b' actions: - type: forward-internal config: url: https://service-b.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "conn.tls.client.subject.common_name == 'service-a'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://service-a.internal" } } ] }, { "expressions": [ "conn.tls.client.subject.common_name == 'service-b'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://service-b.internal" } } ] } ] } ``` *** ## Combine multiple conditions Create complex routing logic by combining multiple conditions. ### Route by path and method ```yaml policy.yml theme={null} on_http_request: # Write operations go to primary database - expressions: - req.url.path.startsWith('/api') - req.method in ['POST', 'PUT', 'DELETE', 'PATCH'] actions: - type: forward-internal config: url: https://primary-database.internal # Read operations can use read replica - expressions: - req.url.path.startsWith('/api') - req.method == 'GET' actions: - type: forward-internal config: url: https://read-replica.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.url.path.startsWith('/api')", "req.method in ['POST', 'PUT', 'DELETE', 'PATCH']" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://primary-database.internal" } } ] }, { "expressions": [ "req.url.path.startsWith('/api')", "req.method == 'GET'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://read-replica.internal" } } ] } ] } ``` ### Route by content type ```yaml policy.yml theme={null} on_http_request: # JSON API requests - expressions: - req.content_type == 'application/json' actions: - type: forward-internal config: url: https://json-api.internal # HTML form submissions - expressions: - req.content_type == 'application/x-www-form-urlencoded' actions: - type: forward-internal config: url: https://form-handler.internal # File uploads (multipart) - expressions: - req.content_type.startsWith('multipart/') actions: - type: forward-internal config: url: https://file-upload.internal ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.content_type == 'application/json'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://json-api.internal" } } ] }, { "expressions": [ "req.content_type == 'application/x-www-form-urlencoded'" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://form-handler.internal" } } ] }, { "expressions": [ "req.content_type.startsWith('multipart/')" ], "actions": [ { "type": "forward-internal", "config": { "url": "https://file-upload.internal" } } ] } ] } ``` *** ## Related resources * [Traffic Policy variables](/docs/gateway/traffic-policy/variables/) * [IP Intelligence variables](/docs/gateway/traffic-policy/variables/ip-intel/) * [CEL expressions](/docs/gateway/traffic-policy/how-it-works#expressions) * [`forward-internal` action](/docs/gateway/traffic-policy/actions/forward-internal/) * [A/B testing examples](/docs/gateway/traffic-policy/examples/a-b-tests/) # URL Rewrites Examples Source: https://ngrok.com/docs/gateway/traffic-policy/examples/url-rewrites Learn how to rewrite URLs using Traffic Policy to map user-friendly URLs to backend permalinks for better SEO and user experience. With Traffic Policy, you can map permalinks to "pretty" alternatives that are easier for both humans and [SEO bots](https://developers.google.com/search/docs/crawling-indexing/googlebot) to understand. This is especially useful for blogs that use a CMS, since the generated permalinks might be affecting your SEO performance. This rule rewrites a user-friendly URL like `/blog/11/example-title` to `/blog/index.php?p=11&title=example-title`, which is readable by your CMS. ```yaml policy.yml theme={null} on_http_request: - expressions: - req.url.path.startsWith('/blog') actions: - type: url-rewrite config: from: "/blog/([0-9]+)/([a-zA-Z]+)/" to: "/blog/index.php?p=$1&title=$2" ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.url.path.startsWith('/blog')" ], "actions": [ { "type": "url-rewrite", "config": { "from": "/blog/([0-9]+)/([a-zA-Z]+)/", "to": "/blog/index.php?p=$1&title=$2" } } ] } ] } ``` See [the `url-rewrite` Traffic Policy action docs](/docs/gateway/traffic-policy/actions/url-rewrite/) for more information. # User Agent Filtering Example Source: https://ngrok.com/docs/gateway/traffic-policy/examples/user-agent-filtering Learn how to filter traffic based on user agent strings using Traffic Policy to deliver tailored content or block specific bots. With Traffic Policy, ngrok automatically parses out useful information from the request's `User-Agent` header and makes them [available as variables](/docs/gateway/traffic-policy/variables/req/#requser-agentname). You can use them to deliver tailored content to users based on their browser and device, or to customize or block for SEO crawlers and bots. This rule delivers tailored content to Microsoft Edge users by matching on the `req.user_agent.name` value. ```yaml policy.yml theme={null} on_http_request: - expressions: - req.user_agent.name == 'Edge' actions: - type: custom-response config: status_code: 200 body: Hello Edge User! ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.user_agent.name == 'Edge'" ], "actions": [ { "type": "custom-response", "config": { "status_code": 200, "body": "Hello Edge User!" } } ] } ] } ``` You can also quickly block bots from your site by denying them by user-agent using the handy boolean flag. ```yaml policy.yml theme={null} on_http_request: - expressions: - req.user_agent.is_bot actions: - type: deny config: status_code: 403 ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "req.user_agent.is_bot" ], "actions": [ { "type": "deny", "config": { "status_code": 403 } } ] } ] } ``` Check out the [ngrok variable documentation](/docs/gateway/traffic-policy/variables/req/#requser-agentname) for all the values you can use in your policy. # Agent Endpoints with Traffic Policy Quickstart Source: https://ngrok.com/docs/gateway/traffic-policy/getting-started/agent-endpoints Learn how to set up an Agent Endpoint with a custom Traffic Policy. [Traffic Policy](/docs/gateway/traffic-policy/) enables you to manage traffic to your ngrok endpoints by dynamically implementing logic based on the request before it reaches your upstream services. This guide will teach you how to configure an [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints) with a custom Traffic Policy using the [ngrok Agent CLI](/docs/gateway/agent/cli/). YAML is whitespace-sensitive. Use spaces only (tabs are not valid in YAML) and keep keys at the same level aligned to the same column. ✅ Valid: ```yaml theme={null} endpoints: - name: example upstream: url: 80 ``` ❌ Invalid—`upstream` is indented inconsistently and will fail to parse: ```yaml theme={null} endpoints: - name: example upstream: url: 80 ``` There are two ways to define your Traffic Policy: * You can create a yaml or json file and manually apply it to specific endpoints when you start them. This approach is best when you want to apply a different Traffic Policy to each endpoint. * You can edit your [Agent Configuration File](/docs/gateway/agent/config/). This file applies to all endpoints you start with the agent CLI by default. Choose this approach to set up traffic management logic for all endpoints running locally on your machine. This guide will demonstrate both approaches. If you don't know which to choose, follow the instructions for using a separate policy file. ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/). * The [ngrok Agent CLI](https://ngrok.com/download) installed. ## 1. Set up your Traffic Policy Create a custom Traffic Policy file with the following contents: ```yaml theme={null} on_http_request: - actions: - type: custom-response config: status_code: 200 body: "Hello, World!" ``` This policy will respond to each HTTP request with a simple “Hello, World!” message. Your configuration file `version` [must be set to `3`](/docs/gateway/agent/config/v3). Enter the following command into your terminal: ```bash theme={null} ngrok config edit ``` In your configuration file, add the following content to the bottom and save the file: ```yaml theme={null} endpoints: - name: my-agent-endpoint description: My first Agent Endpoint with a Traffic Policy upstream: url: 80 traffic_policy: on_http_request: - actions: - type: custom-response config: status_code: 200 body: Hello, World! ``` This policy will respond to each HTTP request with “Hello, World!" ## 2. Start your endpoint Run the agent, applying the Traffic Policy you saved in the previous step with the `--traffic-policy-file` flag: ```bash theme={null} $ ngrok http 80 --traffic-policy-file policy.yml ``` This command starts an HTTP tunnel for port `80`, using the specified `policy.yml` Traffic Policy to manage traffic. Start your Agent Endpoint by running the following command: ```bash theme={null} ngrok start my-agent-endpoint ``` This command will start an HTTP tunnel for port `80`, using the specified Traffic Policy in the config file to manage traffic. ## 3. Test it out After running the ngrok command in the previous step you should now see a URL in the forwarding section. Open the URL in your web browser. You should see the "Hello, World!" message displayed in your browser. ## What's next? You've now successfully set up your first Agent Endpoint with a custom Traffic Policy using the ngrok agent. To learn more about ngrok's Traffic Policy and its capabilities, check out the following resources: * Learn [how the Traffic Policy engine works under the hood](/docs/gateway/traffic-policy/how-it-works). * Check out [the example gallery](/docs/gateway/traffic-policy/examples/) to see Traffic Policy in real-world scenarios. * The list of [available actions](/docs/gateway/traffic-policy/actions/), [macros](/docs/gateway/traffic-policy/macros/) and [variables](/docs/gateway/traffic-policy/variables/) you can use. # Cloud Endpoints with Traffic Policy Quickstart Source: https://ngrok.com/docs/gateway/traffic-policy/getting-started/cloud-endpoints Learn how to create Cloud Endpoints with Traffic Policies using the ngrok API for programmatic endpoint management. [Traffic Policy](/docs/gateway/traffic-policy/) enables you to manage traffic to your ngrok endpoints by dynamically implementing logic based on the request before it reaches your upstream services. This guide will teach you how to configure a [Cloud Endpoint](/docs/gateway/endpoints/cloud-endpoints) with a custom Traffic Policy. YAML is whitespace-sensitive. Use spaces only (tabs are not valid in YAML) and keep keys at the same level aligned to the same column. ✅ Valid: ```yaml theme={null} endpoints: - name: example upstream: url: 80 ``` ❌ Invalid—`upstream` is indented inconsistently and will fail to parse: ```yaml theme={null} endpoints: - name: example upstream: url: 80 ``` This guide walks you through using the ngrok dashboard, which is recommended for most cases. You can also [use the ngrok API](#using-the-api). ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/). * A [reserved domain](https://dashboard.ngrok.com/domains). ## Using the dashboard 1. In the ngrok dashboard, select the [Endpoints & Traffic Policy Tab](https://dashboard.ngrok.com/endpoints). 2. If you already have a Cloud Endpoint to use, select it and move on to the next step. Otherwise, follow these steps to create one. * At the top-right of the page, select [**New Endpoint**](https://dashboard.ngrok.com/endpoints/new). In the modal that appears, select Cloud Endpoint. * In the next step, select the Public binding. In the URL field add the reserved domain you created earlier. * Select Create Endpoint, and you should be taken to that endpoint's page. 3. After selecting or creating an endpoint, you'll be taken to the endpoint's page, where you can add a Traffic Policy. Add the following yaml to create a basic policy, then select **Save**. ```yaml title="Example policy" theme={null} on_http_request: - actions: - type: custom-response config: status_code: 200 body: "Hello, World!" ``` 4. After applying the policy, visit your reserved domain. You should see the "Hello, World!" message displayed in your browser. ## Using the API The best way to configure a Traffic Policy on a Cloud Endpoint is to [use the dashboard](#). To apply a Traffic Policy to a Cloud Endpoint using the ngrok API, you'll need: * The [ngrok Agent CLI](https://ngrok.com/download) installed. * Your [ngrok API key](https://dashboard.ngrok.com/api-keys). These instructions use the ngrok agent CLI to interact with the API. You can also [use the HTTP API](/docs/api-reference/endpoints/create#body-traffic-policy). ### 1. Add your API key to the Agent Create an [API key on your ngrok dashboard](https://dashboard.ngrok.com/api-keys) and then run the following in your terminal: ```bash theme={null} ngrok config add-api-key ``` ### 2. Create your Traffic Policy file Create a file named `policy.yml` with the following contents: ```yaml theme={null} on_http_request: - actions: - type: custom-response config: status_code: 200 body: "Hello, World!" ``` This will be the Traffic Policy used on your Cloud Endpoint to respond to each HTTP request with a simple “Hello, World!” message. ### 3. Create your Cloud Endpoint via the API Create your Cloud Endpoint via the API by running the following in your terminal (make sure to replace `` with your reserved domain): ```bash theme={null} ngrok api endpoints create \ --url https:// \ --traffic-policy "$( See the [Expression Examples](/docs/gateway/traffic-policy/concepts/expressions) documentation to explore expressions across different use cases. ## Actions Actions define the behavior that is applied when the expressions evaluate to true. Each action specifies a particular operation to be applied, such as denying traffic, modifying headers, or redirecting requests. Actions are executed sequentially as defined in the policy, but note that some actions can short-circuit the request and return without executing subsequent actions. For example, the following action denies traffic and returns a `404` HTTP status code: ```yaml theme={null} on_http_request: - actions: - type: "deny" config: status_code: 404 ``` For a full list of actions, check out the [Action Hub](/docs/gateway/traffic-policy/actions/). ### Non-terminating actions Some Actions can short-circuit a request and immediately return a response. These are called **terminating actions**. **Non-terminating actions** do not return a response, but instead go to the next Action in the chain. All **Cloud Endpoint** Traffic Policies must end with a terminating action. This requirement does not apply to **Agent Endpoints**. The list of terminating Traffic Policy Actions includes: * [`close-connection`](/docs/gateway/traffic-policy/actions/close-connection) * [`custom-response`](/docs/gateway/traffic-policy/actions/custom-response) * [`deny`](/docs/gateway/traffic-policy/actions/deny) * [`forward-internal`](/docs/gateway/traffic-policy/actions/forward-internal) ## Chaining rules and priority Multiple rules can be defined within a single phase. Rules are evaluated in the order they are defined, and their execution depends on the type of actions taken. For example: * Some actions (for example, `deny`) immediately stop further rule evaluation. * Others (for example, `url-rewrite`) allow subsequent rules to apply. This flexibility enables layered and powerful Traffic Policies. ## CEL Interpolation **CEL Interpolation** allows you to write dynamic configurations for Traffic Policy actions. It enables expressions to be embedded within string fields of a configuration object, making the Traffic Policy system more flexible and powerful. When an action in a Traffic Policy requires a configuration object, some fields allow CEL expressions to be interpolated directly into strings. This means that you can define dynamic values that are computed based on traffic, connection, or previously executed action variables. For example, when using the `add-headers` action, the `headers` field is a key-value map, where the header values can contain CEL expressions. This allows you to use real-time traffic data, such as the client's IP address, to populate header values dynamically. ### How CEL interpolation works In the context of Traffic Policy actions, CEL expressions can be written within strings using a special syntax: `${expression}`. This tells the system to evaluate the expression at runtime and replace the placeholder with the resulting value. For example, consider the following configuration for the `add-headers` action: ```yaml theme={null} on_http_request: - name: CEL Interpolation Example actions: - type: add-headers config: headers: X-Client-IP: "${conn.client_ip}" X-Request-Path: "${req.url.path}" ``` In this example: * The header `X-Client-IP` is set to the value of the client's IP address (`conn.client_ip`). * The header `X-Request-Path` dynamically reflects the path of the incoming request (`req.url.path`). When the rule is executed, the system evaluates the CEL expressions and substitutes the results into the configuration. This allows for highly customizable behavior based on real-time data. ### What can be interpolated? You can use the following inside of CEL interpolated expressions: * [Variables](/docs/gateway/traffic-policy/variables/) (including action variables) * [Macros](/docs/gateway/traffic-policy/macros/) This dynamic configuration enables Traffic Policies to be tailored based on specific conditions at runtime. ### Why use CEL interpolation? * **Flexibility**: Dynamically adjust action configurations based on traffic, headers, and more. * **Power**: Create complex, context-aware behaviors by utilizing real-time data and previous actions. * **Efficiency**: Avoid having to manually hard-code values or create separate policies for different traffic scenarios. ### Example use cases #### Custom header values Set headers dynamically based on incoming traffic, such as client IP, request path, or even specific action results. ```yaml theme={null} on_http_request: - name: CEL Interpolation Example actions: - type: add-headers config: headers: X-Country-Code: "${conn.geo.country_code}" X-User-Agent: "${req.headers['User-Agent'][0]}" ``` #### Debugging Take the result of an action and set it as metadata on the event stream log for the request or output the response as a custom response to see the result immediately, kind of like `console.log`. ```yaml theme={null} on_http_request: - actions: - type: "restrict-ips" config: enforce: false ip_policies: - "ipp_2p2q5Yt85IKQQ7zbpR4XmUpyVIN" - type: "log" config: metadata: message: "Restrict IPs action would be ${actions.ngrok.restrict_ips.action} for ${conn.client_ip}." matched_cidr: "${actions.ngrok.restrict_ips.matched_cidr}" error_code: "${actions.ngrok.restrict_ips.error.code}" error_message: "${actions.ngrok.restrict_ips.error.message}" - type: "custom-response" config: status_code: 403 body: "Restrict IPs action would be ${actions.ngrok.restrict_ips.action} for ${conn.client_ip}. The matched CIDR is ${actions.ngrok.restrict_ips.matched_cidr}. Your error code is ${actions.ngrok.restrict_ips.error.code} and the error message is the following: ${actions.ngrok.restrict_ips.error.message}" headers: content-type: "text/html" ``` ## What's next? * [Create your first Traffic Policy](/docs/gateway/traffic-policy/getting-started/agent-endpoints/) to start managing your traffic. * Explore [the Traffic Policy Action Hub](/docs/gateway/traffic-policy/actions/) to see all the Actions available to you. * Explore [practical examples](/docs/gateway/traffic-policy/examples/) to see Traffic Policies in action. # Traffic Identities Source: https://ngrok.com/docs/gateway/traffic-policy/identities Learn about Traffic Identities and Sessions for managing authenticated users via OAuth, OIDC, and SAML Traffic Policy actions. ## Overview Identities and Sessions give you live visibility of users who have authenticated to Endpoints via the [OAuth](/docs/gateway/traffic-policy/actions/oauth/), [OpenID Connect](/docs/gateway/traffic-policy/actions/oidc/) and [SAML](/docs/gateway/traffic-policy/actions/saml/) Traffic Policy actions. They enable you to: * Examine active user sessions to your authenticated endpoints including details like device, identity provider, IP address, and network * Revoke sessions to forcibly log users out via the dashboard or API You can manage Identities and Session on your [ngrok Dashboard](https://dashboard.ngrok.com/traffic-identities) or via the [ngrok API](#api). Traffic Identities were previously called 'App Users', you may find references with the old name while they are being transitioned. Diagram showing users authenticating via OAuth, OpenID Connect, or SAML through ngrok to reach apps, with app and user session data providing identity context ## Managing sessions from the Dashboard ### View sessions To view Traffic Identities and sessions: 1. In the [ngrok Dashboard](https://dashboard.ngrok.com), navigate to **Traffic Policy** > **Identities** (or access the [Identities](https://dashboard.ngrok.com/traffic-identities) page directly) The users are displayed in the table. 2. Alternatively, use the search bar to filter users by identity provider, User, and ID. ### View session details 1. Access the [Identities](https://dashboard.ngrok.com/traffic-identities) page. 2. On the Identities table, select a user: * ngrok displays the table with the user identity overview, including the provider who authenticated your user, basic information about the user, and the identity provider used for login * ngrok also displays a list of endpoints the user has accessed. 3. Click on an endpoint that the user has accessed. ngrok displays session information captured while the user was accessing the endpoint. That includes the user device, IP, browser, agent, country, and geo coordinates based on IP Pasting the coordinates into your favorite mapping service will give you more detailed information about the IP geolocation. ### Revoke sessions 1. Access the [Identities](https://dashboard.ngrok.com/traffic-identities) page and locate your user. 2. Click the trash can next to the user and then confirm the deletion. The user is deleted and all sessions are invalidated immediately. Users can reinitiate sessions by logging in again via their Authentication provider. ## Managing Sessions from the API You can also programmatically investigate and revoke user sessions using the [Identities](/docs/api-reference/applicationusers/list) and [App Sessions](/docs/api-reference/applicationsessions/list) APIs. APIs are the recommended approach when you want to drive session monitoring and deletion from third-party apps such as security management solutions and identity workflows. Examples: ### To list user sessions ```bash theme={null} curl --location --request \ GET 'https://api.ngrok.com/app/users' \ --header 'Ngrok-Version: 2' \ --header 'Authorization: Bearer {ngrok api token}' ``` ### To get session details ```bash theme={null} curl --location --request \ GET 'https://api.ngrok.com/app/users/{session-id}' \ --header 'Ngrok-Version: 2' \ --header 'Authorization: Bearer {ngrok api token}' ``` ### To revoke a session ```bash theme={null} curl --location --request \ DELETE 'https://api.ngrok.com/app/users/{session-id}' \ --header 'Ngrok-Version: 2' \ --header 'Authorization: Bearer {ngrok api token}' ``` ## API Identities and Sessions are managed programmatically via: * [/app/users API Resource](/docs/api-reference/applicationusers/list) * [/app/sessions API Resource](/docs/api-reference/applicationsessions/list) ## Traffic Policy identities pricing Identities and Session are available on all plans. # Traffic Policy Overview Source: https://ngrok.com/docs/gateway/traffic-policy/index Learn about ngrok's Traffic Policy for authenticating requests, rate limiting traffic, rewriting URLs, and applying security controls. ngrok's Traffic Policy is a configuration language that offers you the flexibility to filter, match, manage, and orchestrate traffic to your endpoints. * Validate incoming traffic, block malicious traffic, rewrite URLs, and respond with custom content. * Forward traffic to your agents running across the globe and orchestrate traffic across your infrastructure. * Add Traffic Policies to any type of ngrok endpoint to scope traffic management for each of your endpoints. Learn how Traffic Policy works under the hood. Create your first Traffic Policy and start an endpoint to see it in action. Actions are how you manage traffic in your Traffic Policies. They allow you to modify request headers, transform response bodies, and more. Explore a gallery of real-world examples to see how you can incorporate Traffic Policy into your setup. ## Features * [Actions](/docs/gateway/traffic-policy/actions/) - Transform, redirect, and manage traffic flow with actions like `add-headers`, `rate-limit`, and `jwt-validation`. * [Variables](/docs/gateway/traffic-policy/variables/) - Predefined values representing things like the IP address associated with a request, the URL of the endpoint accepting the traffic, and more, which can be used to dynamically manage traffic. * [Macros](/docs/gateway/traffic-policy/macros/) - Apply macros in rule conditions to refine traffic segmentation and configure actions with dynamic precision. ## Use cases Block malicious traffic, Tor exit nodes, bots, and specific IP addresses from accessing your endpoints. Add JWT validation, OAuth, OIDC, and basic authentication to secure your endpoints. Rate limit requests based on endpoint, IP address, or API key to protect your services. Rewrite URLs and paths to route traffic to different backend services or endpoints. ## What's next? * Get started by applying a Traffic Policy to your app with the [ngrok agent](/docs/gateway/traffic-policy/getting-started/agent-endpoints/). * Learn the [key concepts](/docs/gateway/traffic-policy/how-it-works) that make up a Traffic Policy and how it works. * Explore [practical examples](/docs/gateway/traffic-policy/examples/) to see Traffic Policies in action. # Traffic Policy Macros Reference Source: https://ngrok.com/docs/gateway/traffic-policy/macros/index Reference documentation for CEL macros available in Traffic Policy for simplifying expressions and dynamic configuration. ngrok offers a variety of CEL macros that can be used within the Traffic Policy engine to simplify [traffic management](../concepts/expressions) and [dynamic configuration](/docs/gateway/traffic-policy/how-it-works#cel-interpolation).\ These macros help you streamline traffic handling by referencing common values and conditions, making it easier to manage complex logic in your Traffic Policies. You can use these macros in combination with [expressions](../concepts/expressions) and [actions](../actions) to create dynamic, condition-based traffic flows. ## Base64 ### `base64.decode() -> bytes` Decodes base64-encoded string to bytes. This function will return an error if the string input is not base64-encoded. #### Signatures * `base64.decode() -> ` #### Example ```go theme={null} base64.decode('aGVsbG8=') // return b'hello' base64.decode('aGVsbG8') // error ``` ### `base64.encode() -> string` Encodes bytes to a base64-encoded string. #### Signatures * `base64.encode() -> ` #### Example ```go theme={null} base64.encode(b'hello') // return 'aGVsbG8=' ``` ## Basic Auth ### `basic_auth.encode(username, password) -> string` Encodes the passed username and password strings into a Base64 string for use in HTTP Basic Authentication.\ Appends the prefix `Basic ` to the encoded string. #### Example ```go theme={null} basic_auth.encode('username', 'password1') // Basic dXNlcm5hbWU6cGFzc3dvcmQx ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - basic_auth.encode('username', 'password1') == 'Basic dXNlcm5hbWU6cGFzc3dvcmQx' ``` ```json policy.json theme={null} { "expressions": [ "basic_auth.encode('username', 'password1') == 'Basic dXNlcm5hbWU6cGFzc3dvcmQx'" ] } ``` ## Bytes ### `bytes.size() -> int` Determines the number of bytes in a sequence. #### Signatures * `bytes.size() -> int` * `size(bytes) -> int` #### Example ```go theme={null} b'hello'.size() // 5 size(b'world!') // 6 ``` ## HTTP requests Available in [`on_http_request`](/docs/gateway/traffic-policy/how-it-works#phases) and [`on_http_response`](/docs/gateway/traffic-policy/how-it-works#phases) phases. ### `getReqCookie(string) -> cookie` Returns the cookie struct for the specified cookie name, if it exists on the request.\ If there are multiple cookies of the same name, the first from the ordering specified in the Cookie header will be returned. #### Example ```go theme={null} getReqCookie('session') ``` #### Example (expression) ```yaml policy.yml theme={null} expressions: - getReqCookie('session').secure ``` ```json policy.json theme={null} { "expressions": [ "getReqCookie('session').secure" ] } ``` ### `getReqHeader(string) -> list` Returns a list of header values for the provided key on the request.\ Header keys must be written in canonical format.\ Defaults to an empty list if the header is not present. #### Example ```go theme={null} getReqHeader('User-Agent') ``` #### Example (expression) ```yaml policy.yml theme={null} expressions: - getReqHeader('User-Agent').exists(v, v.matches('(?i)google-images')) ``` ```json policy.json theme={null} { "expressions": [ "getReqHeader('User-Agent').exists(v, v.matches('(?i)google-images'))" ] } ``` ### `getQueryParam(string) -> list` Returns a list of the query parameter values from the request URL for the specified key.\ Defaults to an empty list if the query param is not present. #### Example ```go theme={null} getQueryParam('search') ``` #### Example (expression) ```yaml policy.yml theme={null} expressions: - size(getQueryParam('q')) == 0 ``` ```json policy.json theme={null} { "expressions": [ "size(getQueryParam('q')) == 0" ] } ``` ### `hasQueryParam(string) -> bool` Returns `true` or `false` if the specified query parameter key is part of the request URL. #### Example ```go theme={null} hasQueryParam('query') ``` #### Example (expression) ```yaml policy.yml theme={null} expressions: - hasQueryParam('q') ``` ```json policy.json theme={null} { "expressions": [ "hasQueryParam('q')" ] } ``` ### `hasReqCookie(string) -> bool` Returns `true` or `false` if a cookie exists on the request with the specified name. #### Example ```go theme={null} hasReqCookie('session') ``` #### Example (expression) ```yaml policy.yml theme={null} expressions: - hasReqCookie('session') ``` ```json policy.json theme={null} { "expressions": [ "hasReqCookie('session')" ] } ``` ### `hasReqHeader(string) -> bool` Returns `true` or `false` if the provided header key is present on the request.\ Header keys must be written in canonical format. #### Example ```go theme={null} hasReqHeader('Authorization') ``` #### Example (expression) ```yaml policy.yml theme={null} expressions: - hasReqHeader('X-Version-Id') ``` ```json policy.json theme={null} { "expressions": [ "hasReqHeader('X-Version-Id')" ] } ``` ## HTTP responses Available in [`on_http_response`](/docs/gateway/traffic-policy/how-it-works#phases) phase only. ### `getResCookie(string) -> cookie` Returns the cookie struct for the specified cookie name, if it exists on the response.\ If there are multiple cookies of the same name, the cookie with the longest path will be returned. #### Example ```go theme={null} getResCookie('_device_id') ``` #### Example (expression) ```yaml policy.yml theme={null} expressions: - getResCookie('_device_id').value == 'mobile-phone-14' ``` ```json policy.json theme={null} { "expressions": [ "getResCookie('_device_id').value == 'mobile-phone-14'" ] } ``` ### `getResHeader(string) -> list` Returns a list of header values for the provided key on the response.\ Header keys must be written in canonical format. #### Example ```go theme={null} getResHeader('Content-Type') ``` #### Example (expression) ```yaml policy.yml theme={null} expressions: - size(getResHeader('Content-Type').filter(v, v.matches('application/json'))) > 0 ``` ```json policy.json theme={null} { "expressions": [ "size(getResHeader('Content-Type').filter(v, v.matches('application/json'))) > 0" ] } ``` ### `hasResCookie(string) -> bool` Returns `true` or `false` if a cookie exists on the response with the specified name. #### Example ```go theme={null} hasResCookie('_device_id') ``` #### Example (expression) ```yaml policy.yml theme={null} expressions: - hasResCookie('_device_id') ``` ```json policy.json theme={null} { "expressions": [ "hasResCookie('_device_id')" ] } ``` ### `hasResHeader(string) -> bool` Returns `true` or `false` if the provided header key is present on the response.\ Header keys must be written in canonical format.\ Defaults to an empty list if the header is not present. #### Example ```go theme={null} hasResHeader('Content-Type') ``` #### Example (expression) ```yaml policy.yml theme={null} expressions: - hasResHeader('Content-Type') ``` ```json policy.json theme={null} { "expressions": [ "hasResHeader('Content-Type')" ] } ``` ## Hash ### `hash.crc32(string) -> string` Computes the CRC32 checksum of a string using the IEEE polynomial.\ Returns the checksum in hexadecimal format by default. #### Signatures * `hash.crc32(string) -> string` (returns hex) * `hash.crc32(string, format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} hash.crc32('hello') // '3610a686' hash.crc32('hello', 'hex') // '3610a686' hash.crc32('hello', 'base64') // 'NhCmhg==' ``` ### `hash.crc64(string) -> string` Computes the CRC64 checksum of a string using the ISO polynomial.\ Returns the checksum in hexadecimal format by default. #### Signatures * `hash.crc64(string) -> string` (returns hex) * `hash.crc64(string, format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} hash.crc64('hello') // '3c3eeee2d8100000' hash.crc64('hello', 'hex') // '3c3eeee2d8100000' hash.crc64('hello', 'base64') // 'PD7u4tgQAAA=' ``` ### `hash.md5(string) -> string` MD5 is considered cryptographically broken and should not be used for security purposes. Use SHA-256 or higher for security-sensitive applications. Computes the MD5 hash of a string.\ Returns the hash in hexadecimal format by default. #### Signatures * `hash.md5(string) -> string` (returns hex) * `hash.md5(string, format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} hash.md5('hello') // '5d41402abc4b2a76b9719d911017c592' hash.md5('hello', 'hex') // '5d41402abc4b2a76b9719d911017c592' hash.md5('hello', 'base64') // 'XUFAKrxLKna5cZ2REBfFkg==' ``` ### `hash.sha1(string) -> string` SHA-1 is considered cryptographically weak and should not be used for security purposes. Use SHA-256 or higher for security-sensitive applications. Computes the SHA-1 hash of a string.\ Returns the hash in hexadecimal format by default. #### Signatures * `hash.sha1(string) -> string` (returns hex) * `hash.sha1(string, format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} hash.sha1('hello') // 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d' hash.sha1('hello', 'hex') // 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d' hash.sha1('hello', 'base64') // 'qvTGHdzF6KLavt4PO0gs2a6pQ00=' ``` ### `hash.sha256(string) -> string` Computes the SHA-256 hash of a string.\ Returns the hash in hexadecimal format by default. #### Signatures * `hash.sha256(string) -> string` (returns hex) * `hash.sha256(string, format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} hash.sha256('hello') // '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824' hash.sha256('hello', 'hex') // '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824' hash.sha256('hello', 'base64') // 'LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=' ``` ### `hash.sha512(string) -> string` Computes the SHA-512 hash of a string.\ Returns the hash in hexadecimal format by default. #### Signatures * `hash.sha512(string) -> string` (returns hex) * `hash.sha512(string, format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} hash.sha512('hello') // '9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043' hash.sha512('hello', 'hex') // '9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043' hash.sha512('hello', 'base64') // 'm3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==' ``` ### `hash.xxhash64(string) -> string` Computes the XXHash64 hash of a string.\ XXHash is a fast, high-quality hash function.\ Returns the hash in hexadecimal format by default. #### Signatures * `hash.xxhash64(string) -> string` (returns hex) * `hash.xxhash64(string, format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} hash.xxhash64('hello') // '26c7827d889f6da3' hash.xxhash64('hello', 'hex') // '26c7827d889f6da3' hash.xxhash64('hello', 'base64') // 'JseCfYifbaM=' ``` ## JSON ### `json.decode(string) -> list | map` Decodes the passed JSON string into a list or map. #### Example ```go theme={null} json.decode('{"a":"b"}') // map[a:b] json.decode('["a","b","c"]') // string["a","b","c"] ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - json.decode(getReqHeader('x-json-array')[0])[0] == "first entry" ``` ```json policy.json theme={null} { "expressions": [ "json.decode(getReqHeader('x-json-array')[0])[0] == \"first entry\"" ] } ``` ### `json.encode(list | map) -> string` Encodes the passed string into a JSON string. #### Example ```go theme={null} json.encode({a:b}) // {"a":"b"} json.encode(["a","b","c"]) // ["a","b","c"] ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - json.encode(req.content_type.parameters) == '{"charset":"UTF-8"}' ``` ```json policy.json theme={null} { "expressions": [ "json.encode(req.content_type.parameters) == '{\"charset\":\"UTF-8\"}'" ] } ``` ## Lists ### `list.all(x,p) -> bool` Checks if a predicate `p` holds for all elements of a list, where `x` is a variable name to be used in `p` as a reference to the element. #### Example ```go theme={null} [1, 2, 3].all(x, x > 0) // true ``` ### `list.encodeJson() -> string` Encodes the list as a JSON string. #### Example ```go theme={null} ["a","b","c"].encodeJson() // "[\"a\",\"b\",\"c\"]" ``` ### `list.exists(x,p) -> bool` Checks if a predicate `p` holds for at least one element of a list, where `x` is a variable name to be used in `p` as a reference to the element. #### Example ```go theme={null} [1, -2, 3].exists(x, x > 0) // true ``` ### `list.exists_one(x,p) -> bool` Checks if a predicate `p` holds for exactly one element of a list, where `x` is a variable name to be used in `p` as a reference to the element. #### Example ```go theme={null} [1, 2, 3].exists_one(x, x == 2) // true ``` ### `list.filter(x,p) -> list` Filters a list to include only elements that satisfy a condition, where `x` is a variable name to be used in `p` as a reference to the element. #### Example ```go theme={null} [1, 2, 3].filter(x, x > 1) // [2, 3] ``` ### `list.join() -> string` Returns a new string with the elements of the list concatenated.\ Optionally, a separator can be specified to insert between elements. #### Signatures * `>.join() -> ` #### Example ```go theme={null} ['hello', 'mellow'].join() // returns 'hellomellow' ['hello', 'mellow'].join(' ') // returns 'hello mellow' [].join() // returns '' [].join('/') // returns '' ``` ### `list.map(x,t) -> list` Transforms each element in a list by applying the function defined in the expression `t`,where `x` is a variable name to be used in `t` as a reference to the element. #### Example ```go theme={null} [1, 2, 3].map(n, n * n) // [1, 4, 9] ``` ### `list.map(x,p,t) -> list` Transforms each element in a list by applying the function defined in the expression `t` to elements that satisfy predicate `p`, where `x` is a variable name to be used in `p` as a reference to the element. #### Example ```go theme={null} [1, 2, 3, 4].map(num, num % 2 == 0, num * 2) // [4, 8] ``` ### `list.size() -> int` Determines the number of elements in a list. #### Signatures * `list.size() -> int` * `size(list) -> int` #### Example ```go theme={null} ['hello', 'world'].size() // 2 size(['hello', 'world']) // 2 ``` ### `list.slice() -> list` Returns a new sub-list using the indexes provided. #### Signatures * `.slice(, ) -> ` #### Example ```go theme={null} [1,2,3,4].slice(1, 3) // return [2, 3] [1,2,3,4].slice(2, 4) // return [3 ,4] ``` ### `list.flatten() -> list` Flattens a list recursively. If an optional depth is provided, the list is flattened to the specified level. A negative depth value will result in an error. #### Signatures * `.flatten() -> ` * `.flatten() -> ` #### Example ```go theme={null} [1,[2,3],[4]].flatten() // returns [1, 2, 3, 4] [1,[2,[3,4]]].flatten() // returns [1, 2, [3, 4]] [1,2,[],[],[3,4]].flatten() // returns [1, 2, 3, 4] [1,[2,[3,[4]]]].flatten(2) // returns [1, 2, 3, [4]] [1,[2,[3,[4]]]].flatten(-1) // error ``` ### `list.sort() -> list` Sorts a list with comparable elements. If the element type is not comparable or the element types are not the same, the function will produce an error. Comparable types include: int, uint, double, bool, duration, timestamp, string, bytes. #### Signatures * `.sort() -> ` #### Example ```go theme={null} [3, 2, 1].sort() // returns [1, 2, 3] ["b", "c", "a"].sort() // returns ["a", "b", "c"] [1, "b"].sort() // error [[1, 2, 3]].sort() // error ``` ### `list.sortBy(x, keyExpr) -> list` Sorts a list by a key value. The order is determined by the result of an expression applied to each element of the list. The output of the key expression must be a comparable type, otherwise the function will return an error. #### Signatures * `.sortBy(, ) -> ` #### Example ```go theme={null} [ Player { name: "foo", score: 0 }, Player { name: "bar", score: -10 }, Player { name: "baz", score: 1000 }, ].sortBy(e, e.score).map(e, e.name) // returns ["bar", "foo", "baz"] ``` ### `list.distinct() -> list` Returns the distinct elements of a list. #### Signatures * `.distinct() -> ` #### Example ```go theme={null} [1, 2, 2, 3, 3, 3].distinct() // returns [1, 2, 3] ["b", "b", "c", "a", "c"].distinct() // returns ["b", "c", "a"] [1, "b", 2, "b"].distinct() // returns [1, "b", 2] ``` ### `lists.range() -> list` Returns a list of integers from 0 to n-1. #### Signatures * `lists.range() -> ` #### Example ```go theme={null} lists.range(5) // returns [0, 1, 2, 3, 4] ``` ## Maps ### `map.all(x,p) -> bool` Checks if a predicate `p` holds for all elements of a map, where `x` is a variable name to be used in `p` as a reference to the key. #### Example ```go theme={null} {'one': 1, 'two': 2}.all(x, x.startsWith('o')) // false ``` ### `map.encodeJson() -> string` Encodes the map as a JSON string. #### Example ```go theme={null} {a:"b",c:"d"}.encodeJson() // "{\"a\":\"b\",\"c\":\"d\"}" ``` ### `map.encodeQueryString() -> string` Encodes the map as a URL query string. #### Example ```go theme={null} {a:"b",c:"d"}.encodeQueryString() // "a=b&c=d" ``` ### `map.exists(x,p) -> bool` Checks if a predicate `p` holds for at least one element of a map, where `x` is a variable name to be used in `p` as a reference to the key. #### Example ```go theme={null} {'one': 1, 'two': 2}.exists(x, x.startsWith('o')) // true ``` ### `map.exists_one(x,p) -> bool` Checks if a predicate `p` holds for exactly one element of a map, where `x` is a variable name to be used in `p` as a reference to the key. #### Example ```go theme={null} {'one': 1, 'two': 2}.exists_one(x, x == 'one') // true ``` ### `map.filter(x,p) -> list` Filters a map to include only keys that satisfy a condition, where `x` is a variable name to be used in `p` as a reference to the key. #### Example ```go theme={null} {'one': 1, 'two': 2}.filter(x, x.startsWith('o')) // ['one'] ``` ### `map.map(x,t) -> list` Transforms each key in a map by applying the function defined in the expression `t`, where `x` is a variable name to be used in `t` as a reference to the key. #### Example ```go theme={null} {'one': 1, 'two': 2}.map(k, k) // ['one', 'two'] ``` ### `map.map(x,p,t) -> list` Transforms each key in a map by applying the function defined in the expression `t` to keys that satisfy predicate `p`, where `x` is a variable name to be used in `p` as a reference to the key. #### Example ```go theme={null} {'one': 1, 'two': 2, 'three': 3}.map(k, k.startsWith('t'), k.upper()) // ['TWO', 'THREE'] ``` ### `map.size() -> int` Determines the number of entries in a map. #### Signatures * `map.size() -> int` * `size(map) -> int` #### Example ```go theme={null} {'hello': 'world'}.size() // 1 size({1: true, 2: false}) // 2 ``` ## Query string ### `queryString.decode(string) -> map` Decodes the supplied query string into a map. #### Example ```go theme={null} queryString.decode("a=b&c=d") // {a:b,c:d} ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - queryString.decode(req.url.query)["q"] == "hello" ``` ```json policy.json theme={null} { "expressions": [ "queryString.decode(req.url.query)[\"q\"] == \"hello\"" ] } ``` ### `queryString.encode(map) -> string` Encodes the passed map into a query string. #### Example ```go theme={null} queryString.encode({a:b,c:d}) // a=b&c=d ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - queryString.encode({"q":"policy"}) == req.url.query ``` ```json policy.json theme={null} { "expressions": [ "queryString.encode({\"q\":\"policy\"}) == req.url.query" ] } ``` ## Random ### `rand.double() -> double` Returns a random `double` between `0` and `1`. #### Example ```go theme={null} rand.double() >= 0.5 ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - rand.double() >= 0.5 ``` ```json policy.json theme={null} { "expressions": [ "rand.double() >= 0.5" ] } ``` ### `rand.int(min,max) -> int` Returns a random `int` between the provided `min` and `max` values.\ Only supports positive integers and `min` must be larger than the provided `max`.\ By default, `min` is `0` and `max` is `1`. #### Example ```go theme={null} rand.int() == 1 rand.int(0, 10) >= 5 ``` #### Examples (in expression) The following is an example of using `rand.int` with the default values: ```yaml policy.yml theme={null} expressions: - rand.int() == 1 ``` ```json policy.json theme={null} { "expressions": [ "rand.int() == 1" ] } ``` The following is an example of using `rand.int` with custom values: ```yaml policy.yml theme={null} expressions: - rand.int(0, 10) >= 5 ``` ```json policy.json theme={null} { "expressions": [ "rand.int(0, 10) >= 5" ] } ``` ## Secrets ### `secrets.get(string, string) -> string` Takes the vault name as the first argument and the secret name as the second argument.\ Returns the secret value. Security Macros allow you to access sensitive information directly in your Traffic Policies.\ Your ngrok account has a Vault that can store Secrets.\ Any secrets that you add to your vault will be available across your account on all Traffic Policies.\ Updates to these secrets will be reflected across all Traffic Policies automatically. #### How secrets are secured * Secrets are protected at rest using industry standard `AES-256` encryption * ngrok's REST API does not return secrets as part of any of its response payloads * REST API traffic is encrypted in-transit using `HTTP/S` and `TLS 1.2+` For more information on secrets, see [Traffic Policy Secrets](/docs/gateway/traffic-policy/secrets/). #### Example ```yaml theme={null} secrets.get("vault-name", "secret-name") ``` ## String ### `string.matches() -> bool` Tests whether a string matches a given RE2 regular expression.\ This function provides a simple way to validate patterns in strings. #### Signatures * `matches(string, regex) -> bool` * `string.matches(regex) -> bool` #### Example ```go theme={null} matches("foobar", "foo.*") // true "foobar".matches("foo.*") // true matches("hello", "^h.*o$") // true "hello".matches("^h.*o$") // true matches("test", "fail") // false "test".matches("fail") // false ``` ### `string.startsWith() -> bool` Tests whether a string starts with the specified prefix. #### Signatures * `string.startsWith(prefix) -> bool` #### Example ```go theme={null} "hello world".startsWith("hello") // true "foobar".startsWith("foo") // true "ngrok".startsWith("rocks") // false "test".startsWith("") // true ``` ### `string.endsWith() -> bool` Tests whether a string ends with the specified suffix. #### Signatures * `string.endsWith(suffix) -> bool` #### Example ```go theme={null} "hello world".endsWith("world") // true "foobar".endsWith("bar") // true "ngrok".endsWith("rocks") // false "test".endsWith("") // true ``` ### `string.contains() -> bool` Tests whether a string contains the specified substring. #### Signatures * `string.contains(substring) -> bool` #### Example ```go theme={null} "hello world".contains("world") // true "foobar".contains("baz") // false "ngrok".contains("gro") // true "test".contains("") // true ``` ### `string.charAt() -> string` Returns the character at the given position. If the position is negative, or greater than the length of the string, the function will produce an error. #### Signatures * `.charAt() -> ` #### Example ```go theme={null} 'hello'.charAt(4) // returns 'o' 'hello'.charAt(5) // returns '' 'hello'.charAt(-1) // error ``` ### `string.size() -> int` Determines the length of a string in terms of the number of Unicode codepoints. #### Signatures * `string.size() -> int` * `size(string) -> int` #### Example ```go theme={null} "hello".size() // 5 size("world!") // 6 ``` ### `string.indexOf() -> int` Returns the index of the first occurrence of a substring within the string.\ The function also accepts an optional position argument to start the search. #### Signatures * `.indexOf(, ) -> ` #### Example ```go theme={null} 'hello mellow'.indexOf('') // returns 0 'hello mellow'.indexOf('ello') // returns 1 'hello mellow'.indexOf('jello') // returns -1 'hello mellow'.indexOf('', 2) // returns 2 'hello mellow'.indexOf('ello', 2) // returns 7 ``` ### `string.format() -> string` Returns a new string with substitutions being performed, printf-style. The valid formatting clauses are `%s` (string), `%d` (integer), `%f` (double with fixed-point precision), `%e` (double in scientific notation), `%b` (binary integer or boolean), `%x` (lowercase hex), `%X` (uppercase hex), and `%o` (octal). #### Signatures * `.format() -> ` #### Example ```go theme={null} "hello %s".format(["world"]) // returns "hello world" "integer: %d".format([42]) // returns "integer: 42" "%f".format([3.14]) // returns "3.140000" "%x".format([26]) // returns "1a" "binary: %b".format([5]) // returns "binary: 101" "bool: %b".format([true]) // returns "bool: 1" ``` ### `string.lastIndexOf() -> int` Returns the integer index at the start of the last occurrence of the search string. If the search string is not found the function returns -1. The function also accepts an optional position which represents the last index to be considered as the beginning of the substring match. If the substring is the empty string, the index where the search starts is returned (string length or custom). #### Signatures * `.lastIndexOf() -> ` * `.lastIndexOf(, ) -> ` #### Example ```go theme={null} 'hello mellow'.lastIndexOf('') // returns 12 'hello mellow'.lastIndexOf('ello') // returns 7 'hello mellow'.lastIndexOf('jello') // returns -1 'hello mellow'.lastIndexOf('ello', 6) // returns 1 'hello mellow'.lastIndexOf('ello', 20) // error 'hello mellow'.lastIndexOf('ello', -1) // error ``` ### `string.split() -> list` Splits a string into a list of substrings using a specified separator.\ Optionally, a maximum number of splits can be defined. #### Signatures * `.split(, ) -> list` #### Example ```go theme={null} 'hello mellow'.split() // returns ['hello', 'mellow'] 'hello mellow'.split(' ', 1) // returns ['hello'] 'hello,mellow,hi'.split(',') // returns ['hello', 'mellow', 'hi'] 'hello,mellow,hi'.split(',', 2) // returns ['hello', 'mellow'] ``` ### `string.replace() -> string` Replaces occurrences of a substring with another string.\ Optionally, limits the number of replacements. #### Signatures * `.replace(, , ) -> ` #### Example ```go theme={null} 'hello mellow'.replace('l', 'L') // returns 'heLLo meLLow' 'hello mellow'.replace('l', 'L', 2) // returns 'heLLo mellow' 'hello mellow'.replace('x', 'X') // returns 'hello mellow' (no changes) ``` ### `string.decodeBase64() -> string` Decodes the Base64 string and returns it as a string. #### Example ```go theme={null} "c29tZTp0aGluZw==".decodeBase64() // "some:thing" ``` ### `string.decodeJson() -> map | list` Decodes the JSON string and returns it as a map or list. #### Example ```go theme={null} "{\"a\":\"b\"}".decodeJson() // map{a:b} ``` ### `string.decodeQueryString() -> map` Decodes the string as a URL query and returns a map with the query parameters. #### Example ```go theme={null} "a=b&c=d".decodeQueryString() // map{a:b,c:d} ``` ### `string.encodeBase64() -> string` Encodes the string and returns it as a base64 encoded string. #### Example ```go theme={null} "some:thing".encodeBase64() // "c29tZTp0aGluZw==" ``` ### `string.escapeUrl() -> string` Returns the string with percent encoding applied. #### Example ```go theme={null} "i;md/r/$y".escapeUrl() // returns i%3Bmd%2Fr%2F%24y ``` ### `string.isJson() -> bool` Checks if the string is valid JSON and returns true if so, otherwise false. #### Example ```go theme={null} '{"a":"b","c":"d"}'.isJson() // true 'not json'.isJson() // false ``` ### `string.isPrivateIp() -> bool` Checks if the string is a valid private IP address falling in the range of ``` 10.0.0.0 – 10.255.255.255 172.16.0.0 – 172.31.255.255 192.168.0.0 – 192.168.255.255 ``` as per [RFC 1918](https://tools.ietf.org/html/rfc1918).\ It returns true if so, otherwise false. #### Example ```go theme={null} '192.168.1.1'.isPrivateIp() // true ``` ### `string.isQueryString() -> bool` Checks if the string is valid Query String and returns true if so, otherwise false. #### Example ```go theme={null} 'a=b&c=d'.isQueryString() // true 'not a query string'.isQueryString() // false ``` ### `string.isURL() -> bool` Checks if the string is a valid URL and returns true if so, otherwise false. #### Example ```go theme={null} 'https://ngrok.com'.isURL() // true 'not a url'.isURL() // false ``` ### `string.lower() -> string` Lowercases a UTF-8 string. #### Example ```go theme={null} "Ärger in Österreich, ÉCOLE, İSTANBUL, ЙОГА!".lower() // returns "ärger in österreich, école, istanbul, йога!" ``` ### `string.upper() -> string` Uppercases a UTF-8 string. #### Example ```go theme={null} "Ärger in Österreich, ÉCOLE, İSTANBUL, ЙОГА!".upper() // returns "ÄRGER IN ÖSTERREICH, ÉCOLE, İSTANBUL, ЙОГА!" ``` ### `string.parseUrl() -> URL` Returns the provided string as a net [URL map](https://pkg.go.dev/net/url#URL) structure. #### Example ```go theme={null} "https://ngrok.com".parseUrl() // returns {host:ngrok.com, scheme:https} ``` ### `string.unescapeUrl() -> string` Decodes a percent-encoded string back to its original form. #### Example ```go theme={null} "i%3Bmd%2Fr%2F%24y".unescapeUrl() // returns i;md/r/$y ``` ### `string.md5() -> string` MD5 is considered cryptographically broken and should not be used for security purposes. Use SHA-256 or higher for security-sensitive applications. Computes the MD5 hash of the string.\ Returns the hash in hexadecimal format by default. #### Signatures * `string.md5() -> string` (returns hex) * `string.md5(format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} 'hello'.md5() // '5d41402abc4b2a76b9719d911017c592' 'hello'.md5('hex') // '5d41402abc4b2a76b9719d911017c592' 'hello'.md5('base64') // 'XUFAKrxLKna5cZ2REBfFkg==' ``` ### `string.sha1() -> string` SHA-1 is considered cryptographically weak and should not be used for security purposes. Use SHA-256 or higher for security-sensitive applications. Computes the SHA-1 hash of the string.\ Returns the hash in hexadecimal format by default. #### Signatures * `string.sha1() -> string` (returns hex) * `string.sha1(format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} 'hello'.sha1() // 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d' 'hello'.sha1('hex') // 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d' 'hello'.sha1('base64') // 'qvTGHdzF6KLavt4PO0gs2a6pQ00=' ``` ### `string.sha256() -> string` Computes the SHA-256 hash of the string.\ Returns the hash in hexadecimal format by default. #### Signatures * `string.sha256() -> string` (returns hex) * `string.sha256(format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} 'hello'.sha256() // '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824' 'hello'.sha256('hex') // '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824' 'hello'.sha256('base64') // 'LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=' ``` ### `string.sha512() -> string` Computes the SHA-512 hash of the string.\ Returns the hash in hexadecimal format by default. #### Signatures * `string.sha512() -> string` (returns hex) * `string.sha512(format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} 'hello'.sha512() // '9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043' 'hello'.sha512('hex') // '9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043' 'hello'.sha512('base64') // 'm3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==' ``` ### `string.crc32() -> string` Computes the CRC32 checksum of the string using the IEEE polynomial.\ Returns the checksum in hexadecimal format by default. #### Signatures * `string.crc32() -> string` (returns hex) * `string.crc32(format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} 'hello'.crc32() // '3610a686' 'hello'.crc32('hex') // '3610a686' 'hello'.crc32('base64') // 'NhCmhg==' ``` ### `string.crc64() -> string` Computes the CRC64 checksum of the string using the ISO polynomial.\ Returns the checksum in hexadecimal format by default. #### Signatures * `string.crc64() -> string` (returns hex) * `string.crc64(format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} 'hello'.crc64() // '3c3eeee2d8100000' 'hello'.crc64('hex') // '3c3eeee2d8100000' 'hello'.crc64('base64') // 'PD7u4tgQAAA=' ``` ### `string.xxhash64() -> string` Computes the XXHash64 hash of the string.\ XXHash is a fast, high-quality hash function.\ Returns the hash in hexadecimal format by default. #### Signatures * `string.xxhash64() -> string` (returns hex) * `string.xxhash64(format) -> string` (format: "hex" or "base64") #### Examples ```go theme={null} 'hello'.xxhash64() // '26c7827d889f6da3' 'hello'.xxhash64('hex') // '26c7827d889f6da3' 'hello'.xxhash64('base64') // 'JseCfYifbaM=' ``` ### `string.lowerAscii() -> string` Returns a new string where all ASCII characters are lower-cased. This function does not perform Unicode case-mapping for characters outside the ASCII range. #### Signatures * `.lowerAscii() -> ` #### Example ```go theme={null} 'TacoCat'.lowerAscii() // returns 'tacocat' 'TacoCÆt Xii'.lowerAscii() // returns 'tacocÆt xii' ``` ### `string.upperAscii() -> string` Returns a new string where all ASCII characters are upper-cased. This function does not perform Unicode case-mapping for characters outside the ASCII range. #### Signatures * `.upperAscii() -> ` #### Example ```go theme={null} 'TacoCat'.upperAscii() // returns 'TACOCAT' 'TacoCÆt Xii'.upperAscii() // returns 'TACOCÆT XII' ``` ### `string.substring() -> string` Returns the substring given a numeric range corresponding to character positions. Optionally may omit the trailing range for a substring from a given character position until the end of a string. Character offsets are 0-based with an inclusive start range and exclusive end range. It is an error to specify an end range that is lower than the start range, or for either the start or end index to be negative or exceed the string length. #### Signatures * `.substring() -> ` * `.substring(, ) -> ` #### Example ```go theme={null} 'tacocat'.substring(4) // returns 'cat' 'tacocat'.substring(0, 4) // returns 'taco' 'tacocat'.substring(-1) // error 'tacocat'.substring(2, 1) // error ``` ### `string.trim() -> string` Returns a new string which removes the leading and trailing whitespace in the target string. The trim function uses the Unicode definition of whitespace which does not include the zero-width spaces. #### Signatures * `.trim() -> ` #### Example ```go theme={null} ' \ttrim\n '.trim() // returns 'trim' ``` ### `string.reverse() -> string` Returns a new string whose characters are the same as the target string, only formatted in reverse order. #### Signatures * `.reverse() -> ` #### Example ```go theme={null} 'gums'.reverse() // returns 'smug' 'John Smith'.reverse() // returns 'htimS nhoJ' ``` ### `strings.quote() -> string` Takes the given string and makes it safe to print (without any formatting due to escape sequences). If any invalid UTF-8 characters are encountered, they are replaced with \uFFFD. #### Signatures * `strings.quote() -> ` #### Example ```go theme={null} strings.quote('single-quote with "double quote"') // returns '"single-quote with \"double quote\""' strings.quote("two escape sequences \a\n") // returns '"two escape sequences \\a\\n"' ``` ## Timestamps ### `ts.getDate() -> int` Extracts the day of the month (1-based indexing) from a timestamp. #### Signatures * `Timestamp.getDate() -> int` (in UTC) * `Timestamp.getDate(string) -> int` (with timezone) #### Example ```go theme={null} timestamp("2023-12-25T00:00:00Z").getDate() // 25 timestamp("2023-12-25T00:00:00Z").getDate("America/Los_Angeles") // 24 timestamp("2024-01-01T10:00:00Z").getDate() // 1 timestamp("2024-01-01T10:00:00Z").getDate("Asia/Tokyo") // 1 ``` ### `ts.getDayOfMonth() -> int` Returns the day of the month from a timestamp, using zero-based indexing. #### Signatures * `Timestamp.getDayOfMonth() -> int` (in UTC) * `Timestamp.getDayOfMonth(string) -> int` (with timezone) #### Example ```go theme={null} timestamp("2023-12-25T00:00:00Z").getDayOfMonth() // 24 timestamp("2023-12-25T00:00:00Z").getDayOfMonth("America/Los_Angeles") // 23 ``` ### `ts.getDayOfWeek() -> int` Returns the day of the week from a timestamp, using zero-based indexing (0 for Sunday). #### Signatures * `Timestamp.getDayOfWeek() -> int` (in UTC) * `Timestamp.getDayOfWeek(string) -> int` (with timezone) #### Example ```go theme={null} timestamp("2023-12-25T12:00:00Z").getDayOfWeek() // 1 (Monday) ``` ### `ts.getDayOfYear() -> int` Returns the day of the year from a timestamp, using zero-based indexing. #### Signatures * `Timestamp.getDayOfYear() -> int` (in UTC) * `Timestamp.getDayOfYear(string) -> int` (with timezone) #### Example ```go theme={null} timestamp("2023-12-25T12:00:00Z").getDayOfYear() // 358 ``` ### `ts.getFullYear() -> int` Returns the year from a timestamp. #### Signatures * `Timestamp.getFullYear() -> int` (in UTC) * `Timestamp.getFullYear(string) -> int` (with timezone) #### Example ```go theme={null} timestamp("2023-12-25T12:00:00Z").getFullYear() // 2023 ``` ### `ts.getHours() -> int` Returns the hour from a timestamp or converts a duration to hours. #### Signatures * `Timestamp.getHours() -> int` (in UTC) * `Timestamp.getHours(string) -> int` (with timezone) * `Duration.getHours() -> int` (convert the duration to hours) #### Example ```go theme={null} timestamp("2023-12-25T12:00:00Z").getHours() // 12 duration("3h").getHours() // 3 ``` ### `ts.getMilliseconds() -> int` Returns the milliseconds from a timestamp or the milliseconds portion of a duration. #### Signatures * `Timestamp.getMilliseconds() -> int` (in UTC) * `Timestamp.getMilliseconds(string) -> int` (with timezone) * `Duration.getMilliseconds() -> int` (extracts the milliseconds portion) #### Example ```go theme={null} timestamp("2023-12-25T12:00:00.500Z").getMilliseconds() // 500 duration("1.234s").getMilliseconds() // 234 ``` ### `ts.getMinutes() -> int` Returns the minutes from a timestamp or converts a duration to minutes. #### Signatures * `Timestamp.getMinutes() -> int` (in UTC) * `Timestamp.getMinutes(string) -> int` (with timezone) * `Duration.getMinutes() -> int` (convert the duration to minutes) #### Example ```go theme={null} timestamp("2023-12-25T12:30:00Z").getMinutes() // 30 duration("1h30m").getMinutes() // 90 ``` ### `ts.getMonth() -> int` Returns the month from a timestamp, using zero-based indexing (0 for January). #### Signatures * `Timestamp.getMonth() -> int` (in UTC) * `Timestamp.getMonth(string) -> int` (with timezone) #### Example ```go theme={null} timestamp("2023-12-25T12:00:00Z").getMonth() // 11 (December) ``` ### `ts.getSeconds() -> int` Returns the seconds from a timestamp or converts a duration to seconds. #### Signatures * `Timestamp.getSeconds() -> int` (in UTC) * `Timestamp.getSeconds(string) -> int` (with timezone) * `Duration.getSeconds() -> int` (convert the duration to seconds) #### Example ```go theme={null} timestamp("2023-12-25T12:30:30Z").getSeconds() // 30 duration("1m30s").getSeconds() // 90 ``` ## URL ### `url.escape(string) -> string` Returns the string with percent encoding applied. #### Example ```go theme={null} url.escape("i;md/r/$y") // returns i%3Bmd%2Fr%2F%24y ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - url.escape(req.raw) == '%2Ffoo' ``` ```json policy.json theme={null} { "expressions": [ "url.escape(req.raw) == '%2Ffoo'" ] } ``` ### `url.parse(string) -> URL` Returns the provided URL string as a net [URL map](https://pkg.go.dev/net/url#URL) structure. #### Example ```go theme={null} url.parse("https://ngrok.com") // returns {host:ngrok.com, scheme:https} ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - url.parse(req.url).host == 'ngrok.com' ``` ```json policy.json theme={null} { "expressions": [ "url.parse(req.url).host == 'ngrok.com'" ] } ``` ### `url.unescape(string) -> string` Decodes a percent-encoded string back to its original form. #### Example ```go theme={null} url.unescape("i%3Bmd%2Fr%2F%24y") // returns i;md/r/$y ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - url.unescape('%2Ffoo') == '/foo' ``` ```json policy.json theme={null} { "expressions": [ "url.unescape('%2Ffoo') == '/foo'" ] } ``` ## Utility ### `has(field) -> bool` Checks whether a field or property exists in a list or map. #### Example ```go theme={null} has(req.headers['x-custom-header']) ``` ### `inCidrRange(ip,cidr) -> bool` Evaluates whether the given IP address falls within the specified CIDR range. Returns `true` if the IP is within the range, and `false` if it is outside the range or if the provided CIDR is invalid. #### Example ```go theme={null} inCidrRange('192.168.1.100', '192.168.1.0/24') // true ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - inCidrRange(conn.client_ip, '66.249.66.1/24') ``` ```json policy.json theme={null} { "expressions": [ "inCidrRange(conn.client_ip, '66.249.66.1/24')" ] } ``` ### `inCidrRanges(ip,cidrs) -> bool` Checks if the given IP address falls within any of the specified CIDR ranges. Returns `true` if the IP is within at least one valid CIDR range, and `false` if it is not within any valid range.\ Invalid CIDR ranges are ignored. #### Example ```go theme={null} inCidrRanges('192.168.1.100', ['192.168.1.0/24', '10.0.0.0/8']) // true ``` #### Example (in expression) ```yaml policy.yml theme={null} expressions: - inCidrRanges(conn.client_ip, ['66.249.66.1/24', '2001:4860::/32']) ``` ```json policy.json theme={null} { "expressions": [ "inCidrRanges(conn.client_ip, ['66.249.66.1/24', '2001:4860::/32'])" ] } ``` # Using Vaults & Secrets with Traffic Policy Source: https://ngrok.com/docs/gateway/traffic-policy/secrets Store sensitive data in encrypted vaults and reference them dynamically in your Traffic Policies. Secrets enable you to store sensitive data in encrypted vaults and reference them dynamically in your Traffic Policies. This feature eliminates the need to hardcode passwords, API keys, and other sensitive values directly in policy YAML files. When you update a secret in a vault, it automatically rotates across all Traffic Policies that reference it, streamlining credential management. Secrets are supported in all Traffic Policy actions and fields that support CEL. Secrets interpolated into certain actions may appear in cleartext in Traffic Inspector when full capture mode is enabled ## How it works Vaults are secure containers that store your secrets. Each vault can contain multiple secrets, which are encrypted key-value pairs. Secrets are stored using AES-256 encryption at rest and transmitted over HTTPS with TLS 1.2+. When you reference a secret in a Traffic Policy, the value is evaluated at runtime and never persisted in policy documents or logs. The ngrok API never returns secret values in response payloads. Reference secrets in your Traffic Policy using the `secrets.get()` macro: ```yaml theme={null} secrets.get("vault-name", "secret-name") ``` The [macro](/docs/gateway/traffic-policy/macros/#secretsgetstring-string---string) dynamically retrieves the secret value from the specified vault at runtime. ## Using vaults and secrets To use secrets, you'll need to create a vault to store them in. REST APIs are provided on the ngrok service for both [Vaults](/docs/api-reference/vaults/list) and [Secrets](/docs/api-reference/secrets/list). ### Create a vault Use the ngrok Agent CLI to create a vault: ```shell theme={null} ngrok api vaults create --name "my-vault" --description "Description of vault purpose" ``` The response includes the vault ID, which you'll need when creating secrets within the vault: ```json theme={null} { "id": "vault_2yNPzuk6GjHrx3mlOCkJK42RsdR", "name": "my-vault", "description": "Description of vault purpose", "created_at": "2025-08-01T21:10:37Z", "uri": "https://api.ngrok.com/vaults/vault_2yNPzuk6GjHrx3mlOCkJK42RsdR" } ``` ### Create a secret Create a secret within a vault using the vault ID: ```shell theme={null} ngrok api secrets create \ --name "my-secret" \ --value "secret-value" \ --vault-id "vault_2yNPzuk6GjHrx3mlOCkJK42RsdR" \ --description "Optional description" ``` ## Common use cases for secrets ### Basic authentication ```yaml theme={null} on_http_request: - actions: - type: basic-auth config: credentials: - "user123:${secrets.get('my-vault', 'password')}" ``` ### Webhook verification ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: github secret: "${secrets.get('webhooks-vault', 'github-secret')}" ``` ## Audit events Secrets and vaults emit the following [audit events](/docs/obs/events/reference/#audit-events), all of which include the full resource details in the logs: ### Vault events | Log | Description | | - | - | | `vault_created.v0` | Triggered when a vault is created | | `vault_deleted.v0` | Triggered when a vault is deleted | | `vault_updated.v0` | Triggered when a vault is updated (name, description, metadata) | ### Secret events | Log | Description | | - | - | | `secret_created.v0` | Triggered when a secret is created | | `secret_deleted.v0` | Triggered when a secret is deleted | | `secret_updated.v0` | Triggered when a secret is updated | ## Limits Secrets and vaults are free to use. The plan limits below apply. | Plan | Vault Limit | Secret Limit | | - | - | - | | Free | 5 (hard limit) | 5 total (hard limit) | | Hobbyist | 5 (hard limit) | 25 total (hard limit) | | PayGo | 5 (configurable) | 500 default (configurable) | Please [contact support](mailto:support@ngrok.com) to configure limits for the PayGo plan. # Action Variables Source: https://ngrok.com/docs/gateway/traffic-policy/variables/action Reference documentation for action variables provided by Traffic Policy actions for use in subsequent rules and expressions. The following variables are made available for use in subsequent expressions and CEL interpolations after the action has run. Variable values will only apply to the last action execution, results are not concatenated. ### Add Headers

Map of headers that were added by the action.

### Basic Auth

Whether there were Basic Auth credentials presented in the Authorization header.

The username of the credentials presented.

Whether the presented basic auth credentials were authorized for this request.

### Compress Response

Indicates whether the body was already compressed before the action was applied. Returns true if no further compression was performed.

The compression algorithm selected and applied by the action, based on the client's request and action configuration.

### HTTP Request

Message for an error that occurred during the invocation of an action.

A list of HTTP responses for each request attempt.

The IP address of the host returning the response.

A map of HTTP response headers.

The HTTP Status code for the response attempt.

Time it took to receive the response.

The HTTP request.

The HTTP method of the request.

A map of HTTP request headers.

The request url.

The request body.

The last attempted HTTP response. Unlike `actions.ngrok.attempts[i]` this variable also contains the response body.

The IP address of the host returning the response.

A map of HTTP response headers.

The HTTP Status code for the response.

Time it took to receive the response.

The response body

### JWT Validation

The list of JSON Web Tokens (JWTs) processed by the action.

The header portion of the JWT, parsed into a key-value map containing metadata about the token.

The part of the HTTP request where the JWT was located (for example, header, body).

The specific header or body field that contained the JWT (for example, "Authorization").

The payload portion of the JWT, parsed into a key-value map representing the claims.

The signature portion of the JWT, encoded in base64, used for verifying token integrity.

Indicates whether the token passed verification. Returns true if verified successfully.

A machine-readable code describing an error that occurred during the action's execution.

A human-readable message providing details about an error that occurred during the action's execution.

### Log

A key-value map containing metadata that was logged during the action. Each key represents a metadata attribute, and the value provides its corresponding details.

### OAuth

Code for an error that occurred during the invocation of an action.

Message for an error that occurred during the invocation of an action.

Unique identifier for the ngrok Identity entity

Email address of the authorized user from the provider.

Name for the authorized user from the provider.

Identifier for the authorized user from the provider.

The current Identity session identifier for this request.

The access token from the provider.

The refresh token from the provider.

Timestamp when the current session will expire.

Returns true when the session timed out.

Returns true when the current session reached the max duration.

Returns true when ngrok updates the user information on the identity.

### OpenID Connect (OIDC)

Code for an error that occurred during the invocation of an action.

Message for an error that occurred during the invocation of an action.

Unique identifier for the ngrok Identity entity

Email address of the authorized user from the provider.

Name for the authorized user from the provider.

Identifier for the authorized user from the provider.

The current Identity session identifier for this request.

The identity token from the provider for the current user.

The access token from the provider.

The refresh token from the provider.

Timestamp when the current session will expire.

Returns true when the session timed out.

Returns true when the current session reached the max duration.

Returns true when ngrok updates the user information on the identity.

### Rate Limit

The key used for bucketing requests. This is the key used to group and track requests in the rate-limiting process, ensuring that the same bucket is subject to the rate limit across multiple requests.

Indicates whether the request was limited by the rate limit. If `true`, the request was rate-limited based on the configured limits for the specified bucket.

A machine-readable code describing an error that occurred during the action's execution.

A human-readable message providing details about an error that occurred during the action's execution.

### Redirect

A list of elements that were matched during redirection. These represent the request components (for example, path or query parameters) that triggered the action and resulted in the redirect.

The URL to which the traffic was redirected. This is the destination URL returned as part of the redirect response after the action was executed.

A machine-readable code describing an error that occurred during the action's execution.

A human-readable message providing details about an error that occurred during the action's execution.

### Remove Headers

A list of headers that were successfully removed by the action.

### Restrict IPs

The action taken for this request.

`allow` - If the request was permitted. `deny` - If the request was denied.

The CIDR block that matched the incoming client's IP address. If no match was found, this value will be empty.

A machine-readable code describing an error that occurred during the action's execution.

A human-readable message providing details about an error that occurred during the action's execution.

### URL Rewrite

List of elements that matched the URL before the rewrite action was applied. These can be specific parts of the URL, such as the domain, path, or query parameters, that were matched based on the action configuration.

The final URL after the rewrite action has been applied. This is the new URL to which the original request is redirected after the specified modifications have been made.

A machine-readable code describing an error that occurred during the action's execution.

A human-readable message providing details about an error that occurred during the action's execution.

### Verify Webhook

Indicates whether or not the request was successfully verified.

Code for an error that occurred during the invocation of an action.

Message for an error that occurred during the invocation of an action.

# Connection Variables Source: https://ngrok.com/docs/gateway/traffic-policy/variables/connection Reference documentation for connection variables in Traffic Policy including client IP, geo-location, TLS details, Kubernetes pod identity, and IP intelligence data. ## Connection Variables The following variables are available under the `conn` namespace: | Name | Type | Description | | - | - | - | | [`conn.bytes_in`](#connbytes-in) | `int64` | The number of bytes entering the endpoint from the client. | | [`conn.bytes_out`](#connbytes-out) | `int64` | The number of bytes leaving an endpoint to the client. | | [`conn.client_ip`](#connclient-ip) | `string` | Source IP of the connection to the ngrok endpoint. | | [`conn.client_port`](#connclient-port) | `int32` | Source port of the connection to the ngrok endpoint. | | [`conn.server_ip`](#connserver-ip) | `string` | The IP that this connection was established on. | | [`conn.server_port`](#connserver-port) | `int32` | The port that this connection was established on. | | [`conn.server_region`](#connserver-region) | `string` | The ngrok [PoP (Point of Presence)](/docs/gateway/points-of-presence/) that this connection was established on and serviced through. | | [`conn.ts.start`](#conntsstart) | `timestamp` | Timestamp when the connection to ngrok was started. | ### `conn.bytes_in` The number of bytes entering the endpoint from the client. ```yaml policy.yml theme={null} expressions: - "conn.bytes_in > 1000" ``` ```json policy.json theme={null} { "expressions": [ "conn.bytes_in > 1000" ] } ``` ### `conn.bytes_out` The number of bytes leaving an endpoint to the client. ```yaml policy.yml theme={null} expressions: - "conn.bytes_out > 1000" ``` ```json policy.json theme={null} { "expressions": [ "conn.bytes_out > 1000" ] } ``` ### `conn.client_ip` Source IP of the connection to the ngrok endpoint. ```yaml policy.yml theme={null} expressions: - "conn.client_ip in ['::1', '127.0.0.1']" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip in ['::1', '127.0.0.1']" ] } ``` ### `conn.client_port` Source port of the connection to the ngrok endpoint. ```yaml policy.yml theme={null} expressions: - "conn.client_port == 80" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_port == 80" ] } ``` ### `conn.server_ip` The IP that this connection was established on. ```yaml policy.yml theme={null} expressions: - "conn.server_ip == '192.168.1.1'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip == '192.168.1.1'" ] } ``` ### `conn.server_port` The port that this connection was established on. ```yaml policy.yml theme={null} expressions: - "conn.server_port == 80" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_port == 80" ] } ``` ### `conn.server_region` The ngrok [PoP (Point of Presence)](/docs/gateway/points-of-presence/) that this connection was established on and serviced through. ```yaml policy.yml theme={null} expressions: - "conn.server_region == 'eu'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_region == 'eu'" ] } ``` ### `conn.ts.start` Timestamp when the connection to ngrok was started. ```yaml policy.yml theme={null} expressions: - "conn.ts.start > timestamp('2023-12-31T00:00:00Z')" ``` ```json policy.json theme={null} { "expressions": [ "conn.ts.start > timestamp('2023-12-31T00:00:00Z')" ] } ``` ## Connection Geo Variables The following variables are available under the `conn.geo` namespace: | Name | Type | Description | | - | - | - | | [`conn.geo.city`](#conngeocity) | `string` | The name of the city, in EN, where the `conn.client_ip` is likely to originate. | | [`conn.geo.country`](#conngeocountry) | `string` | The name of the country, in EN, where the `conn.client_ip` is likely to originate. | | [`conn.geo.country_code`](#conngeocountry-code) | `string` | The two-letter ISO country code where the `conn.client_ip` is likely to originate. | | [`conn.geo.latitude`](#conngeolatitude) | `string` | The approximate latitude where the `conn.client_ip` is likely to originate. | | [`conn.geo.longitude`](#conngeolongitude) | `string` | The approximate longitude where the `conn.client_ip` is likely to originate. | | [`conn.geo.radius`](#conngeoradius) | `string` | The radius in kilometers around the latitude and longitude where the `conn.client_ip` is likely to originate. | | [`conn.geo.subdivision`](#conngeosubdivision) | `string` | The name of the subdivision, in EN, where the `conn.client_ip` is likely to originate. | ### `conn.geo.city` The name of the city, in EN, where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.geo.city == 'Strongsville'" ``` ```json policy.json theme={null} { "expressions": [ "conn.geo.city == 'Strongsville'" ] } ``` ### `conn.geo.country` The name of the country, in EN, where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.geo.country == 'United States'" ``` ```json policy.json theme={null} { "expressions": [ "conn.geo.country == 'United States'" ] } ``` ### `conn.geo.country_code` The two-letter ISO country code where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.geo.country_code != 'US'" ``` ```json policy.json theme={null} { "expressions": [ "conn.geo.country_code != 'US'" ] } ``` ### `conn.geo.latitude` The approximate latitude where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "double(conn.geo.latitude) >= 45.0" ``` ```json policy.json theme={null} { "expressions": [ "double(conn.geo.latitude) >= 45.0" ] } ``` ### `conn.geo.longitude` The approximate longitude where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "double(conn.geo.longitude) <= -93.0" ``` ```json policy.json theme={null} { "expressions": [ "double(conn.geo.longitude) <= -93.0" ] } ``` ### `conn.geo.radius` The radius in kilometers around the latitude and longitude where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.geo.radius <= '5'" ``` ```json policy.json theme={null} { "expressions": [ "conn.geo.radius <= '5'" ] } ``` ### `conn.geo.subdivision` The name of the subdivision, in EN, where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.geo.subdivision == 'California'" ``` ```json policy.json theme={null} { "expressions": [ "conn.geo.subdivision == 'California'" ] } ``` ## Connection TLS Variables The following variables are available under the `conn.tls` namespace: | Name | Type | Description | | - | - | - | | [`conn.tls.cipher_suite`](#conntlscipher-suite) | `string` | The cipher suite selected during the TLS handshake. | | [`conn.tls.ja4_fingerprint`](#conntlsja4-fingerprint) | `string` | The JA4 fingerprint of the TLS handshake. | | [`conn.tls.negotiated_alpn`](#conntlsnegotiated-alpn) | `string` | TLS Application-Layer Protocol Negotiation (ALPN) Protocol ID of the protocol agreed upon in the TLS handshake | | [`conn.tls.session_resumed`](#conntlssession-resumed) | `bool` | True if the TLS session was resumed. Currently always false | | [`conn.tls.sni`](#conntlssni) | `string` | The hostname included in the `ClientHello` message via the SNI extension. | | [`conn.tls.version`](#conntlsversion) | `string` | The version of the TLS protocol used between the client and the ngrok edge. | ### `conn.tls.cipher_suite` The cipher suite selected during the TLS handshake. ```yaml policy.yml theme={null} expressions: - "conn.tls.cipher_suite == 'TLS_AES_128_GCM_SHA256'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.cipher_suite == 'TLS_AES_128_GCM_SHA256'" ] } ``` ### `conn.tls.ja4_fingerprint` The JA4 fingerprint of the TLS handshake. ```yaml policy.yml theme={null} expressions: - "conn.tls.ja4_fingerprint == 't13d1717h2_5b57614c22b0_f0fc7018f8e8'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.ja4_fingerprint == 't13d1717h2_5b57614c22b0_f0fc7018f8e8'" ] } ``` ### `conn.tls.negotiated_alpn` The TLS Application-Layer Protocol Negotiation (ALPN) Protocol ID of the protocol agreed upon in the TLS handshake. Defaults to `""` if no ALPN was successfully negotiated. ```yaml policy.yml theme={null} expressions: - "conn.tls.negotiated_alpn == 'h2'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.negotiated_alpn == 'h2'" ] } ``` ### `conn.tls.session_resumed` True if the TLS session was resumed. Currently always false as we do not yet support TLS session resumption. ```yaml policy.yml theme={null} expressions: - "conn.tls.session_resumed == false" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.session_resumed == false" ] } ``` ### `conn.tls.sni` The hostname included in the `ClientHello` message via the SNI extension. ```yaml policy.yml theme={null} expressions: - "conn.tls.sni == 'client.example.com'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.sni == 'client.example.com'" ] } ``` ### `conn.tls.version` The version of the TLS protocol used between the client and the ngrok edge. ```yaml policy.yml theme={null} expressions: - "conn.tls.version == '1.3'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.version == '1.3'" ] } ``` ## Connection TLS Client Variables The following variables are available under the `conn.tls.client` namespace: | Name | Type | Description | | - | - | - | | [`conn.tls.client.extensions`](#conntlsclientextensions) | `[]Extension` | Additional information added to the certificate. | | [`conn.tls.client.extensions[i].id`](#conntlsclientextensionsiid) | `string` | The identifier (OID) that specifies the type of extension. | | [`conn.tls.client.extensions[i].critical`](#conntlsclientextensionsicritical) | `bool` | True if the extension is critical. | | [`conn.tls.client.extensions[i].value`](#conntlsclientextensionsivalue) | `[]byte` | The data for the extension. | | [`conn.tls.client.issuer`](#conntlsclientissuer) | `string` | The issuing authority of the certificate as a string roughly following the RFC 2253 Distinguished Names syntax. | | [`conn.tls.client.issuer.common_name`](#conntlsclientissuercommon-name) | `string` | Common name of the issuing authority, usually the domain name. | | [`conn.tls.client.issuer.country`](#conntlsclientissuercountry) | `[]string` | Country names where the issuing authority is located. | | [`conn.tls.client.issuer.locality`](#conntlsclientissuerlocality) | `[]string` | Locality or city of the issuing authority. | | [`conn.tls.client.issuer.organization`](#conntlsclientissuerorganization) | `[]string` | Name of the organization that issued the certificate. | | [`conn.tls.client.issuer.organizational_unit`](#conntlsclientissuerorganizational-unit) | `[]string` | Division of the organization responsible for the certificate. | | [`conn.tls.client.issuer.postal_code`](#conntlsclientissuerpostal-code) | `[]string` | Postal code of the issuing authority. | | [`conn.tls.client.issuer.province`](#conntlsclientissuerprovince) | `[]string` | Province or state of the issuing authority. | | [`conn.tls.client.issuer.street_address`](#conntlsclientissuerstreet-address) | `[]string` | Street address of the issuing authority. | | [`conn.tls.client.pem`](#conntlsclientpem) | `string` | Full PEM-encoded client certificate of the TLS connection. | | [`conn.tls.client.san`](#conntlsclientsan) | `string` | Subject alternative names of the client certificate. | | [`conn.tls.client.san.dns_names`](#conntlsclientsandns-names) | `[]string` | DNS names in the subject alternative names. | | [`conn.tls.client.san.email_addresses`](#conntlsclientsanemail-addresses) | `[]string` | Email addresses in the subject alternative names. | | [`conn.tls.client.san.ip_addresses`](#conntlsclientsanip-addresses) | `[]string` | IP addresses in the subject alternative names. | | [`conn.tls.client.san.uris`](#conntlsclientsanuris) | `[]string` | URIs in the subject alternative names. | | [`conn.tls.client.serial_number`](#conntlsclientserial-number) | `string` | Unique identifier for the certificate. | | [`conn.tls.client.signature_algorithm`](#conntlsclientsignature-algorithm) | `string` | Algorithm used to sign the certificate. | | [`conn.tls.client.subject`](#conntlsclientsubject) | `string` | The entity to whom the certificate is issued as a string roughly following the RFC 2253 Distinguished Names syntax. | | [`conn.tls.client.subject.common_name`](#conntlsclientsubjectcommon-name) | `string` | Common name of the subject, usually the domain name. | | [`conn.tls.client.subject.country`](#conntlsclientsubjectcountry) | `[]string` | Country names where the subject of the certificate is located. | | [`conn.tls.client.subject.locality`](#conntlsclientsubjectlocality) | `[]string` | Locality or city where the subject is located. | | [`conn.tls.client.subject.organization`](#conntlsclientsubjectorganization) | `[]string` | Name of the organization to which the subject belongs. | | [`conn.tls.client.subject.organizational_unit`](#conntlsclientsubjectorganizational-unit) | `[]string` | Division of the organization to which the subject belongs. | | [`conn.tls.client.subject.postal_code`](#conntlsclientsubjectpostal-code) | `[]string` | Postal code where the subject is located. | | [`conn.tls.client.subject.province`](#conntlsclientsubjectprovince) | `[]string` | Province or state where the subject is located. | | [`conn.tls.client.subject.street_address`](#conntlsclientsubjectstreet-address) | `[]string` | Street address where the subject is located. | | [`conn.tls.client.validity.not_after`](#conntlsclientvaliditynot-after) | `timestamp` | Expiration date and time when the certificate is no longer valid. | | [`conn.tls.client.validity.not_before`](#conntlsclientvaliditynot-before) | `timestamp` | Start date and time when the certificate becomes valid. | ### `conn.tls.client.extensions` Additional information added to the certificate. ```yaml policy.yml theme={null} expressions: - "size(conn.tls.client.extensions) > 0" ``` ```json policy.json theme={null} { "expressions": [ "size(conn.tls.client.extensions) > 0" ] } ``` ### `conn.tls.client.extensions[i].id` The identifier (OID) that specifies the type of extension. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.extensions[0].id == '2.5.29.15'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.extensions[0].id == '2.5.29.15'" ] } ``` ### `conn.tls.client.extensions[i].critical` True if the extension is critical. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.extensions[0].critical" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.extensions[0].critical" ] } ``` ### `conn.tls.client.extensions[i].value` The data for the extension. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.extensions[0].value == b' '" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.extensions[0].value == b'\u0003\u0002\u0005 '" ] } ``` ### `conn.tls.client.issuer` The issuing authority of the certificate as a string roughly following the RFC 2253 Distinguished Names syntax. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.issuer == 'CN=E1,O=Let's Encrypt,C=US'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.issuer == 'CN=E1,O=Let's Encrypt,C=US'" ] } ``` ### `conn.tls.client.issuer.common_name` Common name of the issuing authority, usually the domain name. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.issuer.common_name == 'exampleca.com'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.issuer.common_name == 'exampleca.com'" ] } ``` ### `conn.tls.client.issuer.country` Country names where the issuing authority is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.issuer.country == ['US']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.issuer.country == ['US']" ] } ``` ### `conn.tls.client.issuer.locality` Locality or city of the issuing authority. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.issuer.locality == ['Mountain View']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.issuer.locality == ['Mountain View']" ] } ``` ### `conn.tls.client.issuer.organization` Name of the organization that issued the certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.issuer.organization == ['Example CA']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.issuer.organization == ['Example CA']" ] } ``` ### `conn.tls.client.issuer.organizational_unit` Division of the organization responsible for the certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.issuer.organizational_unit == ['Certification Authority Division']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.issuer.organizational_unit == ['Certification Authority Division']" ] } ``` ### `conn.tls.client.issuer.postal_code` Postal code of the issuing authority. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.issuer.postal_code == ['94043']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.issuer.postal_code == ['94043']" ] } ``` ### `conn.tls.client.issuer.province` Province or state of the issuing authority. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.issuer.province == ['California']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.issuer.province == ['California']" ] } ``` ### `conn.tls.client.issuer.street_address` Street address of the issuing authority. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.issuer.street_address == ['1234 Encryption Way']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.issuer.street_address == ['1234 Encryption Way']" ] } ``` ### `conn.tls.client.pem` Full PEM-encoded client certificate of the TLS connection, with `\n` used for newlines. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.pem.starts_with('-----BEGIN CERTIFICATE-----')" - "conn.tls.client.pem.ends_with('-----END CERTIFICATE-----')" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.pem.starts_with('-----BEGIN CERTIFICATE-----')", "conn.tls.client.pem.ends_with('-----END CERTIFICATE-----')" ] } ``` ### `conn.tls.client.san` Subject alternative names of the client certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.san == 'DNS:www.example.com, DNS:example.com, IP Address:192.168.1.1'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.san == 'DNS:www.example.com, DNS:example.com, IP Address:192.168.1.1'" ] } ``` ### `conn.tls.client.san.dns_names` DNS names in the subject alternative names. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.san.dns_names == ['www.example.com', 'example.com']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.san.dns_names == ['www.example.com', 'example.com']" ] } ``` ### `conn.tls.client.san.email_addresses` Email addresses in the subject alternative names. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.san.email_addresses == ['ngrok-email1@example.com', 'ngrok-email2@example.com']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.san.email_addresses == ['ngrok-email1@example.com', 'ngrok-email2@example.com']" ] } ``` ### `conn.tls.client.san.ip_addresses` IP addresses in the subject alternative names. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.san.ip_addresses == ['192.168.1.1']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.san.ip_addresses == ['192.168.1.1']" ] } ``` ### `conn.tls.client.san.uris` URIs in the subject alternative names. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.san.uris == ['https://example.com/example']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.san.uris == ['https://example.com/example']" ] } ``` ### `conn.tls.client.serial_number` Unique identifier for the certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.serial_number == 'b53017e79d4a5208b314a55d3574e0a8'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.serial_number == 'b53017e79d4a5208b314a55d3574e0a8'" ] } ``` ### `conn.tls.client.signature_algorithm` Algorithm used to sign the certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.signature_algorithm == 'SHA256-RSA'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.signature_algorithm == 'SHA256-RSA'" ] } ``` ### `conn.tls.client.subject` The entity to whom the certificate is issued as a string roughly following the RFC 2253 Distinguished Names syntax. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.subject == 'CN=www.example.com'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.subject == 'CN=www.example.com'" ] } ``` ### `conn.tls.client.subject.common_name` Common name of the subject, usually the domain name. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.subject.common_name == 'www.example.com'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.subject.common_name == 'www.example.com'" ] } ``` ### `conn.tls.client.subject.country` Country names where the subject of the certificate is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.subject.country == ['US']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.subject.country == ['US']" ] } ``` ### `conn.tls.client.subject.locality` Locality or city where the subject is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.subject.locality == ['Mountain View']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.subject.locality == ['Mountain View']" ] } ``` ### `conn.tls.client.subject.organization` Name of the organization to which the subject belongs. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.subject.organization == ['Example Corp']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.subject.organization == ['Example Corp']" ] } ``` ### `conn.tls.client.subject.organizational_unit` Division of the organization to which the subject belongs. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.subject.organizational_unit == ['Web Services']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.subject.organizational_unit == ['Web Services']" ] } ``` ### `conn.tls.client.subject.postal_code` Postal code where the subject is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.subject.postal_code == ['94043']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.subject.postal_code == ['94043']" ] } ``` ### `conn.tls.client.subject.province` Province or state where the subject is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.subject.province == ['California']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.subject.province == ['California']" ] } ``` ### `conn.tls.client.subject.street_address` Street address where the subject is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.subject.street_address == ['1234 Secure Blvd']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.subject.street_address == ['1234 Secure Blvd']" ] } ``` ### `conn.tls.client.validity.not_after` Expiration date and time when the certificate is no longer valid. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.validity.not_after == timestamp('2023-01-01T00:00:00Z')" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.validity.not_after == timestamp('2023-01-01T00:00:00Z')" ] } ``` ### `conn.tls.client.validity.not_before` Start date and time when the certificate becomes valid. ```yaml policy.yml theme={null} expressions: - "conn.tls.client.validity.not_before == timestamp('2020-01-01T00:00:00Z')" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.client.validity.not_before == timestamp('2020-01-01T00:00:00Z')" ] } ``` ## Connection TLS Server Variables The following variables are available under the `conn.tls.server` namespace: | Name | Type | Description | | - | - | - | | [`conn.tls.server.extensions`](#conntlsserverextensions) | `[]Extension` | Additional information added to the certificate. | | [`conn.tls.server.extensions[i].id`](#conntlsserverextensionsiid) | `string` | The identifier that specifies the type of extension. | | [`conn.tls.server.extensions[i].critical`](#conntlsserverextensionsicritical) | `bool` | True if the extension is critical. | | [`conn.tls.server.extensions[i].value`](#conntlsserverextensionsivalue) | `[]byte` | The data for the extension. | | [`conn.tls.server.issuer`](#conntlsserverissuer) | `string` | The issuing authority of the certificate as a string roughly following the RFC 2253 Distinguished Names syntax. | | [`conn.tls.server.issuer.common_name`](#conntlsserverissuercommon-name) | `string` | Common name of the issuing authority, usually the domain name. | | [`conn.tls.server.issuer.country`](#conntlsserverissuercountry) | `[]string` | Country names where the issuing authority is located. | | [`conn.tls.server.issuer.locality`](#conntlsserverissuerlocality) | `[]string` | Locality or city of the issuing authority. | | [`conn.tls.server.issuer.organization`](#conntlsserverissuerorganization) | `[]string` | Name of the organization that issued the certificate. | | [`conn.tls.server.issuer.organizational_unit`](#conntlsserverissuerorganizational-unit) | `[]string` | Division of the organization responsible for the certificate. | | [`conn.tls.server.issuer.postal_code`](#conntlsserverissuerpostal-code) | `[]string` | Postal code of the issuing authority. | | [`conn.tls.server.issuer.province`](#conntlsserverissuerprovince) | `[]string` | Province or state of the issuing authority. | | [`conn.tls.server.issuer.street_address`](#conntlsserverissuerstreet-address) | `[]string` | Street address of the issuing authority. | | [`conn.tls.server.san`](#conntlsserversan) | `string` | Subject alternative names of the ngrok server's leaf TLS certificate. | | [`conn.tls.server.san.dns_names`](#conntlsserversandns-names) | `[]string` | DNS names in the subject alternative names of the ngrok server's leaf TLS certificate. | | [`conn.tls.server.san.email_addresses`](#conntlsserversanemail-addresses) | `[]string` | Email addresses in the subject alternative names of the ngrok server's leaf TLS certificate. | | [`conn.tls.server.san.ip_addresses`](#conntlsserversanip-addresses) | `[]string` | IP addresses in the subject alternative names of the ngrok server's leaf TLS certificate. | | [`conn.tls.server.san.uris`](#conntlsserversanuris) | `[]string` | URIs in the subject alternative names of the ngrok server's leaf TLS certificate. | | [`conn.tls.server.serial_number`](#conntlsserverserial-number) | `string` | Unique identifier for the certificate. | | [`conn.tls.server.signature_algorithm`](#conntlsserversignature-algorithm) | `string` | Algorithm used to sign the certificate. | | [`conn.tls.server.subject`](#conntlsserversubject) | `string` | The entity to whom the certificate is issued as a string roughly following the RFC 2253 Distinguished Names syntax. | | [`conn.tls.server.subject.common_name`](#conntlsserversubjectcommon-name) | `string` | Common name of the subject, usually the domain name. | | [`conn.tls.server.subject.country`](#conntlsserversubjectcountry) | `[]string` | Country names where the subject of the certificate is located. | | [`conn.tls.server.subject.locality`](#conntlsserversubjectlocality) | `[]string` | Locality or city where the subject is located. | | [`conn.tls.server.subject.organization`](#conntlsserversubjectorganization) | `[]string` | Name of the organization to which the subject belongs. | | [`conn.tls.server.subject.organizational_unit`](#conntlsserversubjectorganizational-unit) | `[]string` | Division of the organization to which the subject belongs. | | [`conn.tls.server.subject.postal_code`](#conntlsserversubjectpostal-code) | `[]string` | Postal code where the subject is located. | | [`conn.tls.server.subject.province`](#conntlsserversubjectprovince) | `[]string` | Province or state where the subject is located. | | [`conn.tls.server.subject.street_address`](#conntlsserversubjectstreet-address) | `[]string` | Street address where the subject is located. | | [`conn.tls.server.validity.not_after`](#conntlsservervaliditynot-after) | `timestamp` | Expiration date and time when the certificate is no longer valid. | | [`conn.tls.server.validity.not_before`](#conntlsservervaliditynot-before) | `timestamp` | Start date and time when the certificate becomes valid. | ### `conn.tls.server.extensions` Additional information added to the certificate. ```yaml policy.yml theme={null} expressions: - "size(conn.tls.server.extensions) > 0" ``` ```json policy.json theme={null} { "expressions": [ "size(conn.tls.server.extensions) > 0" ] } ``` ### `conn.tls.server.extensions[i].id` The identifier that specifies the type of extension. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.extensions[0].id == '2.5.29.15'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.extensions[0].id == '2.5.29.15'" ] } ``` ### `conn.tls.server.extensions[i].critical` True if the extension is critical. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.extensions[0].critical" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.extensions[0].critical" ] } ``` ### `conn.tls.server.extensions[i].value` The data for the extension. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.extensions[0].value == b' '" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.extensions[0].value == b'\u0003\u0002\u0005 '" ] } ``` ### `conn.tls.server.issuer` The issuing authority of the certificate as a string roughly following the RFC 2253 Distinguished Names syntax. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.issuer == 'CN=E1,O=Let's Encrypt,C=US'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.issuer == 'CN=E1,O=Let's Encrypt,C=US'" ] } ``` ### `conn.tls.server.issuer.common_name` Common name of the issuing authority, usually the domain name. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.issuer.common_name == 'exampleca.com'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.issuer.common_name == 'exampleca.com'" ] } ``` ### `conn.tls.server.issuer.country` Country names where the issuing authority is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.issuer.country == ['US']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.issuer.country == ['US']" ] } ``` ### `conn.tls.server.issuer.locality` Locality or city of the issuing authority. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.issuer.locality == ['Mountain View']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.issuer.locality == ['Mountain View']" ] } ``` ### `conn.tls.server.issuer.organization` Name of the organization that issued the certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.issuer.organization == ['Example CA']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.issuer.organization == ['Example CA']" ] } ``` ### `conn.tls.server.issuer.organizational_unit` Division of the organization responsible for the certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.issuer.organizational_unit == ['Certification Authority Division']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.issuer.organizational_unit == ['Certification Authority Division']" ] } ``` ### `conn.tls.server.issuer.postal_code` Postal code of the issuing authority. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.issuer.postal_code == ['94043']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.issuer.postal_code == ['94043']" ] } ``` ### `conn.tls.server.issuer.province` Province or state of the issuing authority. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.issuer.province == ['California']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.issuer.province == ['California']" ] } ``` ### `conn.tls.server.issuer.street_address` Street address of the issuing authority. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.issuer.street_address == ['1234 Encryption Way']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.issuer.street_address == ['1234 Encryption Way']" ] } ``` ### `conn.tls.server.san` Subject alternative names of the server certificate of the ngrok server's leaf TLS certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.san == 'DNS:www.example.com, DNS:example.com, IP Address:192.168.1.1'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.san == 'DNS:www.example.com, DNS:example.com, IP Address:192.168.1.1'" ] } ``` ### `conn.tls.server.san.dns_names` DNS names in the subject alternative names of the ngrok server's leaf TLS certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.san.dns_names == ['ngrok-dns.com', 'ngrok-dns2.com']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.san.dns_names == ['ngrok-dns.com', 'ngrok-dns2.com']" ] } ``` ### `conn.tls.server.san.email_addresses` Email addresses in the subject alternative names of the ngrok server's leaf TLS certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.san.email_addresses == ['ngrok-email1@example.com', 'ngrok-email2@example.com']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.san.email_addresses == ['ngrok-email1@example.com', 'ngrok-email2@example.com']" ] } ``` ### `conn.tls.server.san.ip_addresses` IP addresses in the subject alternative names of the ngrok server's leaf TLS certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.san.ip_addresses == ['192.168.1.1']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.san.ip_addresses == ['192.168.1.1']" ] } ``` ### `conn.tls.server.san.uris` URIs in the subject alternative names of the ngrok server's leaf TLS certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.san.uris == ['https://example.com/example']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.san.uris == ['https://example.com/example']" ] } ``` ### `conn.tls.server.serial_number` Unique identifier for the certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.serial_number == 'b53017e79d4a5208b314a55d3574e0a8'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.serial_number == 'b53017e79d4a5208b314a55d3574e0a8'" ] } ``` ### `conn.tls.server.signature_algorithm` Algorithm used to sign the certificate. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.signature_algorithm == 'SHA256-RSA'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.signature_algorithm == 'SHA256-RSA'" ] } ``` ### `conn.tls.server.subject` The entity to whom the certificate is issued as a string roughly following the RFC 2253 Distinguished Names syntax. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.subject == 'CN=www.example.com'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.subject == 'CN=www.example.com'" ] } ``` ### `conn.tls.server.subject.common_name` Common name of the subject, usually the domain name. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.subject.common_name == 'ngrok-server.example.com'" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.subject.common_name == 'ngrok-server.example.com'" ] } ``` ### `conn.tls.server.subject.country` Country names where the subject of the certificate is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.subject.country == ['US']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.subject.country == ['US']" ] } ``` ### `conn.tls.server.subject.locality` Locality or city where the subject is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.subject.locality == ['Mountain View']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.subject.locality == ['Mountain View']" ] } ``` ### `conn.tls.server.subject.organization` Name of the organization to which the subject belongs. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.subject.organization == ['Example Corp']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.subject.organization == ['Example Corp']" ] } ``` ### `conn.tls.server.subject.organizational_unit` Division of the organization to which the subject belongs. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.subject.organizational_unit == ['Web Services']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.subject.organizational_unit == ['Web Services']" ] } ``` ### `conn.tls.server.subject.postal_code` Postal code where the subject is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.subject.postal_code == ['94043']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.subject.postal_code == ['94043']" ] } ``` ### `conn.tls.server.subject.province` Province or state where the subject is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.subject.province == ['California']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.subject.province == ['California']" ] } ``` ### `conn.tls.server.subject.street_address` Street address where the subject is located. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.subject.street_address == ['1234 Secure Blvd']" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.subject.street_address == ['1234 Secure Blvd']" ] } ``` ### `conn.tls.server.validity.not_after` Expiration date and time when the certificate is no longer valid. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.validity.not_after > timestamp('2023-01-01T00:00:00Z')" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.validity.not_after > timestamp('2023-01-01T00:00:00Z')" ] } ``` ### `conn.tls.server.validity.not_before` Start date and time when the certificate becomes valid. ```yaml policy.yml theme={null} expressions: - "conn.tls.server.validity.not_before < timestamp('2020-01-01T00:00:00Z')" ``` ```json policy.json theme={null} { "expressions": [ "conn.tls.server.validity.not_before < timestamp('2020-01-01T00:00:00Z')" ] } ``` ## Connection Kubernetes Pod Variables The following variables are available under the `conn.k8s.pod` namespace. They are populated on connections to endpoints with a `kubernetes` binding. They are not available on public or internal endpoints. If pod identity cannot be resolved, the metadata variables will not be set and `conn.k8s.pod.metadata.error_code` will be populated instead. See [`conn.k8s.pod.metadata.error_code`](#connk8spoderror-code) for details. | Name | Type | Description | | - | - | - | | [`conn.k8s.pod.id`](#connk8spodid) | `string` | The unique identifier (UID) of the originating pod. | | [`conn.k8s.pod.metadata.name`](#connk8spodname) | `string` | The name of the originating pod. | | [`conn.k8s.pod.metadata.namespace`](#connk8spodnamespace) | `string` | The namespace the originating pod belongs to. | | [`conn.k8s.pod.metadata.annotations`](#connk8spodannotations) | `map(string, string)` | A map of pod annotations prefixed with `k8s.ngrok.com/`. | | [`conn.k8s.pod.metadata.error_code`](#connk8spoderror-code) | `string` | An error code set when pod identity could not be resolved. | | [`conn.k8s.pod.metadata.error_message`](#connk8spoderror-message) | `string` | A human-readable error message providing additional detail when `conn.k8s.pod.metadata.error_code` is set. | ### `conn.k8s.pod.id` The unique identifier (UID) of the originating pod. Maximum size: 36 bytes. ```yaml policy.yml theme={null} expressions: - "conn.k8s.pod.id == '4b2c1a0e-7f3d-11ee-b962-0242ac120002'" ``` ```json policy.json theme={null} { "expressions": [ "conn.k8s.pod.id == '4b2c1a0e-7f3d-11ee-b962-0242ac120002'" ] } ``` ### `conn.k8s.pod.metadata.name` The name of the originating pod. Maximum size: 255 bytes. ```yaml policy.yml theme={null} expressions: - "conn.k8s.pod.metadata.name in ['worker-a', 'worker-b']" ``` ```json policy.json theme={null} { "expressions": [ "conn.k8s.pod.metadata.name in ['worker-a', 'worker-b']" ] } ``` ### `conn.k8s.pod.metadata.namespace` The namespace the originating pod belongs to. Maximum size: 63 bytes. ```yaml policy.yml theme={null} expressions: - "conn.k8s.pod.metadata.namespace != 'payments'" ``` ```json policy.json theme={null} { "expressions": [ "conn.k8s.pod.metadata.namespace != 'payments'" ] } ``` ### `conn.k8s.pod.metadata.annotations` A map of pod annotations prefixed with `k8s.ngrok.com/`. Only annotations with the `k8s.ngrok.com/` prefix are included. The combined size of all included annotations must not exceed 1024 bytes. If the limit is exceeded, `conn.k8s.pod.metadata.error_code` will be set to `ERR_NGROK_28000` and a truncated annotation map being returned. ```yaml policy.yml theme={null} expressions: - "conn.k8s.pod.metadata.annotations['k8s.ngrok.com/environment'] == 'production'" ``` ```json policy.json theme={null} { "expressions": [ "conn.k8s.pod.metadata.annotations['k8s.ngrok.com/environment'] == 'production'" ] } ``` ### `conn.k8s.pod.metadata.error_code` An error code set when pod identity could not be resolved. When this variable is set, the `conn.k8s.pod` metadata variables will not be populated. | Error code | Description | | - | - | | `ERR_NGROK_28000` | The combined size of one or more pod identity variables exceeded the allowed limit. | | `ERR_NGROK_28001` | Pod identity metadata could not be found for this connection. | It is recommended to check this variable at the start of any policy that relies on pod identity. See [Restricting Access by Kubernetes Pod Identity](/docs/gateway/k8s/guides/how-to/pod-identity) for guidance on handling missing identity. ```yaml policy.yml theme={null} expressions: - "conn.k8s.pod.metadata.error_code == ''" ``` ```json policy.json theme={null} { "expressions": [ "conn.k8s.pod.metadata.error_code == ''" ] } ``` ### `conn.k8s.pod.metadata.error_message` A human-readable error message providing additional detail when `conn.k8s.pod.metadata.error_code` is set. Intended for troubleshooting and diagnostic purposes. ```yaml policy.yml theme={null} expressions: - "conn.k8s.pod.metadata.error_message != ''" ``` ```json policy.json theme={null} { "expressions": [ "conn.k8s.pod.metadata.error_message != ''" ] } ``` # Endpoint Variables Source: https://ngrok.com/docs/gateway/traffic-policy/variables/endpoint Reference documentation for endpoint variables in Traffic Policy that provide metadata about the endpoint processing the request. ## Endpoint Variables The following variables are available under the `endpoint` namespace: | Name | Type | Description | | - | - | - | | [`endpoint.addr`](#endpointaddr) | `string` | The address for this endpoint. | | [`endpoint.host`](#endpointhost) | `string` | The hostname for this endpoint. | | [`endpoint.id`](#endpointid) | `string` | The endpoint that serviced this connection. | | [`endpoint.port`](#endpointport) | `int32` | The port for this endpoint. | | [`endpoint.protocol`](#endpointprotocol) | `string` | The protocol for this endpoint. Current supported values are `http`, `https`, `tcp`, and `tls`. | | [`endpoint.url`](#endpointurl) | `string` | The URL for this endpoint. | ### `endpoint.addr` The address for this endpoint. ```yaml policy.yml theme={null} expressions: - "endpoint.addr == 'my-subdomain.ngrok.app:443'" ``` ```json policy.json theme={null} { "expressions": [ "endpoint.addr == 'my-subdomain.ngrok.app:443'" ] } ``` ### `endpoint.host` The hostname for this endpoint. ```yaml policy.yml theme={null} expressions: - "endpoint.host == 'my-subdomain.ngrok.app'" ``` ```json policy.json theme={null} { "expressions": [ "endpoint.host == 'my-subdomain.ngrok.app'" ] } ``` ### `endpoint.id` The id for this endpoint. ```yaml policy.yml theme={null} expressions: - "endpoint.id == 'ep_2iL8LRbQilSCKYjaslRoqBwJcfT'" ``` ```json policy.json theme={null} { "expressions": [ "endpoint.id == 'ep_2iL8LRbQilSCKYjaslRoqBwJcfT'" ] } ``` ### `endpoint.port` The port for this endpoint. ```yaml policy.yml theme={null} expressions: - "endpoint.port == 443" ``` ```json policy.json theme={null} { "expressions": [ "endpoint.port == 443" ] } ``` ### `endpoint.protocol` The protocol for this endpoint. Current supported values are `http`, `https`, `tcp`, and `tls`. ```yaml policy.yml theme={null} expressions: - "endpoint.protocol == 'https'" ``` ```json policy.json theme={null} { "expressions": [ "endpoint.protocol == 'https'" ] } ``` ### `endpoint.url` The URL for this endpoint. ```yaml policy.yml theme={null} expressions: - "endpoint.url == 'https://my-subdomain.ngrok.app'" ``` ```json policy.json theme={null} { "expressions": [ "endpoint.url == 'https://my-subdomain.ngrok.app'" ] } ``` # Variables Source: https://ngrok.com/docs/gateway/traffic-policy/variables/index Reference documentation for Traffic Policy variables used in expressions and CEL interpolation to access traffic and connection metadata. ngrok provides a set of variables that you can use in Traffic Policy to: * **Conditionally handle traffic** through [Expressions](/docs/gateway/traffic-policy/how-it-works#expressions). * **Dynamically configure actions** using [CEL interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation). * **Debug traffic flows** via actions like [`log`](/docs/gateway/traffic-policy/actions/log) or [`custom-response`](/docs/gateway/traffic-policy/actions/custom-response). ### Available variables: * [Action Variables](/docs/gateway/traffic-policy/variables/action) * [Connection Variables](/docs/gateway/traffic-policy/variables/connection) * [GeoIP Variables](/docs/gateway/traffic-policy/variables/connection#connection-geo-variables) * [IP Intelligence Variables](/docs/gateway/traffic-policy/variables/ip-intel) * [IP Categories](/docs/gateway/traffic-policy/variables/ip-intel/#ip-categories) * [TLS Variables](/docs/gateway/traffic-policy/variables/connection#connection-tls-variables) * [Endpoint Variables](/docs/gateway/traffic-policy/variables/endpoint) * [Request Variables](/docs/gateway/traffic-policy/variables/req) * [Response Variables](/docs/gateway/traffic-policy/variables/res) * [Time Variables](/docs/gateway/traffic-policy/variables/time) # IP Intelligence Source: https://ngrok.com/docs/gateway/traffic-policy/variables/ip-intel Reference documentation for IP Intelligence variables in Traffic Policy for accessing IP reputation, categories, and threat data. IP Intelligence provides you with additional information about any IP address you see in ngrok. It includes Autonomous System information, Geolocation data, as well as information fetched from 3rd parties that list IP address for blocklists or allowlists. See the full list of [IP Categories available here](#ip-categories). These variables can be used in Traffic Policy expressions, or they can be used to enrich your traffic via dynamic injection into headers or responses. ## Client IP Intelligence The following variables are available under the `conn.client_ip` namespace: | Name | Type | Description | | - | - | - | | [`conn.client_ip.categories`](#connclient-ipcategories) | `list` | The list of categories that classify the `conn.client_ip`. Check out our [full list of categories](#ip-categories). | | [`conn.client_ip.is_on_blocklist`](#connclient-ipis_on_blocklist) | `boolean` | `true` when the `conn.client_ip` appears on one of the curated IP reputation blocklists ngrok trusts. Convenience shortcut for blocklist checks against `conn.client_ip.categories`. | | [`conn.client_ip.is_tor_node`](#connclient-ipis_tor_node) | `boolean` | `true` when the `conn.client_ip` is a known Tor exit node. Equivalent to checking for `proxy.anonymous.tor` in `conn.client_ip.categories`. | ### `conn.client_ip.categories` The list of categories that classify the `conn.client_ip`. Check out our [full list of categories](#ip-categories). ```yaml policy.yml theme={null} expressions: - "('proxy.anonymous.tor' in conn.client_ip.categories)" ``` ```json policy.json theme={null} { "expressions": [ "('proxy.anonymous.tor' in conn.client_ip.categories)" ] } ``` ### `conn.client_ip.is_on_blocklist` `true` when the `conn.client_ip` appears on one of the curated IP reputation blocklists ngrok trusts. These are blocklists with low false-positive rates, suitable for blocking traffic on the critical path. This is a convenience shortcut equivalent to checking `conn.client_ip.categories` for any blocklist category. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.is_on_blocklist == true" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.is_on_blocklist == true" ] } ``` ### `conn.client_ip.is_tor_node` `true` when the `conn.client_ip` is a known Tor exit node. This is a convenience shortcut equivalent to checking `'proxy.anonymous.tor' in conn.client_ip.categories`. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.is_tor_node == true" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.is_tor_node == true" ] } ``` ## Client IP Autonomous System The following variables are available under the `conn.client_ip` namespace: | Name | Type | Description | | - | - | - | | [`conn.client_ip.as.number`](#connclient-ipasnumber) | `integer` | The [Autonomous System Number](https://www.arin.net/resources/guide/asn/) of the `conn.client_ip`. | | [`conn.client_ip.as.organization`](#connclient-ipasorganization) | `string` | The Autonomous System Organization name of the `conn.client_ip`. | ### `conn.client_ip.as.number` The [Autonomous System Number](https://www.arin.net/resources/guide/asn/) of the `conn.client_ip`. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.as.number == 12345" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.as.number == 12345" ] } ``` ### `conn.client_ip.as.organization` The Autonomous System Organization name of the `conn.client_ip`. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.as.organization == 'COMCAST-7922'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.as.organization == 'COMCAST-7922'" ] } ``` ## Client IP Geo Location This is the location where the IP address is located, which may be different from where the IP address is registered. For more details, see the [MaxMind documentation](https://support.maxmind.com/hc/en-us/articles/4414762983195-Country-level-and-City-level-Geolocation). | Name | Type | Description | | - | - | - | | [`conn.client_ip.geo.location.city`](#connclient-ipgeolocationcity) | `string` | The name of the city, in EN, where the `conn.client_ip` is likely to originate. | | [`conn.client_ip.geo.location.continent`](#connclient-ipgeolocationcontinent) | `string` | The name of the continent, in EN, where the `conn.client_ip` is likely to originate. | | [`conn.client_ip.geo.location.country`](#connclient-ipgeolocationcountry) | `string` | The name of the country, in EN, where the `conn.client_ip` is likely to originate. | | [`conn.client_ip.geo.location.country_code`](#connclient-ipgeolocationcountry_code) | `string` | The two-letter ISO country code where the `conn.client_ip` is likely to originate. | | [`conn.client_ip.geo.location.is_eu`](#connclient-ipgeolocationis_eu) | `boolean` | Determines if the `conn.client_ip` is located in the EU. Helpful for GDPR compliance. | | [`conn.client_ip.geo.location.latitude`](#connclient-ipgeolocationlatitude) | `string` | The approximate latitude where the `conn.client_ip` is likely to originate. | | [`conn.client_ip.geo.location.longitude`](#connclient-ipgeolocationlongitude) | `string` | The approximate longitude where the `conn.client_ip` is likely to originate. | | [`conn.client_ip.geo.location.metro_code`](#connclient-ipgeolocationmetro_code) | `string` | The metro code where the `conn.client_ip` is likely to originate. | | [`conn.client_ip.geo.location.postal_code`](#connclient-ipgeolocationpostal_code) | `string` | The postal code where the `conn.client_ip` is likely to originate. | | [`conn.client_ip.geo.location.radius`](#connclient-ipgeolocationradius) | `string` | The radius in kilometers around the latitude and longitude where the `conn.client_ip` is likely to originate. | | [`conn.client_ip.geo.location.subdivision`](#connclient-ipgeolocationsubdivision) | `string` | The name of the subdivision, in EN, where the `conn.client_ip` is likely to originate. | | [`conn.client_ip.geo.location.timezone`](#connclient-ipgeolocationtimezone) | `string` | The name of the timezone where the `conn.client_ip` is likely to originate. | ### `conn.client_ip.geo.location.city` The name of the city, in EN, where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.location.city == 'Strongsville'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.location.city == 'Strongsville'" ] } ``` ### `conn.client_ip.geo.location.continent` The name of the continent, in EN, where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.location.continent == 'North America'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.location.continent == 'North America'" ] } ``` ### `conn.client_ip.geo.location.country` The name of the country, in EN, where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.location.country == 'United States'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.location.country == 'United States'" ] } ``` ### `conn.client_ip.geo.location.country_code` The two-letter ISO country code where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.location.country_code != 'US'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.location.country_code != 'US'" ] } ``` ### `conn.client_ip.geo.location.is_eu` Determines if the `conn.client_ip` is in the EU. Helpful for GDPR compliance. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.location.is_eu == true" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.location.is_eu == true" ] } ``` ### `conn.client_ip.geo.location.latitude` The approximate latitude where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "double(conn.client_ip.geo.location.latitude) >= 45.0" ``` ```json policy.json theme={null} { "expressions": [ "double(conn.client_ip.geo.location.latitude) >= 45.0" ] } ``` ### `conn.client_ip.geo.location.longitude` The approximate longitude where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "double(conn.client_ip.geo.location.longitude) <= -93.0" ``` ```json policy.json theme={null} { "expressions": [ "double(conn.client_ip.geo.location.longitude) <= -93.0" ] } ``` ### `conn.client_ip.geo.location.metro_code` The metro code where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.location.metro_code != ''" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.location.metro_code != ''" ] } ``` ### `conn.client_ip.geo.location.postal_code` The postal code where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.location.postal_code == '94324'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.location.postal_code == '94324'" ] } ``` ### `conn.client_ip.geo.location.radius` The radius in kilometers around the latitude and longitude where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.location.radius <= '5'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.location.radius <= '5'" ] } ``` ### `conn.client_ip.geo.location.subdivision` The name of the subdivision, in EN, where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.location.subdivision == 'California'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.location.subdivision == 'California'" ] } ``` ### `conn.client_ip.geo.location.timezone` The name of the timezone where the `conn.client_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.location.timezone == 'America/New_York'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.location.timezone == 'America/New_York'" ] } ``` ## Client IP Registered Geo Location This is the location where the IP address is registered, which may be different from where the IP address is located. For more details, see the [MaxMind documentation](https://support.maxmind.com/hc/en-us/articles/4414762983195-Country-level-and-City-level-Geolocation). | Name | Type | Description | | - | - | - | | [`conn.client_ip.geo.registered_location.city`](#connclient-ipgeoregistered-locationcity) | `string` | The name of the city, in EN, where the `conn.client_ip` is registered. | | [`conn.client_ip.geo.registered_location.continent`](#connclient-ipgeoregistered-locationcontinent) | `string` | The name of the continent, in EN, where the `conn.client_ip` is registered. | | [`conn.client_ip.geo.registered_location.country`](#connclient-ipgeoregistered-locationcountry) | `string` | The name of the country, in EN, where the `conn.client_ip` is registered. | | [`conn.client_ip.geo.registered_location.country_code`](#connclient-ipgeoregistered-locationcountry-code) | `string` | The two-letter ISO country code where the `conn.client_ip` is registered. | | [`conn.client_ip.geo.registered_location.is_eu`](#connclient-ipgeoregistered-locationis-eu) | `boolean` | Determines if the `conn.client_ip` is registered in the EU. Helpful for GDPR compliance. | | [`conn.client_ip.geo.registered_location.metro_code`](#connclient-ipgeoregistered-locationmetro-code) | `string` | The metro code where the `conn.client_ip` is registered. | | [`conn.client_ip.geo.registered_location.subdivision`](#connclient-ipgeoregistered-locationsubdivision) | `string` | The name of the subdivision, in EN, where the `conn.client_ip` is registered. | | [`conn.client_ip.geo.registered_location.timezone`](#connclient-ipgeoregistered-locationtimezone) | `string` | The name of the timezone where the `conn.client_ip` is registered. | ### `conn.client_ip.geo.registered_location.city` The name of the city, in EN, where the `conn.client_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.registered_location.city == 'Strongsville'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.registered_location.city == 'Strongsville'" ] } ``` ### `conn.client_ip.geo.registered_location.continent` The name of the continent, in EN, where the `conn.client_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.registered_location.continent == 'North America'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.registered_location.continent == 'North America'" ] } ``` ### `conn.client_ip.geo.registered_location.country` The name of the country, in EN, where the `conn.client_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.registered_location.country == 'United States'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.registered_location.country == 'United States'" ] } ``` ### `conn.client_ip.geo.registered_location.country_code` The two-letter ISO country code where the `conn.client_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.registered_location.country_code != 'US'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.registered_location.country_code != 'US'" ] } ``` ### `conn.client_ip.geo.registered_location.is_eu` Determines if the `conn.client_ip` is in the EU. Helpful for GDPR compliance. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.registered_location.is_eu == true" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.registered_location.is_eu == true" ] } ``` ### `conn.client_ip.geo.registered_location.subdivision` The name of the subdivision, in EN, where the `conn.client_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.registered_location.subdivision == 'California'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.registered_location.subdivision == 'California'" ] } ``` ### `conn.client_ip.geo.registered_location.metro_code` The metro code where the `conn.client_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.registered_location.metro_code != ''" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.registered_location.metro_code != ''" ] } ``` ### `conn.client_ip.geo.registered_location.timezone` The name of the timezone where the `conn.client_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.client_ip.geo.registered_location.timezone == 'America/New_York'" ``` ```json policy.json theme={null} { "expressions": [ "conn.client_ip.geo.registered_location.timezone == 'America/New_York'" ] } ``` ## Server IP Intelligence The following variables are available under the `conn.server_ip` namespace: | Name | Type | Description | | - | - | - | | [`conn.server_ip.categories`](#connserver-ipcategories) | `list` | The list of categories that classify the `conn.server_ip`. Check out our [full list of categories](#ip-categories). | | [`conn.server_ip.is_on_blocklist`](#connserver-ipis_on_blocklist) | `boolean` | `true` when the `conn.server_ip` appears on one of the curated IP reputation blocklists ngrok trusts. Convenience shortcut for blocklist checks against `conn.server_ip.categories`. | | [`conn.server_ip.is_tor_node`](#connserver-ipis_tor_node) | `boolean` | `true` when the `conn.server_ip` is a known Tor exit node. Equivalent to checking for `proxy.anonymous.tor` in `conn.server_ip.categories`. | ### `conn.server_ip.categories` The list of categories that classify the `conn.server_ip`. Check out our [full list of categories](#ip-categories). ```yaml policy.yml theme={null} expressions: - "('proxy.anonymous.tor' in conn.server_ip.categories)" ``` ```json policy.json theme={null} { "expressions": [ "('proxy.anonymous.tor' in conn.server_ip.categories)" ] } ``` ### `conn.server_ip.is_on_blocklist` `true` when the `conn.server_ip` appears on one of the curated IP reputation blocklists ngrok trusts. These are blocklists with low false-positive rates, suitable for blocking traffic on the critical path. This is a convenience shortcut equivalent to checking `conn.server_ip.categories` for any blocklist category. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.is_on_blocklist == true" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.is_on_blocklist == true" ] } ``` ### `conn.server_ip.is_tor_node` `true` when the `conn.server_ip` is a known Tor exit node. This is a convenience shortcut equivalent to checking `'proxy.anonymous.tor' in conn.server_ip.categories`. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.is_tor_node == true" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.is_tor_node == true" ] } ``` ## Server IP Autonomous System | Name | Type | Description | | - | - | - | | [`conn.server_ip.as.number`](#connserver-ipasnumber) | `integer` | The [Autonomous System Number](https://www.arin.net/resources/guide/asn/) of the `conn.server_ip`. | | [`conn.server_ip.as.organization`](#connserver-ipasorganization) | `string` | The Autonomous System Organization name of the `conn.server_ip`. | ### `conn.server_ip.as.number` The [Autonomous System Number](https://www.arin.net/resources/guide/asn/) of the `conn.server_ip`. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.as.number == 12345" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.as.number == 12345" ] } ``` ### `conn.server_ip.as.organization` The Autonomous System Organization name of the `conn.server_ip`. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.as.organization == 'COMCAST-7922'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.as.organization == 'COMCAST-7922'" ] } ``` ## Server IP Location Geo This is the location where the server IP address is located, which may be different from where the IP address is registered. For more details, see the [MaxMind documentation](https://support.maxmind.com/hc/en-us/articles/4414762983195-Country-level-and-City-level-Geolocation). | Name | Type | Description | | - | - | - | | [`conn.server_ip.geo.location.city`](#connserver-ipgeolocationcity) | `string` | The name of the city, in EN, where the `conn.server_ip` is likely to originate. | | [`conn.server_ip.geo.location.continent`](#connserver-ipgeolocationcontinent) | `string` | The name of the continent, in EN, where the `conn.server_ip` is likely to originate. | | [`conn.server_ip.geo.location.country`](#connserver-ipgeolocationcountry) | `string` | The name of the country, in EN, where the `conn.server_ip` is likely to originate. | | [`conn.server_ip.geo.location.country_code`](#connserver-ipgeolocationcountry_code) | `string` | The two-letter ISO country code where the `conn.server_ip` is likely to originate. | | [`conn.server_ip.geo.location.is_eu`](#connserver-ipgeolocationis_eu) | `boolean` | Determines if the `conn.server_ip` is located in the EU. Helpful for GDPR compliance. | | [`conn.server_ip.geo.location.latitude`](#connserver-ipgeolocationlatitude) | `string` | The approximate latitude where the `conn.server_ip` is likely to originate. | | [`conn.server_ip.geo.location.longitude`](#connserver-ipgeolocationlongitude) | `string` | The approximate longitude where the `conn.server_ip` is likely to originate. | | [`conn.server_ip.geo.location.metro_code`](#connserver-ipgeolocationmetro_code) | `string` | The metro code where the `conn.server_ip` is likely to originate. | | [`conn.server_ip.geo.location.postal_code`](#connserver-ipgeolocationpostal_code) | `string` | The postal code where the `conn.server_ip` is likely to originate. | | [`conn.server_ip.geo.location.radius`](#connserver-ipgeolocationradius) | `string` | The radius in kilometers around the latitude and longitude where the `conn.server_ip` is likely to originate. | | [`conn.server_ip.geo.location.subdivision`](#connserver-ipgeolocationsubdivision) | `string` | The name of the subdivision, in EN, where the `conn.server_ip` is likely to originate. | | [`conn.server_ip.geo.location.timezone`](#connserver-ipgeolocationtimezone) | `string` | The name of the timezone where the `conn.server_ip` is likely to originate. | ### `conn.server_ip.geo.location.city` The name of the city, in EN, where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.location.city == 'Strongsville'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.location.city == 'Strongsville'" ] } ``` ### `conn.server_ip.geo.location.continent` The name of the continent, in EN, where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.location.continent == 'North America'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.location.continent == 'North America'" ] } ``` ### `conn.server_ip.geo.location.country` The name of the country, in EN, where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.location.country == 'United States'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.location.country == 'United States'" ] } ``` ### `conn.server_ip.geo.location.country_code` The two-letter ISO country code where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.location.country_code != 'US'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.location.country_code != 'US'" ] } ``` ### `conn.server_ip.geo.location.is_eu` Determines if the `conn.server_ip` location is in the EU. Helpful for GDPR compliance. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.location.is_eu == true" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.location.is_eu == true" ] } ``` ### `conn.server_ip.geo.location.latitude` The approximate latitude where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "double(conn.server_ip.geo.location.latitude) >= 45.0" ``` ```json policy.json theme={null} { "expressions": [ "double(conn.server_ip.geo.location.latitude) >= 45.0" ] } ``` ### `conn.server_ip.geo.location.longitude` The approximate longitude where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "double(conn.server_ip.geo.location.longitude) <= -93.0" ``` ```json policy.json theme={null} { "expressions": [ "double(conn.server_ip.geo.location.longitude) <= -93.0" ] } ``` ### `conn.server_ip.geo.location.metro_code` The metro code where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.location.metro_code != ''" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.location.metro_code != ''" ] } ``` ### `conn.server_ip.geo.location.postal_code` The postal code where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.location.postal_code == '94324'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.location.postal_code == '94324'" ] } ``` ### `conn.server_ip.geo.location.radius` The radius in kilometers around the latitude and longitude where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.location.radius <= '5'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.location.radius <= '5'" ] } ``` ### `conn.server_ip.geo.location.subdivision` The name of the subdivision, in EN, where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.location.subdivision == 'California'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.location.subdivision == 'California'" ] } ``` ### `conn.server_ip.geo.location.timezone` The name of the timezone where the `conn.server_ip` is likely to originate. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.location.timezone == 'America/New_York'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.location.timezone == 'America/New_York'" ] } ``` ## Server IP Registered Location Geo This is the location where the server IP address is registered, which may be different from where the IP address is located. For more details, see the [MaxMind documentation](https://support.maxmind.com/hc/en-us/articles/4414762983195-Country-level-and-City-level-Geolocation). | Name | Type | Description | | - | - | - | | [`conn.server_ip.geo.registered_location.city`](#connserver-ipgeoregistered_locationcity) | `string` | The name of the city, in EN, where the `conn.server_ip` is registered. | | [`conn.server_ip.geo.registered_location.continent`](#connserver-ipgeoregistered_locationcontinent) | `string` | The name of the continent, in EN, where the `conn.server_ip` is registered. | | [`conn.server_ip.geo.registered_location.country`](#connserver-ipgeoregistered_locationcountry) | `string` | The name of the country, in EN, where the `conn.server_ip` is registered. | | [`conn.server_ip.geo.registered_location.country_code`](#connserver-ipgeoregistered_locationcountry_code) | `string` | The two-letter ISO country code where the `conn.server_ip` is registered. | | [`conn.server_ip.geo.registered_location.is_eu`](#connserver-ipgeoregistered_locationis_eu) | `boolean` | Determines if the `conn.server_ip` is registered in the EU. Helpful for GDPR compliance. | | [`conn.server_ip.geo.registered_location.metro_code`](#connserver-ipgeoregistered_locationmetro_code) | `string` | The metro code where the `conn.server_ip` is registered. | | [`conn.server_ip.geo.registered_location.subdivision`](#connserver-ipgeoregistered_locationsubdivision) | `string` | The name of the subdivision, in EN, where the `conn.server_ip` is registered. | | [`conn.server_ip.geo.registered_location.timezone`](#connserver-ipgeoregistered_locationtimezone) | `string` | The name of the timezone where the `conn.server_ip` is registered. | ### `conn.server_ip.geo.registered_location.city` The name of the city, in EN, where the `conn.server_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.registered_location.city == 'Strongsville'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.registered_location.city == 'Strongsville'" ] } ``` ### `conn.server_ip.geo.registered_location.continent` The name of the continent, in EN, where the `conn.server_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.registered_location.continent == 'North America'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.registered_location.continent == 'North America'" ] } ``` ### `conn.server_ip.geo.registered_location.country` The name of the country, in EN, where the `conn.server_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.registered_location.country == 'United States'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.registered_location.country == 'United States'" ] } ``` ### `conn.server_ip.geo.registered_location.country_code` The two-letter ISO country code where the `conn.server_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.registered_location.country_code != 'US'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.registered_location.country_code != 'US'" ] } ``` ### `conn.server_ip.geo.registered_location.is_eu` Determines if the `conn.server_ip` is in the EU. Helpful for GDPR compliance. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.registered_location.is_eu == true" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.registered_location.is_eu == true" ] } ``` ### `conn.server_ip.geo.registered_location.subdivision` The name of the subdivision, in EN, where the `conn.server_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.registered_location.subdivision == 'California'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.registered_location.subdivision == 'California'" ] } ``` ### `conn.server_ip.geo.registered_location.metro_code` The metro code where the `conn.server_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.registered_location.metro_code != ''" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.registered_location.metro_code != ''" ] } ``` ### `conn.server_ip.geo.registered_location.timezone` The name of the timezone where the `conn.server_ip` is registered. ```yaml policy.yml theme={null} expressions: - "conn.server_ip.geo.registered_location.timezone == 'America/New_York'" ``` ```json policy.json theme={null} { "expressions": [ "conn.server_ip.geo.registered_location.timezone == 'America/New_York'" ] } ``` ## IP Categories The following categories are available to use with the IP Intelligence `.categories` list. The categories are hierarchal and can be matched at any level needed. For example, an IP with the category of `com.cloudflare.ipv4` also has the categories `com` and `com.cloudflare`. This can be helpful if you want to allow all IPs from Cloudflare using `com.cloudflare` which would have both ipv4 and ipv6 lists. Here's an example of how to block Tor exit node IPs from accessing your endpoint. ```yaml policy.yml theme={null} expressions: - "('proxy.anonymous.tor' in conn.client_ip.categories)" ``` ```json policy.json theme={null} { "expressions": [ "('proxy.anonymous.tor' in conn.client_ip.categories)" ] } ``` Or you can block all anonymous proxies which would include Tor as well as any other lists with the `proxy.anonymous` category. ```yaml policy.yml theme={null} expressions: - "('proxy.anonymous' in conn.client_ip.categories)" ``` ```json policy.json theme={null} { "expressions": [ "('proxy.anonymous' in conn.client_ip.categories)" ] } ``` ### Blocklists | Name | Description | Sources | | - | - | - | | `blocklist.greensnow_co` | From [greensnow.co](https://greensnow.co/): GreenSnow is a team consisting of the best specialists in computer security, we harvest a large number of IPs from different computers located around the world. GreenSnow is comparable with Spamhaus for attacks of any kind except for spam. Our list is updated automatically and you can withdraw at any time your IP address if it has been listed. | [Source](https://blocklist.greensnow.co/greensnow.txt) | | `blocklist.stopforumspam` | From [stopforumspam.com](https://www.stopforumspam.com/downloads): The data provided here represents what we believe will only ever be used to abuse. IP Addresses listed here will be returned in API results as "blacklisted". You would be very unlikely to see valid content from the IP addresses listed. | [Source](https://www.stopforumspam.com/downloads/toxic_ip_cidr.txt) | | `blocklist.com.cinsscore.army` | From [cinsscore.com](https://cinsscore.com): The CINS Army list is a subset of the [CINS Active Threat Intelligence](https://cinsscore.com/#cins-ati) ruleset, and consists of IP addresses that meet one of two basic criteria: 1. The IP's recent Rogue Packet score factor is very poor, or 2. The IP has tripped a designated number of 'trusted' alerts across a given number of our Sentinels deployed around the world. | [Source](http://cinsscore.com/list/ci-badguys.txt) | | `blocklist.blocklist_de` | From [BlockList.de](https://www.blocklist.de/en/index.html): All IP addresses that have attacked one of our customers/servers in the last 48 hours. | [Source](https://lists.blocklist.de/lists/all.txt) | | `blocklist.blocklist_de.apache` | From [BlockList.de](https://www.blocklist.de/en/index.html): All IP addresses which have been reported within the last 48 hours as having run attacks on the service Apache, Apache-DDOS, RFI-Attacks. | [Source](https://lists.blocklist.de/lists/apache.txt) | | `blocklist.blocklist_de.bots` | From [BlockList.de](https://www.blocklist.de/en/index.html): All IP addresses which have been reported within the last 48 hours as having run attacks on the RFI-Attacks, REG-Bots, IRC-Bots or BadBots (BadBots = he has posted a Spam-Comment on a open Forum or Wiki). | [Source](https://lists.blocklist.de/lists/bots.txt) | | `blocklist.blocklist_de.bruteforcelogin` | From [BlockList.de](https://www.blocklist.de/en/index.html): All IPs which attacks Joomlas, Wordpress and other Web-Logins with Brute-Force Logins. | [Source](https://lists.blocklist.de/lists/bruteforcelogin.txt) | | `blocklist.blocklist_de.ftp` | From [BlockList.de](https://www.blocklist.de/en/index.html): All IP addresses which have been reported within the last 48 hours for attacks on the Service FTP. | [Source](https://lists.blocklist.de/lists/ftp.txt) | | `blocklist.blocklist_de.imap` | From [BlockList.de](https://www.blocklist.de/en/index.html): All IP addresses which have been reported within the last 48 hours for attacks on the Service imap, sasl, pop3. | [Source](https://lists.blocklist.de/lists/imap.txt) | | `blocklist.blocklist_de.mail` | From [BlockList.de](https://www.blocklist.de/en/index.html): All IP addresses which have been reported within the last 48 hours as having run attacks on the service Mail, Postfix. | [Source](https://lists.blocklist.de/lists/mail.txt) | | `blocklist.blocklist_de.sip` | From [BlockList.de](https://www.blocklist.de/en/index.html): All IP addresses that tried to login in a SIP-, VOIP- or Asterisk-Server and are included in the IPs-List from [http://www.infiltrated.net/](http://www.infiltrated.net/) (Twitter). | [Source](https://lists.blocklist.de/lists/sip.txt) | | `blocklist.blocklist_de.strongips` | From [BlockList.de](https://www.blocklist.de/en/index.html): All IPs which are older then 2 month and have more than 5,000 attacks. | [Source](https://lists.blocklist.de/lists/strongips.txt) | | `blocklist.blocklist_de.ssh` | From [BlockList.de](https://www.blocklist.de/en/index.html): All IP addresses which have been reported within the last 48 hours as having run attacks on the service SSH. | [Source](https://lists.blocklist.de/lists/ssh.txt) | | `blocklist.net.emergingthreats.compromised_ips` | From [emergingthreats.net](https://rules.emergingthreats.net/blockrules/): Emerging Threats Intelligence from Proofpoint provides information on IP addresses and domains that may be compromised. | [Source](https://rules.emergingthreats.net/blockrules/compromised-ips.txt) | | `blocklist.firehol_org.abusers_1d` | From [firehol.org](https://iplists.firehol.org/?ipset=firehol_abusers_1d): An ipset made from blocklists that track abusers in the last 24 hours. | [Source](https://iplists.firehol.org/files/firehol_abusers_1d.netset) | | `blocklist.firehol_org.abusers_30d` | From [firehol.org](https://iplists.firehol.org/?ipset=firehol_abusers_30d): An ipset made from blocklists that track abusers in the last 30 days. | [Source](https://iplists.firehol.org/files/firehol_abusers_30d.netset) | | `blocklist.firehol_org.level_1` | From [firehol.org](https://iplists.firehol.org/?ipset=firehol_level1): The objective is to create a blacklist that can be safe enough to be used on all systems, with a firewall, to block access entirely, from and to its listed IPs. The key prerequisite for this cause, is to have no false positives. All IPs listed should be bad and should be blocked, without exceptions. | [Source](https://iplists.firehol.org/files/firehol_level1.netset) | | `blocklist.firehol_org.level_2` | From [firehol.org](https://iplists.firehol.org/?ipset=firehol_level2): An ipset made from blocklists that track attacks, during the last 48 hours. | [Source](https://iplists.firehol.org/files/firehol_level2.netset) | | `blocklist.firehol_org.level_3` | From [firehol.org](https://iplists.firehol.org/?ipset=firehol_level3): An ipset made from blocklists that track attacks, spyware, viruses. It includes IPs than have been reported or detected in the last 30 days. | [Source](https://iplists.firehol.org/files/firehol_level3.netset) | | `blocklist.org.spamhaus.drop.ipv4` | From [spamhaus.org](https://www.spamhaus.org/blocklists/do-not-route-or-peer/): Don't Route Or Peer (DROP) lists the worst of the worst IP traffic. It is an advisory “drop all traffic”, containing IP ranges which are so dangerous to internet users that Spamhaus provides access to anyone who wants to add this layer of protection, free of charge. | [Source](https://www.spamhaus.org/drop/drop.txt) | | `blocklist.org.spamhaus.drop.ipv6` | From [spamhaus.org](https://www.spamhaus.org/blocklists/do-not-route-or-peer/): Don't Route Or Peer (DROP) lists the worst of the worst IP traffic. It is an advisory “drop all traffic”, containing IP ranges which are so dangerous to internet users that Spamhaus provides access to anyone who wants to add this layer of protection, free of charge. | [Source](https://www.spamhaus.org/drop/drop.txt) | | `blocklist.org.team_cymru.fullbogons.ipv4` | From [team-cymru.com](https://www.team-cymru.com/bogon-reference-http): The traditional bogon prefixes, plus prefixes that have been allocated to RIRs but not yet assigned by those RIRs to ISPs, end-users, etc. | [Source](https://team-cymru.org/Services/Bogons/fullbogons-ipv4.txt) | | `blocklist.org.team_cymru.fullbogons.ipv6` | From [team-cymru.com](https://www.team-cymru.com/bogon-reference-http): IPv6 "fullbogons", all IPv6 prefixes that have not been allocated to RIRs and that have not been assigned by RIRs to ISPs, end-users, etc. | [Source](https://team-cymru.org/Services/Bogons/fullbogons-ipv6.txt) | ### Proxies / VPNs | Name | Description | Sources | | - | - | - | | `proxy.anonymous.firehol_org` | From [firehol.org](https://iplists.firehol.org/?ipset=firehol_anonymous): An ipset that includes all the anonymizing IPs of the world. (includes: anonymous dm\_tor firehol\_proxies tor\_exits) | [Source](https://iplists.firehol.org/files/firehol_anonymous.netset) | | `proxy.anonymous.tor` | These IPs are actively serving as Tor Exit nodes. | [Source](https://www.dan.me.uk/torlist/?exit) | | `proxy.open.firehol_org` | From [firehol.org](https://iplists.firehol.org/?ipset=firehol_proxies): An ipset made from all sources that track open proxies. It includes IPs reported or detected in the last 30 days. | [Source](https://iplists.firehol.org/files/firehol_proxies.netset) | | `proxy.vpn.mullvad` | IPs for [Mullvad VPN](https://mullvad.net/en/servers) | [Source](https://api.mullvad.net/www/relays/all/) | | `proxy.vpn.nordvpn` | IPs for [NordVPN](https://nordvpn.com/) | [Source](https://api.nordvpn.com/v1/servers?limit=0) | | `proxy.vpn.pia` | IPs for [Private Internet Access VPN](https://www.privateinternetaccess.com/) | [Source](https://github.com/Lars-/PIA-servers) | ### Public lists | Name | Description | Sources | | - | - | - | | `public.brianhama.bad-asn-list` | From [Bad ASN List](https://github.com/brianhama/bad-asn-list): An open source list of ASNs known to belong to cloud, managed hosting, and colo facilities. | IPs looked up from [here](https://github.com/brianhama/bad-asn-list/blob/master/bad-asn-list.csv) | ## Organizations ### Ahrefs | Name | Description | Sources | | - | - | - | | `com.ahrefs` | IPs from Ahrefs | [Source](https://api.ahrefs.com/v3/public/crawler-ip-ranges) | ### Algolia | Name | Description | Sources | | - | - | - | | `com.algolia.crawer` | IPs from Algolia Crawler | [Source](https://api.ahrefs.com/v3/public/crawler-ip-ranges) | ### Amazon Web Services (AWS) | Name | Description | Sources | | - | - | - | | `com.aws.sns` | IPs from AWS SNS service | [Source](https://ip-ranges.amazonaws.com/ip-ranges.json) | | `com.aws.us-east-1.sns` | IPs from AWS SNS service for the `us-east-1` region | [Source](https://ip-ranges.amazonaws.com/ip-ranges.json) | | `com.aws.us-east-2.sns` | IPs from AWS SNS service for the `us-east-2` region | [Source](https://ip-ranges.amazonaws.com/ip-ranges.json) | | `com.aws.us-west-1.sns` | IPs from AWS SNS service for the `us-west-1` region | [Source](https://ip-ranges.amazonaws.com/ip-ranges.json) | | `com.aws.us-west-2.sns` | IPs from AWS SNS service for the `us-west-2` region | [Source](https://ip-ranges.amazonaws.com/ip-ranges.json) | | `com.aws` | IPs from AWS | [Source](https://ip-ranges.amazonaws.com/ip-ranges.json) | ### Anthropic | Name | Description | Sources | | - | - | - | | `com.anthropic.api` | IPs for [Anthropic](https://docs.anthropic.com/en/api/ip-addresses) | [Source](https://docs.anthropic.com/en/api/ip-addresses) | ### Apple | Name | Description | Sources | | - | - | - | | `com.apple.applebot.ipv4` | [Applebot, the web crawler for Apple.](https://support.apple.com/en-us/119829) | [Source](https://search.developer.apple.com/applebot.json) | | `com.apple.icloud-private-relay` | From [Apple](https://developer.apple.com/icloud/prepare-your-network-for-icloud-private-relay/): iCloud Private Relay is an internet privacy service offered as a part of an iCloud+ subscription that allows users on iOS 15, iPadOS 15, and macOS Monterey and later to connect to and browse the web more privately and securely. | [Source](https://mask-api.icloud.com/egress-ip-ranges.csv) | ### Atlassian | Name | Description | Sources | | - | - | - | | `com.atlassian.bitbucket` | IPs for [Atlassian Bitbucket](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#IP-address-ranges) | [Source](https://ip-ranges.atlassian.com/) | | `com.atlassian.confluence` | IPs for [Atlassian Confluence](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#IP-address-ranges) | [Source](https://ip-ranges.atlassian.com/) | | `com.atlassian.email` | IPs for [Atlassian Email](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#IP-address-ranges) | [Source](https://ip-ranges.atlassian.com/) | | `com.atlassian.forge` | IPs for [Atlassian Forge](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#IP-address-ranges) | [Source](https://ip-ranges.atlassian.com/) | | `com.atlassian.halp` | IPs for [Atlassian Halp](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#IP-address-ranges) | [Source](https://ip-ranges.atlassian.com/) | | `com.atlassian.jira` | IPs for [Atlassian Jira](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#IP-address-ranges) | [Source](https://ip-ranges.atlassian.com/) | | `com.atlassian.loom` | IPs for [Atlassian Loom](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#IP-address-ranges) | [Source](https://ip-ranges.atlassian.com/) | | `com.atlassian.opsgenie` | IPs for [Atlassian OpsGenie](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#IP-address-ranges) | [Source](https://ip-ranges.atlassian.com/) | | `com.atlassian.statuspage` | IPs for [Atlassian StatusPage](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#IP-address-ranges) | [Source](https://ip-ranges.atlassian.com/) | | `com.atlassian.trello` | IPs for [Atlassian Trello](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#IP-address-ranges) | [Source](https://ip-ranges.atlassian.com/) | ### Babbar | Name | Description | Sources | | - | - | - | | `tech.babbar.barkrowler` | IPs for [Babbar Barkrowler](https://www.babbar.tech/crawler) | [Source](https://www.babbar.tech/barkrowler-ip-ranges.json) | ### BetterStack | Name | Description | Sources | | - | - | - | | `com.betterstack.betteruptimebot` | IPs for [BetterUptimeBot](https://betterstack.com/docs/uptime/frequently-asked-questions/#what-ips-does-uptime-use) | [Source](https://uptime.betterstack.com/ips.txt) | ### Bing (Microsoft) | Name | Description | Sources | | - | - | - | | `com.bing.bingbot.ipv4` | IPs for [BingBot](https://www.bing.com/webmasters/help/how-to-verify-bingbot-3905dc26) | [Source](https://www.bing.com/toolbox/bingbot.json) | ### Brevo | Name | Description | Sources | | - | - | - | | `com.brevo.webhooks` | IPs for [Brevo Webhooks](https://developers.brevo.com/docs/how-to-use-webhooks#securing-your-webhooks) | [Source](https://developers.brevo.com/docs/how-to-use-webhooks#securing-your-webhooks) | ### Bunny CDN | Name | Description | Sources | | - | - | - | | `net.bunny.` | IPs for [Bunny CDN](https://bunny.net/) | [IPv4](https://api.bunny.net/system/edgeserverlist/plain) [IPv6](https://api.bunny.net/system/edgeserverlist/ipv6) | ### Censys | Name | Description | Sources | | - | - | - | | `io.censys.scanners` | The IPs that [Censys](https://censys.com/) bots use to continually scan the entire public IPv4 address space across all possible IP and port combinations using automatic protocol detection. | [Source](https://docs.censys.com/docs/opt-out-of-data-collection) | ### Cloudflare | Name | Description | Sources | | - | - | - | | `com.cloudflare.ipv4` | IPv4s from Cloudflare | [IPv4](https://www.cloudflare.com/ips-v4/#), [IPv6](https://www.cloudflare.com/ips-v6/#) | | `com.cloudflare.ipv6` | IPv6s from Cloudflare | [IPv4](https://www.cloudflare.com/ips-v4/#), [IPv6](https://www.cloudflare.com/ips-v6/#) | ### Common Crawl (CCBot) | Name | Description | Sources | | - | - | - | | `org.commoncrawl.ccbot` | The IPs for [CCBot](https://commoncrawl.org/faq) | [Source](https://commoncrawl.org/faq) | ### Criteo | Name | Description | Sources | | - | - | - | | `com.criteo.criteobot` | The IPs for [Criteo Criteobot](https://www.criteo.com/criteo-crawler/) | [Source](https://www.criteo.com/criteo-crawler/) | ### DigitalOcean | Name | Description | Sources | | - | - | - | | `com.digitalocean` | IPs from DigitalOcean | [Source](https://digitalocean.com/geo/google.csv) | ### Datadog | Name | Description | Sources | | - | - | - | | `com.datadog` | IPs for Datadog | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.agents` | IPs for Datadog Agents | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.api` | IPs for Datadog API | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.apm` | IPs for Datadog APM | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.global` | IPs for Datadog Global | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.logs` | IPs for Datadog Logs | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.orchestrator` | IPs for Datadog Orchestrators | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.process` | IPs for Datadog Processes | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.remote-configuration` | IPs for Datadog Remote Configurations | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.synthetics-private-locations` | IPs for Datadog Synthetics Private Locations | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.synthetics` | IPs for Datadog Synthetics | [Source](https://ip-ranges.datadoghq.com/) | | `com.datadog.webhooks` | IPs for Datadog Webhooks | [Source](https://ip-ranges.datadoghq.com/) | ### Delinea | Name | Description | Sources | | - | - | - | | `com.delinea.` | IPs for [Delinea](https://delinea.com) | [Source](https://docs.delinea.com/online-help/delinea-platform/getting-started/architecture.htm#DelineaPlatformHighLevelOverview) | ### DuckDuckGo | Name | Description | Sources | | - | - | - | | `com.duckduckgo.duckduckbot` | IPs for [DuckDuckBot](https://duckduckgo.com/duckduckgo-help-pages/results/duckduckbot) | [Source](https://duckduckgo.com/duckduckgo-help-pages/results/duckduckbot) | ### Fastly | Name | Description | Sources | | - | - | - | | `com.fastly.` | IPs for Fastly | [Source](https://api.fastly.com/public-ip-list) | ### Firehol | Name | Description | Sources | | - | - | - | | `blocklist.firehol_org.level_4` | From [firehol.org](https://iplists.firehol.org/?ipset=firehol_level4): An ipset made from blocklists that track attacks, but may include a large number of false positives. | [Source](https://iplists.firehol.org/files/firehol_level4.netset) | | `blocklist.firehol_org.webserver` | From [firehol.org](https://iplists.firehol.org/?ipset=firehol_webserver): A web server IP blacklist made from blocklists that track IPs that should never be used by your web users. This list includes IPs that are servers hosting malware, bots, etc or users having a long criminal history. | [Source](https://iplists.firehol.org/files/firehol_webserver.netset) | ### Github | Name | Description | Sources | | - | - | - | | `com.github.actions_macos` | IPs for Github Actions from MacOS | [Source](https://api.github.com/meta) | | `com.github.actions` | IPs for Github Actions | [Source](https://api.github.com/meta) | | `com.github.api` | IPs for the Github API | [Source](https://api.github.com/meta) | | `com.github.codespaces` | IPs for Github Code Spaces | [Source](https://api.github.com/meta) | | `com.github.copilot` | IPs for Github CoPilot | [Source](https://api.github.com/meta) | | `com.github.dependabot` | IPs for Github Dependabot | [Source](https://api.github.com/meta) | | `com.github.git` | IPs for Github Git | [Source](https://api.github.com/meta) | | `com.github.github_enterprise_importer` | IPs for Github Enterprise Importer | [Source](https://api.github.com/meta) | | `com.github.hooks` | IPs for Github Webhooks | [Source](https://api.github.com/meta) | | `com.github.importer` | IPs for Github Importer | [Source](https://api.github.com/meta) | | `com.github.packages` | IPs for Github Packages | [Source](https://api.github.com/meta) | | `com.github.pages` | IPs for Github Pages | [Source](https://api.github.com/meta) | | `com.github.web` | IPs for Github Web | [Source](https://api.github.com/meta) | ### GitLab | Name | Description | Sources | | - | - | - | | `com.gitlab.webhooks` | IPs for GitLab Webhooks | [Source](https://docs.gitlab.com/user/gitlab_com/#ip-range) | ### Google | Name | Description | Sources | | - | - | - | | `com.google.` | IPs for [Google](https://support.google.com/a/answer/10026322?hl=en) | [Source](https://www.gstatic.com/ipranges/goog.json) | | `com.google.googlebot.` | IPs for [GoogleBot](https://developers.google.com/search/docs/crawling-indexing/verifying-googlebot) | [Source](https://developers.google.com/static/search/apis/ipranges/googlebot.json) | ### IBM | Name | Description | Sources | | - | - | - | | `com.ibm.advmon_nimsoft` | IPs for [IBM AdvMon (Nimsoft)](https://cloud.ibm.com/docs/infrastructure-hub?topic=infrastructure-hub-ibm-cloud-ip-ranges). Based on the [IP Ranges Calculater Tool](https://calculator.maisonprosper.com/). | [Source](https://raw.githubusercontent.com/dprosper/cidr-calculator/refs/heads/main/data/datacenters.json) | | `com.ibm.file_block` | IPs for [IBM File & Block](https://cloud.ibm.com/docs/infrastructure-hub?topic=infrastructure-hub-ibm-cloud-ip-ranges). Based on the [IP Ranges Calculater Tool](https://calculator.maisonprosper.com/). | [Source](https://raw.githubusercontent.com/dprosper/cidr-calculator/refs/heads/main/data/datacenters.json) | | `com.ibm.icos` | IPs for [IBM ICOS](https://cloud.ibm.com/docs/infrastructure-hub?topic=infrastructure-hub-ibm-cloud-ip-ranges). Based on the [IP Ranges Calculater Tool](https://calculator.maisonprosper.com/). | [Source](https://raw.githubusercontent.com/dprosper/cidr-calculator/refs/heads/main/data/datacenters.json) | | `com.ibm.ims` | IPs for [IBM IMS](https://cloud.ibm.com/docs/infrastructure-hub?topic=infrastructure-hub-ibm-cloud-ip-ranges). Based on the [IP Ranges Calculater Tool](https://calculator.maisonprosper.com/). | [Source](https://raw.githubusercontent.com/dprosper/cidr-calculator/refs/heads/main/data/datacenters.json) | | `com.ibm.rhel` | IPs for [IBM RHEL](https://cloud.ibm.com/docs/infrastructure-hub?topic=infrastructure-hub-ibm-cloud-ip-ranges). Based on the [IP Ranges Calculater Tool](https://calculator.maisonprosper.com/). | [Source](https://raw.githubusercontent.com/dprosper/cidr-calculator/refs/heads/main/data/datacenters.json) | | `com.ibm.ssl_vpn` | IPs for [IBM SSL VPN](https://cloud.ibm.com/docs/infrastructure-hub?topic=infrastructure-hub-ibm-cloud-ip-ranges). Based on the [IP Ranges Calculater Tool](https://calculator.maisonprosper.com/). | [Source](https://raw.githubusercontent.com/dprosper/cidr-calculator/refs/heads/main/data/datacenters.json) | | `com.ibm.service_network` | IPs for [IBM Service Network](https://cloud.ibm.com/docs/infrastructure-hub?topic=infrastructure-hub-ibm-cloud-ip-ranges). Based on the [IP Ranges Calculater Tool](https://calculator.maisonprosper.com/). | [Source](https://raw.githubusercontent.com/dprosper/cidr-calculator/refs/heads/main/data/datacenters.json) | | `com.ibm.evault` | IPs for [IBM eVault](https://cloud.ibm.com/docs/infrastructure-hub?topic=infrastructure-hub-ibm-cloud-ip-ranges). Based on the [IP Ranges Calculater Tool](https://calculator.maisonprosper.com/). | [Source](https://raw.githubusercontent.com/dprosper/cidr-calculator/refs/heads/main/data/datacenters.json) | ### Linode (Akamai) | Name | Description | Sources | | - | - | - | | `com.linode` | IPs for Linode (now part of Akamai). | [Source](https://geoip.linode.com/) | ### Meta (Facebook) | Name | Description | Sources | | - | - | - | | `com..crawler` | IPs for [Meta Web Crawlers](https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#identify). | [Source](https://www.facebook.com/peering/geofeed) | ### Microsoft Azure | Name | Description | Sources | | - | - | - | | `com.microsoft.azure` | IPs for Microsoft Azure | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.actiongroup` | IPs for Microsoft Azure ActionGroup | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.applicationinsightsavailability` | IPs for Microsoft Azure Application Insights Availability | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.autonomousdevelopmentplatform` | IPs for Microsoft Azure Autonomous Development Platform | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuread` | IPs for Microsoft Entra ID | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureadvancedthreatprotection` | IPs for Microsoft Azure Advanced Threat Protection | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureapiforfhir` | IPs for Microsoft Azure API For FHIR | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureapimanagement` | IPs for Microsoft Azure Api Management | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureappconfiguration` | IPs for Microsoft Azure App Configuration | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureappservice` | IPs for Microsoft Azure App Service | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureappservicemanagement` | IPs for Microsoft Azure App Service Management | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurearcinfrastructure` | IPs for Microsoft Azure Arc Infrastructure | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureattestation` | IPs for Microsoft Azure Attestation | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureautomation` | IPs for Microsoft Azure Automation | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurebackup` | IPs for Microsoft Azure Backup | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurebotservice` | IPs for Microsoft Azure Bot Service | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurecognitivesearch` | IPs for Microsoft Azure Cognitive Search | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureconnectors` | IPs for Microsoft Azure Connectors | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurecontainerregistry` | IPs for Microsoft Azure Container Registry | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurecosmosdb` | IPs for Microsoft Azure CosmosDB | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuredatabricks` | IPs for Microsoft Azure Databricks | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuredataexplorermanagement` | IPs for Microsoft Azure Data Explorer Management | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuredatalake` | IPs for Microsoft Azure Data Lake | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuredeviceupdate` | IPs for Microsoft Azure Device Update | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuredevops` | IPs for Microsoft Azure DevOps | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuredevspaces` | IPs for Microsoft Azure DevSpaces | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuredigitaltwins` | IPs for Microsoft Azure Digital Twins | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureeventgrid` | IPs for Microsoft Azure Event Grid | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureeventhub` | IPs for Microsoft Azure Event Hub | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurefrontdoor` | IPs for Microsoft Azure Front Door | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureidentity` | IPs for Microsoft Azure Identity | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureinformationprotection` | IPs for Microsoft Azure Information Protection | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureiothub` | IPs for Microsoft Azure IoT Hub | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurekeyvault` | IPs for Microsoft Azure Key Vault | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureloadtestinginstancemanagement` | IPs for Microsoft Azure Load Testing Instance Management | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuremachinelearning` | IPs for Microsoft Azure Machine Learning | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuremachinelearninginference` | IPs for Microsoft Azure Machine Learning Inference | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuremanagedgrafana` | IPs for Microsoft Azure Managed Grafana | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuremonitor` | IPs for Microsoft Azure Monitor | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuremonitorforsap` | IPs for Microsoft Azure Monitor For SAP | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureopendatasets` | IPs for Microsoft Azure Open Datasets | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureportal` | IPs for Microsoft Azure Portal | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureresourcemanager` | IPs for Microsoft Azure Resource Manager | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuresecuritycenter` | IPs for Microsoft Azure Security Center | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuresentinel` | IPs for Microsoft Azure Sentinel | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureservicebus` | IPs for Microsoft Azure Service Bus | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuresignalr` | IPs for Microsoft Azure SignalR | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuresiterecovery` | IPs for Microsoft Azure Site Recovery | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurespheresecureservice_prod` | IPs for Microsoft Azure Sphere Secure Service Prod | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurespringcloud` | IPs for Microsoft Azure Spring Cloud | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuresql` | IPs for Microsoft Azure SQL | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurestack` | IPs for Microsoft Azure Stack | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurestorage` | IPs for Microsoft Azure Storage | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azuretrafficmanager` | IPs for Microsoft Azure Traffic Manager | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azureupdatedelivery` | IPs for Microsoft Azure Update Delivery | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurevideoanalyzerformedia` | IPs for Microsoft Azure Video Analyzer For Media | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.azurewebpubsub` | IPs for Microsoft Azure Web PubSub | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.batchnodemanagement` | IPs for Microsoft Azure Batch Node Management | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.chaosstudio` | IPs for Microsoft Azure Chaos Studio | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.cognitiveservicesfrontend` | IPs for Microsoft Azure Cognitive Services Frontend | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.cognitiveservicesmanagement` | IPs for Microsoft Azure Cognitive Services Management | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.datafactory` | IPs for Microsoft Azure Data Factory | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.dynamics365businesscentral` | IPs for Microsoft Azure Dynamics 365 Business Central | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.dynamics365formarketingemail` | IPs for Microsoft Azure Dynamics 365 For Marketing Email | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.eopextpublished` | IPs for Microsoft Azure EOP Ext Published | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.gatewaymanager` | IPs for Microsoft Azure Gateway Manager | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.grafana` | IPs for Microsoft Azure Grafana | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.hdinsight` | IPs for Microsoft Azure HDInsight | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.logicapps` | IPs for Microsoft Azure Logic Apps | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.m365managementactivityapi` | IPs for Microsoft Azure M365 Management Activity Api | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.m365managementactivityapiwebhook` | IPs for Microsoft Azure M365 Management Activity Api Webhook | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.marketplace` | IPs for Microsoft Azure Marketplace | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.microsoftazurefluidrelay` | IPs for Microsoft Azure Microsoft Azure Fluid Relay | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.microsoftcloudappsecurity` | IPs for Microsoft Azure Microsoft Cloud App Security | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.microsoftcontainerregistry` | IPs for Microsoft Azure Microsoft Container Registry | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.microsoftdefenderforendpoint` | IPs for Microsoft Azure Microsoft Defender For Endpoint | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.microsoftpurviewpolicydistribution` | IPs for Microsoft Azure Microsoft Purview Policy Distribution | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.onedscollector` | IPs for Microsoft Azure One Ds Collector | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.powerbi` | IPs for Microsoft Azure Power BI | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.powerplatforminfra` | IPs for Microsoft Azure Power Platform Infra | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.powerplatformplex` | IPs for Microsoft Azure Power Platform Plex | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.powerqueryonline` | IPs for Microsoft Azure Power Query Online | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.sccservice` | IPs for Microsoft Azure SCCservice | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.scuba` | IPs for Microsoft Azure Scuba | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.securitycopilot` | IPs for Microsoft Azure Security Copilot | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.serialconsole` | IPs for Microsoft Azure Serial Console | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.servicefabric` | IPs for Microsoft Azure Service Fabric | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.sqlmanagement` | IPs for Microsoft Azure Sql Management | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.storagemover` | IPs for Microsoft Azure Storage Mover | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.storagesyncservice` | IPs for Microsoft Azure Storage Sync Service | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.tridentkusto` | IPs for Microsoft Azure Trident Kusto | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.windowsadmincenter` | IPs for Microsoft Azure Windows Admin Center | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.windowsvirtualdesktop` | IPs for Microsoft Azure Windows Virtual Desktop | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | | `com.microsoft.azure.wvdrelays` | IPs for Microsoft Azure WVD Relays | [Source](https://download.microsoft.com/download/7/1/d/71d86715-5596-4529-9b13-da13a5de5b63/ServiceTags_Public_20250526.json) | ### Microsoft Office | Name | Description | Sources | | - | - | - | | `com.office.common.china` | IPs for [Microsoft Office](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges-21vianet?view=o365-worldwide) in China. | [Source](https://endpoints.office.com/endpoints/China?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.common.usgovdod` | IPs for [Microsoft Office](https://learn.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-u-s-government-dod-endpoints?view=o365-worldwide) for U.S. Government DoD plans only. | [Source](https://endpoints.office.com/endpoints/USGOVDoD?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.common.usgovgcchigh` | IPs for [Microsoft Office](https://learn.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-u-s-government-gcc-high-endpoints?view=o365-worldwide) for U.S. Government GCC High plans only. | [Source](https://endpoints.office.com/endpoints/USGOVGCCHigh?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.common.worldwide` | IPs for [Microsoft Office](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide) worldwide. | [Source](https://endpoints.office.com/endpoints/worldwide?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.exchange.china` | IPs for [Microsoft Exchange](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges-21vianet?view=o365-worldwide) in China. | [Source](https://endpoints.office.com/endpoints/China?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.exchange.usgovdod` | IPs for [Microsoft Exchange](https://learn.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-u-s-government-dod-endpoints?view=o365-worldwide) for U.S. Government DoD plans only. | [Source](https://endpoints.office.com/endpoints/USGOVDoD?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.exchange.usgovgcchigh` | IPs for [Microsoft Exchange](https://learn.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-u-s-government-gcc-high-endpoints?view=o365-worldwide) for U.S. Government GCC High plans only. | [Source](https://endpoints.office.com/endpoints/USGOVGCCHigh?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.exchange.worldwide` | IPs for [Microsoft Exchange](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide) worldwide. | [Source](https://endpoints.office.com/endpoints/worldwide?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.skype.china` | IPs for [Microsoft Skype / Microsoft Teams](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges-21vianet?view=o365-worldwide) in China. | [Source](https://endpoints.office.com/endpoints/China?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.skype.usgovdod` | IPs for [Microsoft Skype / Microsoft Teams](https://learn.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-u-s-government-dod-endpoints?view=o365-worldwide) for U.S. Government DoD plans only. | [Source](https://endpoints.office.com/endpoints/USGOVDoD?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.skype.usgovgcchigh` | IPs for [Microsoft Skype / Microsoft Teams](https://learn.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-u-s-government-gcc-high-endpoints?view=o365-worldwide) for U.S. Government GCC High plans only. | [Source](https://endpoints.office.com/endpoints/USGOVGCCHigh?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.skype.worldwide` | IPs for [Microsoft Skype / Microsoft Teams](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide) worldwide. | [Source](https://endpoints.office.com/endpoints/worldwide?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.sharepoint.china` | IPs for [Microsoft SharePoint](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges-21vianet?view=o365-worldwide) in China. | [Source](https://endpoints.office.com/endpoints/China?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.sharepoint.usgovdod` | IPs for [Microsoft SharePoint](https://learn.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-u-s-government-dod-endpoints?view=o365-worldwide) for U.S. Government DoD plans only. | [Source](https://endpoints.office.com/endpoints/USGOVDoD?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.sharepoint.usgovgcchigh` | IPs for [Microsoft SharePoint](https://learn.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-u-s-government-gcc-high-endpoints?view=o365-worldwide) for U.S. Government GCC High plans only. | [Source](https://endpoints.office.com/endpoints/USGOVGCCHigh?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | | `com.office.sharepoint.worldwide` | IPs for [Microsoft SharePoint](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide) worldwide. | [Source](https://endpoints.office.com/endpoints/worldwide?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) | ### Okta | Name | Description | Sources | | - | - | - | | `com.okta.apac` | IPs for Okta in the `apac` region. | [Source](https://s3.amazonaws.com/okta-ip-ranges/ip_ranges.json) | | `com.okta.ca` | IPs for Okta in the `ca` region. | [Source](https://s3.amazonaws.com/okta-ip-ranges/ip_ranges.json) | | `com.okta.emea` | IPs for Okta in the `emea` region. | [Source](https://s3.amazonaws.com/okta-ip-ranges/ip_ranges.json) | | `com.okta.emea_pam` | IPs for Okta in the `emea_pam` region. | [Source](https://s3.amazonaws.com/okta-ip-ranges/ip_ranges.json) | | `com.okta.in` | IPs for Okta in the `in` region. | [Source](https://s3.amazonaws.com/okta-ip-ranges/ip_ranges.json) | | `com.okta.preview` | IPs for Okta in the `preview` region. | [Source](https://s3.amazonaws.com/okta-ip-ranges/ip_ranges.json) | | `com.okta.preview_pam` | IPs for Okta in the `preview_pam` region. | [Source](https://s3.amazonaws.com/okta-ip-ranges/ip_ranges.json) | | `com.okta.us` | IPs for Okta in the `us` region. | [Source](https://s3.amazonaws.com/okta-ip-ranges/ip_ranges.json) | | `com.okta.us_pam` | IPs for Okta in the `us_pam` region. | [Source](https://s3.amazonaws.com/okta-ip-ranges/ip_ranges.json) | ### OpenAI (ChatGPT) | Name | Description | Sources | | - | - | - | | `com.openai.chatgpt.actions.ipv4` | IPs for OpenAI ChatGPT Actions. | [Source](https://openai.com/chatgpt-actions.json) | | `com.openai.chatgpt.user.ipv4` | IPs for [ChatGPT-User](https://platform.openai.com/docs/bots) which is for user actions in ChatGPT and Custom GPTs. | [Source](https://openai.com/chatgpt-user.json) | | `com.openai.gptbot.ipv4` | IPs for [GPTBot](https://platform.openai.com/docs/bots) which is used to make our generative AI foundation models more useful and safe. | [Source](https://openai.com/gptbot.json) | | `com.openai.searchbot.ipv4` | IPs for [OpenAI Searchbot](https://platform.openai.com/docs/bots). | [Source](https://openai.com/searchbot.json) | ### Oracle | Name | Description | Sources | | - | - | - | | `com.oracle.object_storage` | IPs for [Oracle Object Storage](https://docs.oracle.com/en-us/iaas/Content/General/Concepts/addressranges.htm). | [Source](https://docs.oracle.com/en-us/iaas/tools/public_ip_ranges.json) | | `com.oracle.oci` | IPs for [Oracle OCI](https://docs.oracle.com/en-us/iaas/Content/General/Concepts/addressranges.htm). | [Source](https://docs.oracle.com/en-us/iaas/tools/public_ip_ranges.json) | | `com.oracle.osn` | IPs for [Oracle OSN](https://docs.oracle.com/en-us/iaas/Content/General/Concepts/addressranges.htm). | [Source](https://docs.oracle.com/en-us/iaas/tools/public_ip_ranges.json) | ### Pagerduty | Name | Description | Sources | | - | - | - | | `com.pagerduty.webhooks.` | IPs for Pagerduty Webhooks split out by region (`us` or `eu`). | [Source](https://developer.pagerduty.com/docs/webhook-ips) | ### Perplexity AI | Name | Description | Sources | | - | - | - | | `ai.perplexity.bot.ipv4` | IPs for [Perplexity AI bot](https://docs.perplexity.ai/guides/bots). | [Source](https://www.perplexity.com/perplexitybot.json) | | `ai.perplexity.user.ipv4` | IPs for [Perplexity AI users](https://docs.perplexity.ai/guides/bots). | [Source](https://www.perplexity.com/perplexity-user.json) | ### Pingdom | Name | Description | Sources | | - | - | - | | `com.pingdom.` | IPs for [Pingdom](https://documentation.solarwinds.com/en/success_center/pingdom/content/topics/pingdom-probe-servers-ip-addresses.htm). | [IPv4](https://my.pingdom.com/probes/ipv4) [IPv6](https://my.pingdom.com/probes/ipv6) | ### Plaid | Name | Description | Sources | | - | - | - | | `com.plaid.webhooks` | IPs for Plaid Webhooks. | [Source](https://plaid.com/docs/api/webhooks/#configuring-webhooks) | ### RingCentral | Name | Description | Sources | | - | - | - | | `com.ringcentral` | IPs for [RingCentral](https://support.ringcentral.com/article-v2/Network-requirements.html?brand=RingCentral\&product=RingEX\&language=en_US). | [Source](https://support.ringcentral.com/article-v2/Network-requirements.html?brand=RingCentral\&product=RingEX\&language=en_US) | ### RSS API | Name | Description | Sources | | - | - | - | | `net.rssapi` | IPs for [RSS API](https://rssapi.net/). | [Source](https://rssapi.net/ips.txt) | ### Salesforce | Name | Description | Sources | | - | - | - | | `com.salesforce.core` | IPs for Salesforce. | [Source](https://help.salesforce.com/s/articleView?id=000384438\&type=1) | | `com.salesforce.gov` | IPs for Salesforce Government Cloud. | [Source](https://help.salesforce.com/s/articleView?id=000386297\&type=1\&language=en_US) | | `com.salesforce.hyperforce` | IPs for Salesforce Hyperforce. | [Source](https://ip-ranges.salesforce.com/ip-ranges.json) | | `com.salesforce.news` | IPs for Salesforce News, Account Logos, and Automated Account Fields. | [Source](https://help.salesforce.com/s/articleView?id=xcloud.allowlist_account_features.htm\&type=5) | ### Semrush | Name | Description | Sources | | - | - | - | | `com.semrush.semrushbot.siteaudit` | IPs for [Semrush SiteAudit](https://www.semrush.com/kb/681-site-audit-troubleshooting#whitelist-bot) | [Source](https://www.semrush.com/kb/681-site-audit-troubleshooting#whitelist-bot) | | `com.semrush.splitsignalbot` | IPs for [Semrush SplitSignalBot](https://www.semrush.com/kb/1149-issues-with-crawling-a-domain) | [Source](https://www.semrush.com/kb/1149-issues-with-crawling-a-domain) | ### Stripe | Name | Description | Sources | | - | - | - | | `com.stripe.api` | The full list of IP addresses that api.stripe.com may resolve to | [Source](https://stripe.com/files/ips/ips_api.txt) | | `com.stripe.armada_gator` | The full list of IP addresses that files.stripe.com, armada.stripe.com, and gator.stripe.com may resolve to | [Source](https://stripe.com/files/ips/ips_armada_gator.txt) | | `com.stripe.webhooks` | The full list of IP addresses that Stripe webhook notifications may come from | [Source](https://stripe.com/files/ips/ips_webhooks.txt) | ### Svix | Name | Description | Sources | | - | - | - | | `com.svix.webhooks` | IPs for [Svix webhooks](https://docs.svix.com/receiving/source-ips). | [Source](https://docs.svix.com/webhook-ips.json) | ### Telegram | Name | Description | Sources | | - | - | - | | `org.telegram` | IPs for Telegram Bots | [Source](https://core.telegram.org/resources/cidr.txt) | ### Teleport | Name | Description | Sources | | - | - | - | | `com.teleport` | Public address allow list for [Teleport](https://goteleport.com/) | [Source](https://goteleport.com/docs/ips/) | ### Telnyx | Name | Description | Sources | | - | - | - | | `com.telnyx.media` | IPs for [Telnyx Media](https://sip.telnyx.com/) | [Source](https://sip.telnyx.com/) | | `com.telnyx.webhooks` | IPs for [Telnyx Webhooks](https://support.telnyx.com/en/articles/1130687-whitelisting-telnyx-ip-addresses) | [Source](https://support.telnyx.com/en/articles/1130687-whitelisting-telnyx-ip-addresses) | ### UptimeRobot | Name | Description | Sources | | - | - | - | | `com.uptimerobot.` | IPs for [UptimeRobot](https://uptimerobot.com/help/locations/) | [Source](https://uptimerobot.com/inc/files/ips/IPv4andIPv6.txt) | ### Vultr | Name | Description | Sources | | - | - | - | | `com.vultr` | IPs for [Vultr](https://www.vultr.com/) | [Source](https://geofeed.constant.com/) | ### WebPageTest | Name | Description | Sources | | - | - | - | | `org.webpagetest` | IPs for [WebPageTest](https://www.webpagetest.org/) | [Source](https://www.webpagetest.org/addresses.php?f=json) | ### X (Twitter) | Name | Description | Sources | | - | - | - | | `com.x.twitterbot` | IPs for [X.com Twitterbot](https://developer.x.com/en/docs/x-for-websites/cards/guides/troubleshooting-cards#validate_twitterbot) | [Source](https://developer.x.com/en/docs/x-for-websites/cards/guides/troubleshooting-cards#validate_twitterbot) | ### Yandex | Name | Description | Sources | | - | - | - | | `com.yandex` | IPs for [Yandex](https://yandex.com/ips) | [Source](https://yandex.com/ips) | ### Zendesk | Name | Description | Sources | | - | - | - | | `com.zendesk` | IPs for [Zendesk](https://developer.zendesk.com/api-reference/ticketing/account-configuration/public_ips/#list-zendesk-public-ips) | [Source](https://subdomain.zendesk.com/ips) | ### Zoom | Name | Description | Sources | | - | - | - | | `us.zoom` | IPs for [Zoom](https://support.zoom.com/hc/en/article?id=zm_kb\&sysparm_article=KB0060548#h_01EJHWF2FSMCD2HFEPMQJMKAM4) | [Source](https://assets.zoom.us/docs/ipranges/Zoom.txt) | | `us.zoom.meetings.` | IPs for [Zoom Meetings](https://support.zoom.com/hc/en/article?id=zm_kb\&sysparm_article=KB0060548#h_01EJHWF2FSMCD2HFEPMQJMKAM4) | [IPv4](https://assets.zoom.us/docs/ipranges/ZoomMeetings.txt) [IPv6](https://assets.zoom.us/docs/ipranges/ZoomMeetings-IPv6.txt) | | `us.zoom.crc` | IPs for [Zoom Cloud Room Connector](https://support.zoom.com/hc/en/article?id=zm_kb\&sysparm_article=KB0060548#h_01EJHWF2FSMCD2HFEPMQJMKAM4) | [Source](https://assets.zoom.us/docs/ipranges/ZoomCRC.txt) | | `us.zoom.phone.` | IPs for [Zoom Phone](https://support.zoom.com/hc/en/article?id=zm_kb\&sysparm_article=KB0060548#h_01EJHWF2FSMCD2HFEPMQJMKAM4) | [IPv4](https://assets.zoom.us/docs/ipranges/ZoomPhone.txt) [IPv6](https://assets.zoom.us/docs/ipranges/ZoomPhone-IPv6.txt) | | `us.zoom.cc.` | IPs for [Zoom Contact Center](https://support.zoom.com/hc/en/article?id=zm_kb\&sysparm_article=KB0060548#h_01EJHWF2FSMCD2HFEPMQJMKAM4) | [IPv4](https://assets.zoom.us/docs/ipranges/ZoomCC.txt) [IPv6](https://assets.zoom.us/docs/ipranges/ZoomCC-IPv6.txt) | | `us.zoom.zva` | IPs for [Zoom Virtual Agent](https://support.zoom.com/hc/en/article?id=zm_kb\&sysparm_article=KB0060548#h_01EJHWF2FSMCD2HFEPMQJMKAM4) | [Source](https://assets.zoom.us/docs/ipranges/ZoomZVA.txt) | | `us.zoom.cdn.` | IPs for [Zoom CDN](https://support.zoom.com/hc/en/article?id=zm_kb\&sysparm_article=KB0060548#h_01EJHWF2FSMCD2HFEPMQJMKAM4) | [IPv4](https://assets.zoom.us/docs/ipranges/ZoomCDN.txt) [IPv6](https://assets.zoom.us/docs/ipranges/ZoomCDN-IPv6.txt) | | `us.zoom.apps.` | IPs for [Zoom Apps](https://support.zoom.com/hc/en/article?id=zm_kb\&sysparm_article=KB0060548#h_01EJHWF2FSMCD2HFEPMQJMKAM4) | [IPv4](https://assets.zoom.us/docs/ipranges/ZoomApps.txt) [IPv6](https://assets.zoom.us/docs/ipranges/ZoomApps-IPv6.txt) | ### Zscaler | Name | Description | Sources | | - | - | - | | `com.zscaler.private.zpa` | [Zscaler Private Access Firewall Allowlist for private.zscaler.com](https://config.zscaler.com/private.zscaler.com/zpa) | [Source](https://config.zscaler.com/api/private.zscaler.com/zpa/json) | | `net.zdxbeta.future` | [Zscaler Egress IP ranges and Future Data Centers for zdxbeta.net](https://config.zscaler.com/zdxbeta.net/cloud-telemetry-gateway-ranges) | [Source](https://config.zscaler.com/api/zdxbeta.net/cenr/json) | | `net.zdxbeta.hubs.` | [Zscaler Hub IP Addresses for zdxbeta.net](https://config.zscaler.com/zdxbeta.net/hubs) | [Required](https://config.zscaler.com/api/zdxbeta.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zdxbeta.net/hubs/cidr/json/recommended) | | `net.zdxcloud.future` | [Zscaler Egress IP ranges and Future Data Centers for zdxcloud.net](https://config.zscaler.com/zdxcloud.net/cloud-telemetry-gateway-ranges) | [Source](https://config.zscaler.com/api/zdxcloud.net/cenr/json) | | `net.zdxcloud.hubs.` | [Zscaler Hub IP Addresses for zdxcloud.net](https://config.zscaler.com/zdxcloud.net/hubs) | [Required](https://config.zscaler.com/api/zdxcloud.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zdxcloud.net/hubs/cidr/json/recommended) | | `net.zdxgov.hubs.` | [Zscaler Hub IP Addresses for zdxgov.net](https://config.zscaler.com/zdxgov.net/hubs) | [Required](https://config.zscaler.com/api/zdxgov.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zdxgov.net/hubs/cidr/json/recommended) | | `net.zdxten.hubs.` | [Zscaler Hub IP Addresses for zdxten.net](https://config.zscaler.com/zdxten.net/hubs) | [Required](https://config.zscaler.com/api/zdxten.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zdxten.net/hubs/cidr/json/recommended) | | `net.zdxten.zpa` | [Zscaler Private Access Firewall Allowlist for zdxten.net](https://config.zscaler.com/zdxten.net/) | [Source](https://config.zscaler.com/api/zdxten.net/zpa/json) | | `net.zpabeta.zpa` | [Zscaler Private Access Firewall Allowlist for zpabeta.net](https://config.zscaler.com/zpabeta.net/) | [Source](https://config.zscaler.com/api/zpabeta.net/zpa/json) | | `net.zpagov.zpa` | [Zscaler Private Access Firewall Allowlist for zpagov.net](https://config.zscaler.com/zpagov.net/) | [Source](https://config.zscaler.com/api/zpagov.net/zpa/json) | | `net.zpatwo.zpa` | [Zscaler Private Access Firewall Allowlist for zpatwo.net](https://config.zscaler.com/zpatwo.net/) | [Source](https://config.zscaler.com/api/zpatwo.net/zpa/json) | | `net.zscaler.cenr.` | [Current Data Centers for zscaler.net](https://config.zscaler.com/zscaler.net/) | [Source](https://config.zscaler.com/api/zscaler.net/cenr/json) | | `net.zscaler.future` | [Zscaler Aggregate IP Address Ranges for zscaler.net](https://config.zscaler.com/zscaler.net/) | [Source](https://config.zscaler.com/api/zscaler.net/future/json) | | `net.zscaler.hubs.` | [Zscaler Hub IP Addresses for zscaler.net](https://config.zscaler.com/zscaler.net/hubs) | [Required](https://config.zscaler.com/api/zscaler.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zscaler.net/hubs/cidr/json/recommended) | | `net.zscaler.zpa` | [Zscaler Private Access Firewall Allowlist for zscaler.net](https://config.zscaler.com/zscaler.net/) | [Source](https://config.zscaler.com/api/zscaler.net/zpa/json) | | `net.zscalerbeta.cenr.` | [Current Data Centers for zscalerbeta.net](https://config.zscaler.com/api/zscalerbeta.net/cenr/json) | [Source](https://config.zscaler.com/api/zscalerbeta.net/cenr/json) | | `net.zscalerbeta.future` | [Zscaler Aggregate IP Address Ranges for zscalerbeta.net](https://config.zscaler.com/zscalerbeta.net/) | [Source](https://config.zscaler.com/api/zscalerbeta.net/future/json) | | `net.zscalerbeta.hubs.` | [Zscaler Hub IP Addresses for zscalerbeta.net](https://config.zscaler.com/zscalerbeta.net/hubs) | [Required](https://config.zscaler.com/api/zscalerbeta.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zscalerbeta.net/hubs/cidr/json/recommended) | | `net.zscalerbeta.zpa` | [Zscaler Private Access Firewall Allowlist for zscalerbeta.net](https://config.zscaler.com/zscalerbeta.net/) | [Source](https://config.zscaler.com/api/zscalerbeta.net/zpa/json) | | `net.zscalergov.cenr.` | [Current Data Centers for zscalergov.net](https://config.zscaler.com/zscalergov.net/) | [Source](https://config.zscaler.com/api/zscalergov.net/cenr/json) | | `net.zscalergov.future` | [Zscaler Aggregate IP Address Ranges for zscalergov.net](https://config.zscaler.com/zscalergov.net/) | [Source](https://config.zscaler.com/api/zscalergov.net/future/json) | | `net.zscalergov.hubs.` | [Zscaler Hub IP Addresses for zscalergov.net](https://config.zscaler.com/zscalergov.net/hubs) | [Required](https://config.zscaler.com/api/zscalergov.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zscalergov.net/hubs/cidr/json/recommended) | | `net.zscalergov.zpa` | [Zscaler Private Access Firewall Allowlist for zscalergov.net](https://config.zscaler.com/zscalergov.net/) | [Source](https://config.zscaler.com/api/zscalergov.net/zpa/json) | | `net.zscalerone.cenr.` | [Current Data Centers for zscalerone.net](https://config.zscaler.com/api/zscalerone.net/cenr/json) | [Source](https://config.zscaler.com/api/zscalerone.net/cenr/json) | | `net.zscalerone.future` | [Zscaler Aggregate IP Address Ranges for zscalerone.net](https://config.zscaler.com/zscalerone.net/) | [Source](https://config.zscaler.com/api/zscalerone.net/future/json) | | `net.zscalerone.hubs.` | [Zscaler Hub IP Addresses for zscalerone.net](https://config.zscaler.com/zscalerone.net/hubs) | [Required](https://config.zscaler.com/api/zscalerone.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zscalerone.net/hubs/cidr/json/recommended) | | `net.zscalerone.zpa` | [Zscaler Private Access Firewall Allowlist for zscalerone.net](https://config.zscaler.com/zscalerone.net/) | [Source](https://config.zscaler.com/api/zscalerone.net/zpa/json) | | `net.zscalerten.cenr.` | [Current Data Centers for zscalerten.net](https://config.zscaler.com/zscalerten.net/) | [Source](https://config.zscaler.com/api/zscalerten.net/cenr/json) | | `net.zscalerten.hubs.` | [Zscaler Hub IP Addresses for zscalerten.net](https://config.zscaler.com/zscalerten.net/hubs) | [Required](https://config.zscaler.com/api/zscalerten.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zscalerten.net/hubs/cidr/json/recommended) | | `net.zscalerten.zpa` | [Zscaler Private Access Firewall Allowlist for zscalerten.net](https://config.zscaler.com/zscalerten.net/) | [Source](https://config.zscaler.com/api/zscalerten.net/zpa/json) | | `net.zscalerthree.cenr.` | [Current Data Centers for zscalerthree.net](https://config.zscaler.com/zscalerthree.net/) | [Source](https://config.zscaler.com/api/zscalerthree.net/cenr/json) | | `net.zscalerthree.future` | [Zscaler Aggregate IP Address Ranges for zscalerthree.net](https://config.zscaler.com/zscalerthree.net/) | [Source](https://config.zscaler.com/api/zscalerthree.net/future/json) | | `net.zscalerthree.hubs.` | [Zscaler Hub IP Addresses for zscalerthree.net](https://config.zscaler.com/zscalerthree.net/hubs) | [Required](https://config.zscaler.com/api/zscalerthree.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zscalerthree.net/hubs/cidr/json/recommended) | | `net.zscalerthree.zpa` | [Zscaler Private Access Firewall Allowlist for zscalerthree.net](https://config.zscaler.com/zscalerthree.net/) | [Source](https://config.zscaler.com/api/zscalerthree.net/zpa/json) | | `net.zscalertwo.cenr.` | [Current Data Centers for zscalertwo.net](https://config.zscaler.com/zscalertwo.net/) | [Source](https://config.zscaler.com/api/zscalertwo.net/cenr/json) | | `net.zscalertwo.future` | [Zscaler Aggregate IP Address Ranges for zscalertwo.net](https://config.zscaler.com/zscalertwo.net/) | [Source](https://config.zscaler.com/api/zscalertwo.net/future/json) | | `net.zscalertwo.hubs.` | [Zscaler Hub IP Addresses for zscalertwo.net](https://config.zscaler.com/zscalertwo.net/hubs) | [Required](https://config.zscaler.com/api/zscalertwo.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zscalertwo.net/hubs/cidr/json/recommended) | | `net.zscalertwo.zpa` | [Zscaler Private Access Firewall Allowlist for zscalertwo.net](https://config.zscaler.com/zscalertwo.net/) | [Source](https://config.zscaler.com/api/zscalertwo.net/zpa/json) | | `net.zscloud.cenr.` | [Current Data Centers for zscloud.net](https://config.zscaler.com/zscloud.net/) | [Source](https://config.zscaler.com/api/zscloud.net/cenr/json) | | `net.zscloud.future` | [Zscaler Aggregate IP Address Ranges for zscloud.net](https://config.zscaler.com/zscloud.net/) | [Source](https://config.zscaler.com/api/zscloud.net/future/json) | | `net.zscloud.hubs.` | [Zscaler Hub IP Addresses for zscloud.net](https://config.zscaler.com/zscloud.net/hubs) | [Required](https://config.zscaler.com/api/zscloud.net/hubs/cidr/json/required), [Recommended](https://config.zscaler.com/api/zscloud.net/hubs/cidr/json/recommended) | | `net.zscloud.zpa` | [Zscaler Private Access Firewall Allowlist for zpatwo.net](https://config.zscaler.com/zpatwo.net/) | [Source](https://config.zscaler.com/api/zpatwo.net/zpa/json) | | `us.zpagov.zpa` | [Zscaler Private Access Firewall Allowlist for zpagov.us](https://config.zscaler.com/zpagov.us/) | [Source](https://config.zscaler.com/api/zpagov.us/zpa/json) | # HTTP Request Variables Source: https://ngrok.com/docs/gateway/traffic-policy/variables/req Reference documentation for HTTP request variables in Traffic Policy including headers, method, URL, body, and user agent information. ## Request Variables The following variables are available under the `req` namespace: | Name | Type | Description | | - | - | - | | [`req.content_encoding`](#reqcontent-encoding) | `list[string]` | The encodings set in the `Content-Encoding` header for this request. | | [`req.content_length`](#reqcontent-length) | `int64` | The content length of the body in bytes. This may not be present if the request does not contain a body or if the client does not specify a content length because they are streaming the body. | | [`req.content_type`](#reqcontent-type) | `string` | The media type set in the `Content-Type` header for this request. | | [`req.content_type.parameters`](#reqcontent-typeparameters) | `map[string]string` | The parameters set in the `Content-Type` header for this request. | | [`req.content_type.raw`](#reqcontent-typeraw) | `string` | The value of the `Content-Type` header for this request. | | [`req.cookies`](#reqcookies) | `map[string][]cookie` | The HTTP cookie objects included in this request. If there are multiple cookies with the same name, they will be ordered as specified in the `Cookie` header. | | [`req.cookies[k][i].name`](#reqcookieskiname) | `string` | The name of the cookie. | | [`req.cookies[k][i].value`](#reqcookieskivalue) | `string` | The value of the cookie. | | [`req.headers`](#reqheaders) | `map[string][]string` | The request headers parsed as a map of lower-case names to values. | | [`req.host`](#reqhost) | `string` | The value of the host header field for this request. | | [`req.location`](#reqlocation) | `string` | The value of the Location header for this request. | | [`req.method`](#reqmethod) | `string` | The method for this request. | | [`req.trailers`](#reqtrailers) | `map[string][]string` | The request trailers parsed as a map of lower-case names to values. | | [`req.ts.body_received`](#reqtsbody-received) | `timestamp` | The timestamp when ngrok received the body of the request. This may not be present if the request does not contain a body. | | [`req.ts.header_received`](#reqtsheader-received) | `timestamp` | The timestamp when ngrok received the header of the request. | | [`req.url`](#requrl) | `string` | The normalized full URL for this request. | | [`req.url.authority`](#requrlauthority) | `string` | The authority portion of the URL. | | [`req.url.host`](#requrlhost) | `string` | The hostname portion of the host for this request. | | [`req.url.path`](#requrlpath) | `string` | The path for this request including the leading forward slash. | | [`req.url.port`](#requrlport) | `int32` | The port portion of the URL. This may not be present if the URL does not explicitly specify a port. | | [`req.url.query`](#requrlquery) | `string` | The full query string for this request excluding the leading question mark. | | [`req.url.query_params`](#requrlquery-params) | `map[string][]string` | The request query string parsed as a map of names to values. | | [`req.url.raw`](#requrlraw) | `string` | The un-normalized full URL for this request. | | [`req.url.raw_path`](#requrlraw-path) | `string` | The un-normalized path including the leading slash for this request. | | [`req.url.scheme`](#requrlscheme) | `string` | The scheme for this request. | | [`req.url.uri`](#requrluri) | `string` | The URI (path and query) portion of the URL. | | [`req.url.user_password`](#requrluser-password) | `string` | The `user:password` portion of the URL. | | [`req.user_agent`](#requser-agent) | `string` | The value of the `User-Agent` header for this request. | | [`req.user_agent.name`](#requser-agentname) | `string` | The name of the user agent in the `User-Agent` header for this request. | | [`req.user_agent.version`](#requser-agentversion) | `string` | The version of the user agent in the `User-Agent` header for this request. | | [`req.user_agent.os`](#requser-agentos) | `string` | The operating system of the user agent in the `User-Agent` header for this request. | | [`req.user_agent.os_version`](#requser-agentos-version) | `string` | The operating system of the user agent in the `User-Agent` header for this request. | | [`req.user_agent.device`](#requser-agentdevice) | `string` | The device in the `User-Agent` header for this request. | | [`req.user_agent.is_mobile`](#requser-agentis-mobile) | `bool` | Whether the `User-Agent` header for this request is a mobile device. | | [`req.user_agent.is_tablet`](#requser-agentis-tablet) | `bool` | Whether the `User-Agent` header for this request is a tablet device. | | [`req.user_agent.is_desktop`](#requser-agentis-desktop) | `bool` | Whether the `User-Agent` header for this request is a desktop device. | | [`req.user_agent.is_bot`](#requser-agentis-bot) | `bool` | Whether the `User-Agent` header for this request is a Bot User. | | [`req.user_agent.url`](#requser-agenturl) | `string` | The URL of the user agent in the `User-Agent` header for this request. | | [`req.version`](#reqversion) | `string` | The HTTP version for this request. | ### `req.content_encoding` The encoding set in the `Content-Encoding` header for this request. ```yaml policy.yml theme={null} expressions: - "req.content_encoding[0] == 'br'" ``` ```json policy.json theme={null} { "expressions": [ "req.content_encoding[0] == 'br'" ] } ``` ### `req.content_length` The content length of the body in bytes. This may not be present if the request does not contain a body or if the client does not specify a content length because they are streaming the body. ```yaml policy.yml theme={null} expressions: - "req.content_length > 10000000" ``` ```json policy.json theme={null} { "expressions": [ "req.content_length > 10000000" ] } ``` ### `req.content_type` The media type set in the `Content-Type` header for this request. ```yaml policy.yml theme={null} expressions: - "req.content_type == 'application/json'" ``` ```json policy.json theme={null} { "expressions": [ "req.content_type == 'application/json'" ] } ``` ### `req.content_type.parameters` The parameters set in the `Content-Type` header for this request. ```yaml policy.yml theme={null} expressions: - "req.content_type.parameters['charset'] == 'utf-8'" ``` ```json policy.json theme={null} { "expressions": [ "req.content_type.parameters['charset'] == 'utf-8'" ] } ``` ### `req.content_type.raw` The value of the `Content-Type` header for this request. ```yaml policy.yml theme={null} expressions: - "req.content_type.raw == 'application/json; charset=utf-8'" ``` ```json policy.json theme={null} { "expressions": [ "req.content_type.raw == 'application/json; charset=utf-8'" ] } ``` ### `req.cookies` The HTTP cookie objects included in this request. If there are multiple cookies with the same name, they will be ordered as specified in the `Cookie` header. ```yaml policy.yml theme={null} expressions: - "size(req.cookies) > 0" ``` ```json policy.json theme={null} { "expressions": [ "size(req.cookies) > 0" ] } ``` ### `req.cookies[k][i].name` The name of the cookie. ```yaml policy.yml theme={null} expressions: - "req.cookies['sessionId'][0].name == 'sessionId'" ``` ```json policy.json theme={null} { "expressions": [ "req.cookies['sessionId'][0].name == 'sessionId'" ] } ``` ### `req.cookies[k][i].value` The value of the cookie. ```yaml policy.yml theme={null} expressions: - "req.cookies['sessionId'][0].value > 'value'" ``` ```json policy.json theme={null} { "expressions": [ "req.cookies['sessionId'][0].value > 'value'" ] } ``` ### `req.headers` The request headers parsed as a map of lower-case names to values. ```yaml policy.yml theme={null} expressions: - "'Fizz' in req.headers['baz']" ``` ```json policy.json theme={null} { "expressions": [ "'Fizz' in req.headers['baz']" ] } ``` ### `req.host` The value of the host header field value for this request. ```yaml policy.yml theme={null} expressions: - "req.host == 'nba.com'" ``` ```json policy.json theme={null} { "expressions": [ "req.host == 'nba.com'" ] } ``` ### `req.location` The value of the Location header for this request. ```yaml policy.yml theme={null} expressions: - "req.location == '/index.html'" ``` ```json policy.json theme={null} { "expressions": [ "req.location == '/index.html'" ] } ``` ### `req.method` The method for this request. ```yaml policy.yml theme={null} expressions: - "req.method == 'POST' || req.method == 'PUT'" ``` ```json policy.json theme={null} { "expressions": [ "req.method == 'POST' || req.method == 'PUT'" ] } ``` ### `req.trailers` The request trailers parsed as a map of lower-case names to values. ```yaml policy.yml theme={null} expressions: - "'Fizz' in req.trailers['baz']" ``` ```json policy.json theme={null} { "expressions": [ "'Fizz' in req.trailers['baz']" ] } ``` ### `req.ts.body_received` The timestamp when ngrok received the body of the request. This may not be present if the request does not contain a body. ```yaml policy.yml theme={null} expressions: - "req.ts.body_received > timestamp('2023-12-31T00:00:00Z')" ``` ```json policy.json theme={null} { "expressions": [ "req.ts.body_received > timestamp('2023-12-31T00:00:00Z')" ] } ``` ### `req.ts.header_received` The timestamp when ngrok received the header of the request. ```yaml policy.yml theme={null} expressions: - "req.ts.header_received > timestamp('2023-12-31T00:00:00Z')" ``` ```json policy.json theme={null} { "expressions": [ "req.ts.header_received > timestamp('2023-12-31T00:00:00Z')" ] } ``` ### `req.url` The normalized full URL for this request. ```yaml policy.yml theme={null} expressions: - "req.url == 'https://nba.com/')" ``` ```json policy.json theme={null} { "expressions": [ "req.url == 'https://nba.com/')" ] } ``` ### `req.url.authority` The authority portion of the URL. ```yaml policy.yml theme={null} expressions: - "req.url.authority == 'user:password@nba.com'" ``` ```json policy.json theme={null} { "expressions": [ "req.url.authority == 'user:password@nba.com'" ] } ``` ### `req.url.host` The hostname portion of the host for this request. ```yaml policy.yml theme={null} expressions: - "req.url.host == 'nba.com'" ``` ```json policy.json theme={null} { "expressions": [ "req.url.host == 'nba.com'" ] } ``` ### `req.url.path` The path for this request including the leading forward slash. ```yaml policy.yml theme={null} expressions: - "req.url.path.startsWith('/foo')" ``` ```json policy.json theme={null} { "expressions": [ "req.url.path.startsWith('/foo')" ] } ``` ### `req.url.port` The port portion of the URL. This may not be present if the URL does not explicitly specify a port. ```yaml policy.yml theme={null} expressions: - "req.url.port == 443" ``` ```json policy.json theme={null} { "expressions": [ "req.url.port == 443" ] } ``` ### `req.url.query` The full query string for this request excluding the leading question mark. ```yaml policy.yml theme={null} expressions: - "req.url.query == 'number=23&name=jordan'" ``` ```json policy.json theme={null} { "expressions": [ "req.url.query == 'number=23&name=jordan'" ] } ``` ### `req.url.query_params` The request query string parsed as a map of names to values. ```yaml policy.yml theme={null} expressions: - "'bar' in req.url.query_params['foo']" ``` ```json policy.json theme={null} { "expressions": [ "'bar' in req.url.query_params['foo']" ] } ``` ### `req.url.raw` The un-normalized full URL for this request. ```yaml policy.yml theme={null} expressions: - "req.url.raw.includes('/foo')" ``` ```json policy.json theme={null} { "expressions": [ "req.url.raw.includes('/foo')" ] } ``` ### `req.url.raw_path` The un-normalized path including the leading slash for this request. ```yaml policy.yml theme={null} expressions: - "req.url.raw_path.startsWith('/foo')" ``` ```json policy.json theme={null} { "expressions": [ "req.url.raw_path.startsWith('/foo')" ] } ``` ### `req.url.scheme` The scheme for this request. ```yaml policy.yml theme={null} expressions: - "req.url.scheme == 'https'" ``` ```json policy.json theme={null} { "expressions": [ "req.url.scheme == 'https'" ] } ``` ### `req.url.uri` The URI (path and query) portion of the URL. ```yaml policy.yml theme={null} expressions: - "req.url.uri.contains('/api/players?number=23&name=jordan')" ``` ```json policy.json theme={null} { "expressions": [ "req.url.uri.contains('/api/players?number=23&name=jordan')" ] } ``` ### `req.url.user_password` The `user:password` portion of the URL. ```yaml policy.yml theme={null} expressions: - "req.url.user_password == 'user:password'" ``` ```json policy.json theme={null} { "expressions": [ "req.url.user_password == 'user:password'" ] } ``` ### `req.user_agent` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.contains('curl')" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.contains('curl')" ] } ``` ### `req.user_agent.name` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.name == 'Safari'" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.name == 'Safari'" ] } ``` ### `req.user_agent.version` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.version == '10.1.2'" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.version == '10.1.2'" ] } ``` ### `req.user_agent.os` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.os == 'macOS'" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.os == 'macOS'" ] } ``` ### `req.user_agent.os_version` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.os_version == '10.12.6'" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.os_version == '10.12.6'" ] } ``` ### `req.user_agent.device` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.device.contains('Android')" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.device.contains('Android')" ] } ``` ### `req.user_agent.is_mobile` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.is_mobile" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.is_mobile" ] } ``` ### `req.user_agent.is_tablet` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.is_tablet" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.is_tablet" ] } ``` ### `req.user_agent.is_desktop` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.is_desktop" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.is_desktop" ] } ``` ### `req.user_agent.is_bot` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.is_bot" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.is_bot" ] } ``` ### `req.user_agent.url` The value of the `User-Agent` header for this request. ```yaml policy.yml theme={null} expressions: - "req.user_agent.url.contains('https')" ``` ```json policy.json theme={null} { "expressions": [ "req.user_agent.url.contains('https')" ] } ``` ### `req.version` The HTTP version for this request. ```yaml policy.yml theme={null} expressions: - "req.version == 'HTTP/2.0'" ``` ```json policy.json theme={null} { "expressions": [ "req.version == 'HTTP/2.0'" ] } ``` # HTTP Response Variables Source: https://ngrok.com/docs/gateway/traffic-policy/variables/res Reference documentation for HTTP response variables in Traffic Policy including status code, headers, and body content. ## Response Variables The following variables are available under the `res` namespace: | Name | Type | Description | | - | - | - | | [`res.content_encoding`](#rescontent-encoding) | `list[string]` | The encoding set in the `Content-Encoding` header for this response. | | [`res.content_length`](#rescontent-length) | `int64` | The length of the content associated with the response. | | [`res.content_type`](#rescontent-type) | `string` | The media type set in the `Content-Type` header for this response. | | [`res.content_type.parameters`](#rescontent-typeparameters) | `map[string]string` | The parameters set in the `Content-Type` header for this response. | | [`res.content_type.raw`](#rescontent-typeraw) | `string` | The `Content-Type` header for this response as a string. | | [`res.cookies`](#rescookies) | `map[string][]cookie` | The HTTP cookie objects included in this response. If there are multiple cookies with the same name, they will be ordered by path length, with longest path first. | | [`res.cookies[k][i].domain`](#rescookieskidomain) | `string` | The value of the `Domain` attribute of the cookie. | | [`res.cookies[k][i].expires`](#rescookieskiexpires) | `timestamp` | The value of the `Expires` attribute of the cookie. | | [`res.cookies[k][i].http_only`](#rescookieskihttp-only) | `boolean` | The value of the `HttpOnly` attribute of the cookie. | | [`res.cookies[k][i].max_age`](#rescookieskimax-age) | `duration` | The value of the `MaxAge` attribute of the cookie. | | [`res.cookies[k][i].name`](#rescookieskiname) | `string` | The name of the cookie. | | [`res.cookies[k][i].path`](#rescookieskipath) | `string` | The value of the `Path` attribute of the cookie. | | [`res.cookies[k][i].same_site`](#rescookieskisame-site) | `string` | The value of the `SameSite` attribute of the cookie. | | [`res.cookies[k][i].secure`](#rescookieskisecure) | `boolean` | The value of the `Secure` attribute of the cookie. | | [`res.cookies[k][i].unparsed_attributes`](#rescookieskiunparsed-attributes) | `map[string]string` | Any non-standard attributes of the cookie parsed as a map of names to values . | | [`res.cookies[k][i].value`](#rescookieskivalue) | `string` | The value of the cookie. | | [`res.headers`](#resheaders) | `map[string][]string` | The response headers parsed as a map of lower-case names to values. | | [`res.location`](#reslocation) | `string` | The `Location` header value of this response. | | [`res.status_code`](#resstatus-code) | `int32` | The status code of this response. | | [`res.trailers`](#restrailers) | `map[string][]string` | The response trailers parsed as a map of lower-case names to values. | ### `res.content_encoding` The encoding set in the Content-Encoding header for this response. ```yaml policy.yml theme={null} expressions: - "res.content_encoding[0] == 'br'" ``` ```json policy.json theme={null} { "expressions": [ "res.content_encoding[0] == 'br'" ] } ``` ### `res.content_length` The length of the content associated with the response. ```yaml policy.yml theme={null} expressions: - "res.content_length != 0" ``` ```json policy.json theme={null} { "expressions": [ "res.content_length != 0" ] } ``` ### `res.content_type` The media type set in the Content-Type header for this response. ```yaml policy.yml theme={null} expressions: - "res.content_type == 'application/json'" ``` ```json policy.json theme={null} { "expressions": [ "res.content_type == 'application/json'" ] } ``` ### `res.content_type.parameters` The parameters set in the Content-Type header for this response. ```yaml policy.yml theme={null} expressions: - "res.content_type.parameters['charset'] == 'utf-8'" ``` ```json policy.json theme={null} { "expressions": [ "res.content_type.parameters['charset'] == 'utf-8'" ] } ``` ### `res.content_type.raw` The Content-Type header for this response as a string. ```yaml policy.yml theme={null} expressions: - "res.content_type.raw == 'application/json; charset=utf-8'" ``` ```json policy.json theme={null} { "expressions": [ "res.content_type.raw == 'application/json; charset=utf-8'" ] } ``` ### `res.cookies` The HTTP cookie objects included in this response. If there are multiple cookies with the same name, they will be ordered by path length, with longest path first. ```yaml policy.yml theme={null} expressions: - "size(res.cookies) > 0" ``` ```json policy.json theme={null} { "expressions": [ "size(res.cookies) > 0" ] } ``` ### `res.cookies[k][i].domain` The value of the `Domain` attribute of the cookie. ```yaml policy.yml theme={null} expressions: - "res.cookies['sessionId'][0].domain == 'nba.com'" ``` ```json policy.json theme={null} { "expressions": [ "res.cookies['sessionId'][0].domain == 'nba.com'" ] } ``` ### `res.cookies[k][i].expires` The value of the `Expires` attribute of the cookie. ```yaml policy.yml theme={null} expressions: - "res.cookies['sessionId'][0].expires > timestamp(time.now)" ``` ```json policy.json theme={null} { "expressions": [ "res.cookies['sessionId'][0].expires > timestamp(time.now)" ] } ``` ### `res.cookies[k][i].http_only` The value of the `HttpOnly` attribute of the cookie. ```yaml policy.yml theme={null} expressions: - "res.cookies['sessionId'][0].http_only" ``` ```json policy.json theme={null} { "expressions": [ "res.cookies['sessionId'][0].http_only" ] } ``` ### `res.cookies[k][i].max_age` The value of the `MaxAge` attribute of the cookie. ```yaml policy.yml theme={null} expressions: - "res.cookies['sessionId'][0].max_age > duration('24h')" ``` ```json policy.json theme={null} { "expressions": [ "res.cookies['sessionId'][0].max_age > duration('24h')" ] } ``` ### `res.cookies[k][i].name` The name of the cookie. ```yaml policy.yml theme={null} expressions: - "res.cookies['sessionId'][0].name == 'sessionId'" ``` ```json policy.json theme={null} { "expressions": [ "res.cookies['sessionId'][0].name == 'sessionId'" ] } ``` ### `res.cookies[k][i].path` The value of the `Path` attribute of the cookie. ```yaml policy.yml theme={null} expressions: - "res.cookies['sessionId'][0].path == '/" ``` ```json policy.json theme={null} { "expressions": [ "res.cookies['sessionId'][0].path == '/" ] } ``` ### `res.cookies[k][i].same_site` The value of the `SameSite` attribute of the cookie. ```yaml policy.yml theme={null} expressions: - "res.cookies['sessionId'][0].same_site" ``` ```json policy.json theme={null} { "expressions": [ "res.cookies['sessionId'][0].same_site" ] } ``` ### `res.cookies[k][i].secure` The value of the `Secure` attribute of the cookie. ```yaml policy.yml theme={null} expressions: - "res.cookies['sessionId'][0].secure" ``` ```json policy.json theme={null} { "expressions": [ "res.cookies['sessionId'][0].secure" ] } ``` ### `res.cookies[k][i].unparsed_attributes` Any non-standard attributes of the cookie parsed as a map of names to values. ```yaml policy.yml theme={null} expressions: - "size(res.cookies['sessionId'][0].unparsed_attributes) > 0" ``` ```json policy.json theme={null} { "expressions": [ "size(res.cookies['sessionId'][0].unparsed_attributes) > 0" ] } ``` ### `res.cookies[k][i].value` The value of the cookie. ```yaml policy.yml theme={null} expressions: - "res.cookies['sessionId'][0].value > 'value'" ``` ```json policy.json theme={null} { "expressions": [ "res.cookies['sessionId'][0].value > 'value'" ] } ``` ### `res.headers` The response headers parsed as a map of lower-case names to values. ```yaml policy.yml theme={null} expressions: - "'Fizz' in res.headers['baz']" ``` ```json policy.json theme={null} { "expressions": [ "'Fizz' in res.headers['baz']" ] } ``` ### `res.location` The location header value of the response. ```yaml policy.yml theme={null} expressions: - "res.location == '/index.html'" ``` ```json policy.json theme={null} { "expressions": [ "res.location == '/index.html'" ] } ``` ### `res.status_code` The status code of this response. ```yaml policy.yml theme={null} expressions: - "res.status_code >= 300" ``` ```json policy.json theme={null} { "expressions": [ "res.status_code >= 300" ] } ``` ### `res.trailers` The response trailers parsed as a map of lower-case names to values. ```yaml policy.yml theme={null} expressions: - "'fizz' in res.trailers['baz']" ``` ```json policy.json theme={null} { "expressions": [ "'fizz' in res.trailers['baz']" ] } ``` # Time Variables Source: https://ngrok.com/docs/gateway/traffic-policy/variables/time Reference documentation for time variables in Traffic Policy for accessing current time, date, and timezone information. ## Time variables The following variables are available under the `time` namespace: | Name | Type | Description | | - | - | - | | [`time.now`](#timenow) | `string` | The current UTC time in [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339) format. | ### `time.now` The current UTC time in [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339) format. ```yaml policy.yml theme={null} expressions: - "conn.ts.end < timestamp(time.now)" ``` ```json policy.json theme={null} { "expressions": [ "conn.ts.end < timestamp(time.now)" ] } ``` # Webhooks to On-Prem Source: https://ngrok.com/docs/gateway/webhooks-to-on-prem/overview Use ngrok to grant controlled, compliant access by trusted third-party webhook providers to services inside your network. ngrok lets trusted third-party cloud services like GitHub, Twilio, and Stripe send webhooks to applications running behind your firewall. ngrok verifies webhook signatures at its network edge, so only legitimate traffic reaches your applications. ## Why route webhooks this way? * **Centralize control.** Verification, routing, and secrets live in a single Traffic Policy. Adding a webhook provider means updating one policy, not rolling out code changes to multiple services. * **Prevent webhook spoofing.** Signatures are verified at the edge for [70+ providers](/docs/gateway/traffic-policy/actions/verify-webhook/#supported-providers). Spoofed or tampered requests are rejected before they reach any of your compute. ## Which scenario fits you? Your service runs on-prem or in a private network that blocks inbound connections. Your service handles PHI or other regulated data in a cloud container. A platform team owns service configuration, access, and auth from a single surface. ## Next steps Set up one front door that verifies and routes webhooks to a private service, then adapt it to your scenario. Browse the 70+ supported webhook providers, including Twilio, Shopify, and DocuSign. Read how signature verification proves that a webhook came from the provider. Keep provider signing secrets in an encrypted vault instead of pasting them into your policy. # Webhooks to On-Prem Quickstart Source: https://ngrok.com/docs/gateway/webhooks-to-on-prem/quickstart Verify webhooks and route them to a private service. Not sure which setup fits you? The [full tutorial](/docs/gateway/webhooks-to-on-prem/tutorial) covers three variants: behind a firewall, regulated workloads, and platform-governed exposure. This quickstart walks you through the fastest path to a working webhook gateway using ngrok. You'll learn how to: * Run an internal endpoint in your network alongside the service that will receive webhooks * Create a Cloud Endpoint that verifies each webhook's signature and forwards it to the internal endpoint * Confirm that a genuine webhook reaches your service and a forged one is rejected This example verifies GitHub webhooks, but the shape is identical for any [supported provider](/docs/gateway/traffic-policy/actions/verify-webhook/#supported-providers). ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/signup). * The [ngrok agent](/docs/getting-started/) installed on the machine where your service runs. * A service running locally that should receive webhooks (this quickstart uses port `8080` as an example). * The webhook signing secret for your provider. In GitHub, you'll set the secret at the repository level: **Settings** → **Webhooks** → **Secret**. ## 1. Configure an internal endpoint Open the ngrok configuration file on the machine running your service and add an internal endpoint that points to it. You can do so by editing your config file with the `ngrok config edit` terminal command. Update yours to resemble the following snippet. ```yaml title="ngrok.yml" highlight={6-10} theme={null} version: 3 agent: authtoken: YOUR_AUTHTOKEN endpoints: - name: my-service url: https://my-service.internal upstream: url: 8080 ``` Replace `YOUR_AUTHTOKEN` with the authtoken from your [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). Next, start the agent: ```bash theme={null} ngrok start --all ``` ```bash theme={null} ngrok start --config /path/to/ngrok.yml --all ``` Webhooks still can't reach your service until you create a Cloud Endpoint. ## 2. Create a Cloud Endpoint Cloud Endpoints are persistent, always-on endpoints managed from the dashboard or API. They use an attached Traffic Policy to handle incoming connections. This is where webhook verification and request routing happen. [Create a new Cloud Endpoint](https://dashboard.ngrok.com/endpoints/new/cloud) in the dashboard. Replace the default Traffic Policy with the following: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: github secret: "YOUR_GITHUB_WEBHOOK_SECRET" - type: forward-internal config: url: https://my-service.internal ``` This block of YAML uses the `verify-webhook` Traffic Policy Action to check the signature on every incoming request and forward only genuine GitHub webhooks to the internal endpoint you configured in step 1. A request that fails verification is rejected with a `403` before it ever reaches your service. ## 3. Test it Paste the URL of your ngrok Cloud Endpoint into your GitHub webhook settings at **Settings** → **Webhooks** → **Payload URL**. Make sure that the webhook secret matches the one in your Traffic Policy. Use GitHub's **Recent Deliveries** → **Redeliver** to send a signed event to your Cloud Endpoint. You should receive a verified, signed webhook on your service on port `8080`. Make a curl request to confirm that unsigned webhooks are rejected: ```bash theme={null} curl -i https:// ``` You should get a `403 Forbidden` since the request has no valid signature. ## What's next * [Follow the full tutorial](/docs/gateway/webhooks-to-on-prem/tutorial) for a complete setup with multiple providers, vaulted secrets, and authtoken ACLs. * [Store your signing secrets in a vault](/docs/gateway/traffic-policy/secrets) instead of pasting them into your policy. * [Verify webhooks from a provider that isn't on the supported list](/docs/gateway/webhooks-to-on-prem/tutorial#verifying-a-provider-that-isnt-on-the-supported-list) using a shared-secret check or upstream verification. * Read how [signature verification](/docs/gateway/traffic-policy/actions/verify-webhook) works and review security best practices. # Webhooks to On-Prem Full Tutorial Source: https://ngrok.com/docs/gateway/webhooks-to-on-prem/tutorial Follow an in-depth tutorial to set up webhook verification and routing to your internal services, all managed by a single Traffic Policy. This guide sets up a single entry point where webhooks access your internal services. Verify and route requests from third-party providers without opening your firewall or using a VPN. ngrok can verify webhooks from 70+ providers out of the box. See the complete list of [supported webhook providers](/docs/gateway/traffic-policy/actions/verify-webhook/#supported-providers). ## How it works Webhooks hit one public address that you configure for your Cloud Endpoint. A single Traffic Policy on that address checks the signature on every request, rejects anything that fails, and only forwards verified requests. The ngrok agent opens an outbound connection to ngrok and forwards webhook traffic to your internal service, so you never have to open an inbound port or tap your firewall. Diagram showing a webhook provider sending a request to a single public ngrok address that verifies each signature and forwards genuine requests inward to a private service ## Three ways teams use this The webhook gateway shape is the same for everyone. What differs is where your service runs and what you most need to guarantee: * **Behind a firewall:** Your service runs on-prem or in a private network that blocks inbound connections. Webhooks reach your port without you having to open inbound connections. * **Regulated workloads:** Your service handles PHI or other regulated data, often in a cloud container. The path has to be end-to-end encrypted and must not capture or store payloads. * **Platform-governed:** A platform team owns service configuration, access, and auth from a single surface. Endpoints and policy are declared as version-controlled config, and agents are scoped so developers can't create shadow exposure. In this tutorial, you'll build the shared front door in steps 1–4, then apply the guardrail for your scenario in step 5. ## Tutorial ### What you'll need * An ngrok account. If you don't have one, [sign up](https://dashboard.ngrok.com/signup). * The [ngrok agent](/docs/getting-started/), one of the [SDKs](/docs/agent-sdks), or the [Kubernetes Operator](/docs/k8s) installed where your service runs. * The webhook signing secret for each provider you want to verify. You can find these in each provider's developer dashboard. * (Optional) The ngrok agent CLI if you want to create a vault for your webhook secrets. ### 1. Give the receiving service a private address Each service that handles webhooks gets an internal [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints/): a private address that only receives traffic when it's forwarded through the [`forward-internal`](/docs/gateway/traffic-policy/actions/forward-internal/) action. Internal endpoint hostnames must end with `.internal`. How you create it depends on where the service runs. Run the agent next to your service. It connects outbound over TLS on port 443, so nothing needs to change on the firewall. ```bash theme={null} ngrok http $SERVICE_PORT --url https://payment-service.internal ``` Run the agent (or an [SDK](/docs/agent-sdks)) inside the container alongside your app. The container only needs outbound network access—no ingress rule, no public IP—so a workload with an egress-only policy can still receive webhooks. ```bash theme={null} ngrok http $SERVICE_PORT --url https://payment-service.internal ``` Let the [ngrok Operator](/docs/gateway/k8s/) manage the endpoint as a version-controlled resource, so the platform team owns it in Git rather than in someone's shell history. ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: payment-service namespace: webhooks spec: url: https://payment-service.internal upstream: url: http://payment-service.webhooks:8080 ``` Repeat for every service that will receive webhook, for example, `notif-service.internal` and `deploy-service.internal`. ### 2. Create the front door Navigate to the [**Endpoints**](https://dashboard.ngrok.com/endpoints) section of the dashboard, click **New +**, then **Cloud Endpoint**, and enter the domain you want to use for your public URL. (In Kubernetes, you can create this as a [`CloudEndpoint`](/docs/gateway/k8s/crds/cloudendpoint/) resource instead.) ### 3. (Optional) Store your signing secrets in a vault Each route verifies a provider's signature using that provider's signing secret. For production, keep those secrets in [Secrets for Traffic Policy](/docs/gateway/traffic-policy/secrets) so they're encrypted and referenced by name rather than pasted in plaintext. This step is optional, but a vault keeps secrets out of your policy source and lets you rotate them without editing routes. ```bash theme={null} ngrok api vaults create --name "webhook-secrets" --description "Webhook validation secrets" ``` Add each provider's secret, changing `$VAULT_ID` to match the vault ID returned above: ```bash theme={null} ngrok api secrets create --name "github-secret" --value "your_github_webhook_secret_here" --vault-id "$VAULT_ID" ngrok api secrets create --name "twilio-secret" --value "your_twilio_auth_token_here" --vault-id "$VAULT_ID" ngrok api secrets create --name "stripe-secret" --value "your_stripe_secret_here" --vault-id "$VAULT_ID" ``` ### 4. Verify the sender and route inward On your Cloud Endpoint's Traffic Policy, each rule matches a provider by request path, verifies the signature with [`verify-webhook`](/docs/gateway/traffic-policy/actions/verify-webhook), and forwards verified requests to the matching internal service with [`forward-internal`](/docs/gateway/traffic-policy/actions/forward-internal/). A request with a bad or missing signature is rejected with a `403`. ```yaml theme={null} on_http_request: - expressions: - "req.url.path.startsWith('/github')" actions: - type: verify-webhook config: provider: github secret: "${secrets.get('webhook-secrets', 'github-secret')}" - type: forward-internal config: url: https://deploy-service.internal - expressions: - "req.url.path.startsWith('/twilio')" actions: - type: verify-webhook config: provider: twilio secret: "${secrets.get('webhook-secrets', 'twilio-secret')}" - type: forward-internal config: url: https://notif-service.internal - expressions: - "req.url.path.startsWith('/stripe')" actions: - type: verify-webhook config: provider: stripe secret: "${secrets.get('webhook-secrets', 'stripe-secret')}" - type: forward-internal config: url: https://payment-service.internal ``` ```yaml theme={null} on_http_request: - expressions: - "req.url.path.startsWith('/github')" actions: - type: verify-webhook config: provider: github secret: "your_github_webhook_secret_here" - type: forward-internal config: url: https://deploy-service.internal - expressions: - "req.url.path.startsWith('/twilio')" actions: - type: verify-webhook config: provider: twilio secret: "your_twilio_auth_token_here" - type: forward-internal config: url: https://notif-service.internal - expressions: - "req.url.path.startsWith('/stripe')" actions: - type: verify-webhook config: provider: stripe secret: "your_stripe_secret_here" - type: forward-internal config: url: https://payment-service.internal ``` ### 5. Add the guardrail your use case needs The front door verifies and routes for everyone. Layer on additional security depending on what you need most. Nothing more is required: the agent's connection is outbound-only over port 443, and your `.internal` services are only reachable through `forward-internal`. For PHI or other regulated data, keep the payload encrypted the whole way and make sure ngrok never retains it: * **End-to-end encryption.** Terminate TLS within your network at [the ngrok agent](/docs/gateway/agent/agent-tls-termination) or at your upstream service. With this configuration, ngrok forwards ciphertext rather than decrypting traffic at the edge. * **No stored payloads.** Leave full traffic capture off. Use TCP or TLS endpoints to bypass all traffic capture, including most request metadata. Review ngrok's compliance posture and request a BAA through the [Trust Center](https://trust.ngrok.com) before sending regulated data. Let the platform team issue ACL-bound authtokens and manage Traffic Policy config as code. * **Scope the agent's authtoken with an ACL** so you define what endpoints that agent can start. See [Authtoken ACLs](/docs/gateway/agent/cli-api) for the full syntax, but as an example: ``` bind:deploy-service.internal ``` * **Declare endpoints and policy as version-controlled resources**. ### 6. Configure your webhook URL at each provider and test Add your ngrok URL and provider-specific webhook path within each provider's dashboard: * **GitHub**: `https://$NGROK_DOMAIN/github` * **Twilio**: `https://$NGROK_DOMAIN/twilio` * **Stripe**: `https://$NGROK_DOMAIN/stripe` Most providers offer a "send test event" button. Trigger one and watch it flow through: a valid event is verified and forwarded to the matching internal service, while a request with a bad or missing signature is rejected at the front door. You can confirm what happened for each request in the [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector). If you turned the inspector off for a regulated endpoint, verify against your own service logs instead. ## Verifying a provider that isn't on the supported list `verify-webhook` supports a fixed set of [providers](/docs/gateway/traffic-policy/actions/verify-webhook/#supported-providers). If your webhook provider isn't on the list, here are your options: ### Use a Traffic Policy expression If the provider authenticates by sending a static secret in a header or query parameter, check for the header in a Traffic Policy expression and reject anything that fails: ```yaml theme={null} on_http_request: - expressions: - "req.headers.get('x-webhook-secret') != '${secrets.get('webhook-secrets', 'shared-secret')}'" actions: - type: deny config: status_code: 403 # forward-internal rules follow ``` Combine with the [`restrict-ips`](/docs/gateway/traffic-policy/actions/restrict-ips) action locked to the provider's published IP ranges or with `jwt-validation` if the webhook arrives as a signed JWT. ### Ask us to add it Email us at [support@ngrok.com](mailto:support@ngrok.com) to [request your provider](/docs/gateway/traffic-policy/actions/verify-webhook/#supported-providers). # Cloud Endpoints Quickstart Source: https://ngrok.com/docs/getting-started/cloud-endpoints-quickstart Set up your first ngrok Cloud Endpoint to start managing traffic to your services. Cloud Endpoints provide a persistent way to service traffic. Unlike Agent Endpoints, which are only available as long as the agent is running, Cloud Endpoints run on ngrok's cloud service, allowing you to accept public traffic even if your internal apps or services are down. Cloud Endpoints provide an additional layer of traffic management above Agent Endpoints and can be used to extend their functionality in several key ways. Using a Traffic Policy, a Cloud Endpoint can route traffic to multiple other endpoints based on criteria such as path, header, IP, and more. A common use case for this pattern is to create a Cloud Endpoint that accepts public traffic, authenticates it, then routes it to one of several internal Agent Endpoints running on machines that require privileged access. This guide walks you through creating Cloud Endpoints in two ways: first, through the ngrok dashboard, and second, using the Agent CLI to interact with the ngrok API. ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/signup). * Your [ngrok auth token](https://dashboard.ngrok.com/get-started/your-authtoken). * An [ngrok API key](https://dashboard.ngrok.com/api-keys). This doc assumes you've already completed the [Agent CLI Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/)—if not, you should start there to learn about some of ngrok's fundamental features before continuing here. ## Part one: your first Cloud Endpoint The best way to familiarize yourself with Cloud Endpoints is to create one in your ngrok dashboard. ### 1. Install the ngrok Agent CLI If this is your first time using ngrok, run the command that corresponds to your operating system to install the Agent CLI: ```bash theme={null} brew install ngrok ``` ```bash theme={null} curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \ | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \ && echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \ | sudo tee /etc/apt/sources.list.d/ngrok.list \ && sudo apt update \ && sudo apt install ngrok ``` Install via the Windows App Store Or [follow the direct installation guide](https://download.ngrok.com?tab=download) if you can't use one of the options above. To test that it's been installed correctly, run the following command in your terminal and confirm that ngrok prints its help text. ```bash theme={null} ngrok help ``` ### 2. Connect your account Connect your agent to your ngrok account by providing your auth token as shown below—replace `$YOUR_TOKEN` with the string given to you [in the dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). ```bash theme={null} ngrok config add-authtoken $YOUR_TOKEN ``` ### 3. Create your Cloud Endpoint in the dashboard [Click here to create a new Cloud Endpoint](https://dashboard.ngrok.com/endpoints/new/cloud) in your dashboard. There are three bindings to choose from: for this example, select **Public**. In the **URL** field, enter the domain you reserved in step 1. Then click **Create Cloud Endpoint**. This takes you to a page where you can manage your new endpoint's [Traffic Policy](/docs/gateway/traffic-policy/). You can leave all of the default settings in place for now. Traffic policies are optional for Agent Endpoints but required for Cloud Endpoints, which is why the dashboard generates one for you automatically. Visit the URL you specified to confirm that it's online. You should see a default landing page that says "This is your new Cloud Endpoint!" There are many different things you could do with this endpoint from here. Now you can put it to work forwarding traffic to an app running on your local device. ### 4. Start your app or service Start up the app or service you'd like to put online. If you don't have an app to work with, you can create a minimal app in your language of choice using the following code to set up a basic HTTP server at port `8080`. ```javascript theme={null} const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/html' }); const html = ` Test Page

Hello from Node.js HTTP Server!

`; res.end(html); }); const port = 8080; server.listen(port, () => { console.log(`Serving custom HTML at http://localhost:${port}`); }); ``` Navigate to the directory where this file is located and run the following command to start the server: ```bash theme={null} node service.js ```
```go title="service.go" theme={null} package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go HTTP Server!") } func main() { http.HandleFunc("/", handler) fmt.Println("Starting server at http://localhost:8080") err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Println("Server failed:", err) } } ``` Navigate to the directory where this file is located and run the following command to start the server: ```bash theme={null} go run service.go ``` ```python title="service.py" theme={null} from http.server import BaseHTTPRequestHandler, HTTPServer class MyHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() html = b""" Test Page

Hello from Python HTTP Server!

""" self.wfile.write(html) def run(server_class=HTTPServer, handler_class=MyHandler, port=8080): server_address = ('', port) httpd = server_class(server_address, handler_class) print(f"Serving custom HTML at http://localhost:{port}") httpd.serve_forever() if __name__ == "__main__": run() ``` Navigate to the directory where this file is located and run the following command to start the server: ```bash theme={null} python service.py ```
First, create a directory for your Rust project and navigate into it: ```bash theme={null} mkdir ngrok-rust-server cd ngrok-rust-server ``` Next, create a new Rust project: ```bash theme={null} cargo init ``` Then, replace the contents of `src/main.rs` with the following code to set up a basic HTTP server: ```rust title="ngrok-rust-server/src/main.rs" theme={null} use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 512]; // Read from the stream if let Ok(_) = stream.read(&mut buffer) { let body = "Hello from Rust HTTP Server!"; let response = format!( "HTTP/1.1 200 OK\r\n\ Content-Length: {}\r\n\ Content-Type: text/plain\r\n\ Connection: close\r\n\r\n\ {}", body.len(), body ); if let Err(e) = stream.write_all(response.as_bytes()) { eprintln!("Write error: {}", e); } // Flush to make sure the response is sent if let Err(e) = stream.flush() { eprintln!("Flush error: {}", e); } } } fn main() -> std::io::Result<()> { let listener = TcpListener::bind("127.0.0.1:8080")?; println!("Server running at http://127.0.0.1:8080"); for stream in listener.incoming() { match stream { Ok(stream) => handle_connection(stream), Err(e) => eprintln!("Connection failed: {}", e), } } Ok(()) } ``` To run the server, navigate to the root directory of the project (where the `Cargo.toml` file is located) and run the following command: ```bash theme={null} cargo run ```
### 5. Start your Agent endpoint Start the ngrok agent by running the following command (replace `8080` if your app is running on a different port): ```bash theme={null} ngrok http 8080 --url https://default.internal ``` The `--url` flag connects your Agent Endpoint to your Cloud Endpoint, so when requests are made to your domain they'll be routed here. The default URL for this is `https://default.internal`, which was defined in the Traffic Policy when you created the Cloud Endpoint in step 4. ### 6. Test your Cloud Endpoint Your endpoint should now be online. Visit your domain URL to see the app that's running on your local device. ## Part two: creating Cloud Endpoints via the ngrok API Now that you're familiar with the key concepts necessary to work with Cloud Endpoints, you'll create another one using the ngrok API, which you can access via the Agent CLI. (You can use any HTTP client you prefer for interacting with the ngrok API, but the Agent CLI provides a simple and direct access point for getting started.) Keep your local app and Agent Endpoint from part one running. If you only have one domain to work with, delete the Cloud Endpoint you created in the dashboard before continuing so you can reuse the domain in the steps below. ### 1. Create your Traffic Policy Create a `policy.yml` on your local machine and add the code snippet below. This policy states that incoming HTTP requests should be forwarded to your internal Agent Endpoint at `https://default.internal`; if that endpoint's not reachable, then it falls back on a custom response to indicate that the app is offline. ```yaml title="policy.yml" theme={null} on_http_request: - actions: - type: "forward-internal" config: url: "https://default.internal" on_error: continue - type: custom-response config: status_code: 200 headers: content-type: text/html body: | Agent offline!

Run ngrok http 80 --url https://default.internal to put your application online!

``` ### 2. Create your Cloud Endpoint Create your Cloud Endpoint by running the following command—replace `$YOUR_API_KEY` and `$YOUR_DOMAIN` with their respective values: ```bash theme={null} ngrok api endpoints create \ --api-key $YOUR_API_KEY \ --description "Cloud endpoint for my API" \ --type cloud \ --bindings public \ --url `$YOUR_DOMAIN` \ --traffic-policy-file policy.yml ``` This command contains all of the configuration steps you worked through in the dashboard to create a Cloud Endpoint in part one. ### 3. Test your endpoint Your endpoint should now be online. Visit your reserved domain URL to see the app that's running on your local device. To see the fallback response, terminate your local app or Agent Endpoint and then visit your Cloud Endpoint URL again. You should see the custom message defined in `policy.yml.` ## What's next? In this guide, you learned how to create a Cloud Endpoint using both the dashboard and the ngrok API. You configured a basic Traffic Policy to forward requests from one endpoint to another, and you saw how Cloud Endpoints can be layered on top of Agent Endpoints to route traffic to different services and fallback responses based on the criteria you define. What else can you do with these features? * See [the Cloud Endpoints overview](/docs/gateway/endpoints/cloud-endpoints/#use-cases) to learn more about use cases, how they compare to Agent Endpoints, and more. * If you need to interact with Cloud Endpoints programmatically, check out ngrok's [API client libraries](/docs/api/#client-libraries). * See [Load Balancing with Cloud Endpoints](/docs/gateway/endpoints/cloud-endpoints/forwarding-and-load-balancing/) to learn how to distribute traffic across multiple internal endpoints. * This quickstart barely scratches the surface of what's possible with a Traffic Policy—check out the [Actions overview](/docs/gateway/traffic-policy/actions/) to see what else you can do. # Go SDK Quickstart Source: https://ngrok.com/docs/getting-started/go Learn how to get started with the ngrok Go SDK to create secure tunnels from your Go applications to the internet. Complete the ngrok Go SDK quickstart: 1. Create `service.go` — a Go HTTP server on port 8080 that responds "Hello from Go HTTP Server!" 2. Create a new Go module: `mkdir ngrok-go-demo && cd ngrok-go-demo && go mod init ngrok-go-demo` 3. Install dependencies: `go get golang.ngrok.com/ngrok/v2 github.com/joho/godotenv` 4. Create a `.env` file with NGROK\_AUTHTOKEN and NGROK\_DOMAIN — ask me for these values 5. Create `endpoint.go` using `agent.Forward()` to tunnel port 8080 to my reserved domain with a Google OAuth traffic policy 6. Run `go run service.go` in one terminal and `go run endpoint.go` in another 7. Confirm the tunnel URL is printed and that visiting it requires a Google login Ask me for my auth token and reserved domain before starting. [The ngrok Go SDK](https://pkg.go.dev/golang.ngrok.com/ngrok/v2) is an open-source, idiomatic Go package that enables you to quickly and efficiently serve Go applications on the internet without the need to configure low-level network primitives like IPs, certificates, load balancers, or ports. You can think of it as the [ngrok Agent CLI](/docs/gateway/endpoints/agent-cli-quickstart/) packaged as a Go library. This quickstart uses the ngrok Go SDK to create an Agent Endpoint that forwards traffic from the internet to a Go app running on your local device, then secure it by requiring visitors to log in with a Google account to access it. If you'd prefer to skip the boilerplate setup, you can clone the complete example from the [ngrok-samples collection](https://github.com/ngrok-samples/go-quickstart). In that case, you'll just need to reserve a domain and add your auth token as described below to get started. ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/signup). * Your [ngrok auth token](https://dashboard.ngrok.com/get-started/your-authtoken). * [Go installed](https://go.dev/doc/install) on your machine. You can check this by running `go version` in your terminal. ## 1. Start your app or service Start up the app or service you'd like to put online. This is the app that your Agent Endpoint will forward online traffic to. If you don't have an app to work with, you can create a minimal Go app using the following code to set up a basic HTTP server at port `8080`. ```go title="service.go" theme={null} package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go HTTP Server!") } func main() { http.HandleFunc("/", handler) fmt.Println("Starting server at http://localhost:8080") err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Println("Server failed:", err) } } ``` Navigate to the directory where this file is located and run the following command to start the server: ```bash theme={null} go run service.go ``` ## 2. Install the Go SDK Open a new terminal, then run the following commands to create a new Go module and install the SDK: ```bash theme={null} mkdir ngrok-go-demo cd ngrok-go-demo go mod init ngrok-go-demo go get golang.ngrok.com/ngrok/v2 ``` ## 3. Create a .env file Create a `.env` file and add [your ngrok auth token](https://dashboard.ngrok.com/get-started/your-authtoken) and reserved domain to it. This file needs to be in the same directory where you initialized your Go module in the previous step. Run the following command to create the `.env` file: ```bash theme={null} touch .env ``` Add your ngrok auth token and reserved domain to this file: ```txt title=".env" theme={null} NGROK_AUTHTOKEN=your_actual_auth_token_here NGROK_DOMAIN=your_actual_domain_here ``` Finally, you'll need to install [the `godotenv` package](https://github.com/joho/godotenv?tab=readme-ov-file#godotenv--) to access your environment variables from your code: ```bash theme={null} go get github.com/joho/godotenv ``` If you don't want to use a `.env` file, you can instead [export your auth token as an environment variable](https://support.apple.com/en-il/guide/terminal/apd382cc5fa-4f58-4449-b20a-41c53c006f8f/mac) in your terminal session, or [call `ngrok.WithAuthToken("your-token")`](https://pkg.go.dev/golang.ngrok.com/ngrok/v2#WithAuthtoken) in your code. You can set your reserved domain directly in your endpoint file below using `ngrok.WithURL()`. ## 4. Create your endpoint Create your Agent Endpoint, which will forward public traffic to your app. Create a new file named `endpoint.go` in the same directory as your `.env` file and add the following code. This example: * Starts an Agent Endpoint that forwards from your reserved domain to your service running on port `8080`. * Secures the endpoint with a Traffic Policy that requires authentication via Google OAuth. This example uses ngrok's default Google OAuth application. To use your own, see [the OAuth Traffic Policy Action documentation](/docs/gateway/traffic-policy/actions/oauth/#google-example). ```go title="endpoint.go" theme={null} package main import ( "context" "fmt" "log" "os" "github.com/joho/godotenv" "golang.ngrok.com/ngrok/v2" ) func main() { if err := godotenv.Load(".env"); err != nil { log.Fatal("Error loading .env file", err) } if err := run(context.Background()); err != nil { log.Fatal(err) } } const trafficPolicy = ` on_http_request: - actions: - type: oauth config: provider: google ` const address = "http://localhost:8080" func run(ctx context.Context) error { agent, err := ngrok.NewAgent(ngrok.WithAuthtoken(os.Getenv("NGROK_AUTHTOKEN"))) if err != nil { return err } ln, err := agent.Forward(ctx, ngrok.WithUpstream(address), ngrok.WithURL(os.Getenv("NGROK_DOMAIN")), ngrok.WithTrafficPolicy(trafficPolicy), ) if err != nil { fmt.Println("Error", err) return err } fmt.Println("Endpoint online: forwarding from", ln.URL(), "to", address) // Explicitly stop forwarding; otherwise it runs indefinitely <-ln.Done() return nil } ``` ## 5. Test your endpoint Test your endpoint by running the following terminal command: ```bash theme={null} go run endpoint.go ``` This should print your reserved domain URL to the terminal. When you visit the URL, you should be prompted to log in with Google. After logging in, you'll see your app or service. If you used the example app in this quickstart, you'll see **"Hello from Go HTTP Server!"** displayed in your browser. ## What's next? In this guide, you learned how to use the Go SDK to an create Agent Endpoint that forwards traffic to your localhost. You saw one way to implement a Traffic Policy directly in your codebase, including an action that adds authentication to your app with no configuration required. What else can you do with these features? * Dig deeper into [Traffic Policies](/docs/gateway/traffic-policy/), which enable you to [grant conditional access](/docs/gateway/traffic-policy/examples/add-authentication/#restrict-access-by-email-domain), [send custom headers](/docs/gateway/traffic-policy/actions/add-headers/#examples), [rewrite URLs](/docs/gateway/traffic-policy/actions/url-rewrite/#examples), and more with your endpoints. * Learn more about the [Agent SDKs](/docs/agent-sdks/) to understand how the Go SDK works under the hood. * Check out [the ngrok Go SDK repo](https://github.com/ngrok/ngrok-go?tab=readme-ov-file#ngrok-go) for more code examples. * If your use case calls for a centrally managed, always-on endpoint instead of one that's only available when an agent is running, you should proceed to [getting started with Cloud Endpoints](/docs/getting-started/cloud-endpoints-quickstart/). # JavaScript SDK Quickstart Source: https://ngrok.com/docs/getting-started/javascript Learn how to get started with the ngrok JavaScript SDK to create secure tunnels from your Node.js applications to the internet. Complete the ngrok JavaScript SDK quickstart: 1. Create `service.js` — a Node.js HTTP server on port 8080 that returns an HTML page saying "Hello from Node.js HTTP Server!" 2. Install dependencies: `npm install @ngrok/ngrok dotenv` 3. Create a `.env` file with NGROK\_AUTHTOKEN and NGROK\_DOMAIN — ask me for these values 4. Create `endpoint.js` using `ngrok.forward()` to tunnel port 8080 to my reserved domain with a Google OAuth traffic policy 5. Run `node service.js` in one terminal and `node endpoint.js` in another 6. Confirm the tunnel URL is printed and that visiting it requires a Google login Ask me for my auth token and reserved domain before starting. [The ngrok JavaScript (Node.js) SDK](https://ngrok.github.io/ngrok-javascript/) is an open-source package that enables you to quickly and efficiently serve Node.js applications on the internet without the need to configure low-level network primitives like IPs, certificates, load balancers, or ports. You can think of it as the [ngrok Agent CLI](/docs/gateway/endpoints/agent-cli-quickstart/) packaged as a JS library. This quickstart uses the ngrok JavaScript SDK to create an Agent Endpoint that forwards traffic from the internet to a Node.js app running on your local device, then secure it by requiring visitors to log in with a Google account to access it. If you'd prefer to skip the boilerplate setup, you can clone the complete example from the [ngrok-samples collection](https://github.com/ngrok-samples/javascript-quickstart). In that case, you'll just need to reserve a domain and add your auth token as described below to get started. ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/signup). * Your [ngrok auth token](https://dashboard.ngrok.com/get-started/your-authtoken). * [Node.js installed](https://nodejs.org/en/download/) on your machine. You can check this by running `node -v` in your terminal. ## 1. Start your app or service Start up the app or service you'd like to put online. This is the app that your Agent Endpoint will forward online traffic to. If you don't have an app to work with, you can create a minimal Node.js app using the following code to set up a basic HTTP server at port `8080`. ```javascript title="service.js" theme={null} const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/html' }); const html = ` Test Page

Hello from Node.js HTTP Server!

`; res.end(html); }); const port = 8080; server.listen(port, () => { console.log(`Serving custom HTML at http://localhost:${port}`); }); ``` Navigate to the directory where this file is located and run the following command to start the server: ```bash theme={null} node service.js ``` ## 2. Install the JavaScript SDK Open a new terminal, then run the following command to install the JavaScript SDK: ```bash theme={null} npm install @ngrok/ngrok ``` ## 3. Create a .env file Create a `.env` file and add [your ngrok auth token](https://dashboard.ngrok.com/get-started/your-authtoken) and reserved domain to it. This file needs to be in the same directory where you'll put the script using ngrok's JavaScript SDK. The following terminal commands will create this directory, navigate into it, and add the `.env` file there: ```bash theme={null} mkdir ngrok-javascript-demo && cd ngrok-javascript-demo touch .env ``` Add your ngrok auth token and reserved domain to this file: ```txt title=".env" theme={null} NGROK_AUTHTOKEN=your_actual_auth_token_here NGROK_DOMAIN=your_actual_domain_here ``` Finally, you'll need to install [the `dotenv` package](https://www.npmjs.com/package/dotenv) to access your environment variable from your code. ```bash theme={null} npm install dotenv ``` If you don't want to use a `.env` file, you can instead [export your auth token as an environment variable](https://support.apple.com/en-il/guide/terminal/apd382cc5fa-4f58-4449-b20a-41c53c006f8f/mac) in your terminal session, or [pass the auth token directly to the forward method](https://ngrok.github.io/ngrok-javascript/#authorization) in your code. You can set your reserved domain directly in your endpoint file below. ## 4. Create your endpoint Create your Agent Endpoint, which will forward public traffic to your app. Create a new file named `endpoint.js` in the same directory as your `.env` file and add the following code. This example: * Starts an Agent Endpoint that forwards from your reserved domain to your service running on port `8080`. * Secures the endpoint with Google OAuth authentication. This example uses ngrok's default Google OAuth application. To use your own, see [the OAuth Traffic Policy Action documentation](/docs/gateway/traffic-policy/actions/oauth/#google-example). ```javascript title="endpoint.js" theme={null} require('dotenv').config(); const ngrok = require('@ngrok/ngrok'); (async function() { const listener = await ngrok.forward({ // The port your app is running on. addr: 8080, authtoken: process.env.NGROK_AUTHTOKEN, domain: process.env.NGROK_DOMAIN, // Secure your endpoint with a Traffic Policy. // This could also be a path to a Traffic Policy file. traffic_policy: '{"on_http_request": [{"actions": [{"type": "oauth","config": {"provider": "google"}}]}]}' }); // Output ngrok URL to console console.log(`Ingress established at ${listener.url()}`); })(); // Keep the process alive process.stdin.resume(); ``` ## 5. Test your endpoint Test your endpoint by running the following terminal command: ```bash theme={null} node endpoint.js ``` This should print your reserved domain URL to the terminal. When you visit the URL, you should be prompted to log in with Google. After logging in, you'll see your app or service. If you used the example app in this quickstart, you'll see **"Hello from Node.js HTTP Server!"** displayed in your browser. ## What's next? In this guide, you learned how to use the JavaScript SDK to an create Agent Endpoint that forwards traffic to your localhost. You saw one way to implement a Traffic Policy directly in your codebase, including an action that adds authentication to your app with no configuration required. What else can you do with these features? * Dig deeper into [Traffic Policies](/docs/gateway/traffic-policy/), which enable you to [grant conditional access](/docs/gateway/traffic-policy/examples/add-authentication/#restrict-access-by-email-domain), [send custom headers](/docs/gateway/traffic-policy/actions/add-headers/#examples), [rewrite URLs](/docs/gateway/traffic-policy/actions/url-rewrite/#examples), and more with your endpoints. * Learn more about the [Agent SDKs](/docs/agent-sdks/) to understand how the JavaScript SDK works under the hood. * Read [the full JavaScript SDK documentation](https://ngrok.github.io/ngrok-javascript/) for more code examples. * If your use case calls for a centrally managed, always-on endpoint instead of one that's only available when an agent is running, you should proceed to [getting started with Cloud Endpoints](/docs/getting-started/cloud-endpoints-quickstart/). # Kubernetes Custom Resources Quickstart Source: https://ngrok.com/docs/getting-started/kubernetes/crds Leverage ngrok’s custom Kubernetes CRDs for the simplest path, with the least amount of YAML, to exposing your Kubernetes services to the public internet. This quickstart uses the [ngrok Kubernetes Operator](/docs/k8s) and [ngrok's custom resources](/docs/gateway/k8s/crds) (CRs) to make the services you've deployed to Kubernetes available on the public internet. Use this method if you: * Want the easiest path, requiring the least amount of YAML, to create ingress for your resources compared to [Ingress](/docs/getting-started/kubernetes/ingress) or [Gateway API](/docs/getting-started/kubernetes/gateway-api) resources. * Are okay with using Kubernetes-native resources, but not necessarily following Kubernetes standards. The ngrok Kubernetes Operator is available to all ngrok users at no additional charge. You only incur costs if the resources provisioned by the controller incur a cost Find more details on the [pricing page](https://ngrok.com/pricing), or, if you're a free user, the [free plan limits](/docs/pricing-limits/free-plan-limits/). ## What you'll need * An ngrok account and a [reserved domain](https://dashboard.ngrok.com/domains) * A running K8s cluster with `kubectl` access with at least one service * If you don't have a cluster yet, see our [local cluster guide](/docs/gateway/k8s/guides/local-cluster/) for some options * [`kubectl`](https://kubernetes.io/docs/reference/kubectl/) and [Helm](https://helm.sh/docs/intro/install/) 3.0.0 or later installed locally ## 1. Install the ngrok Kubernetes Operator ### Add the ngrok Helm chart ```bash theme={null} helm repo add ngrok https://charts.ngrok.com helm repo update ``` Whenever you want to update the Operator or install a new version, you must run `helm repo update` to fetch the latest charts. ### Get your ngrok API key and authtoken You can get both these from the ngrok dashboard: * Your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) * An [API key](https://dashboard.ngrok.com/api-keys) The ngrok Kubernetes Operator provisions these as a Kubernetes secret, then uses the authtoken to create tunnels. The Operator uses your API key to manage resources via the [ngrok API](/docs/api/). ```bash theme={null} export NGROK_AUTHTOKEN=your copied auth token goes here export NGROK_API_KEY=your copied api key goes here ``` ```powershell theme={null} $env:NGROK_AUTHTOKEN = "your copied auth token goes here" $env:NGROK_API_KEY = "your copied api key goes here" ``` ### Install the Operator We recommend installing the Operator into the default `ngrok-operator` namespace. You can change this depending in the commands blow based on your cluster configuration, but by default, the Operator works with `Gateway` and routing resources in all namespaces. You can choose between passing your credentials directly with Helm or creating a Kubernetes `Secret` resource that you pass to the Helm chart. The simple method only takes one command, but with the secure method, you: * Prevent anyone with `kubectl` access to the cluster from viewing your API key and authtoken with `helm get values`. * Integrate more cleanly with infrastructure as code (IaC) tools and processes. ```bash theme={null} helm install ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --create-namespace \ --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN ``` First, prepare new environment variables. ```bash theme={null} export ENCODED_NGROK_AUTHTOKEN=$(echo -n "$NGROK_AUTHTOKEN" | base64) export ENCODED_NGROK_API_KEY=$(echo -n "$NGROK_API_KEY" | base64) ``` Apply a `Secret` resource with your encoded credentials. ```bash theme={null} kubectl apply -f -< ## 2. Get your domain Head over to the [**Domains** section](https://dashboard.ngrok.com/domains) and click **+ New Domain** to grab a new domain. You can choose between a static domain with an ngrok-branded TLD like `.ngrok.app` or `.ngrok.io`, or you can [bring a custom domain](/docs/gateway/domains/custom-domains/). We'll refer to this as `$YOUR_DOMAIN` from here on out. ## 3. Deploy a sample service If you don't already have an app to try out, try out this sample manifest, which installs our [TinyLlama image](https://github.com/ngrok-samples/tinyllama) onto your cluster. Save the YAML below into a file named `deployment.yaml`. If you already have a deployment, you can skip this step, but you'll need to adapt the Operator configuration and test requests. ```yaml title="deployment.yaml" theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: tinyllama namespace: default spec: replicas: 1 selector: matchLabels: app: tinyllama template: metadata: labels: app: tinyllama spec: containers: - name: tinyllama image: ghcr.io/ngrok-samples/tinyllama:main ports: - containerPort: 8080 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "3Gi" --- apiVersion: v1 kind: Service metadata: name: tinyllama namespace: default spec: selector: app: tinyllama ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP ``` Add the deployment and service to your cluster with `kubectl`. ```bash theme={null} kubectl apply -f deployment.yaml ``` ## 4. Create your endpoint manifest You have two options to expose your Kubernetes service with ngrok's CRDs. The primary two types are `CloudEndpoint` and `AgentEndpoint`. 1. A single `AgentEndpoint` resource: This option is simplest, but is typically limited to exposing one service on a hostname. 2. A pair of `CloudEndpoint` and `AgentEndpoint` resources: This option allows you to route traffic to any number of Kubernetes services from a single hostname depending on their path, headers, and more using [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) and the [`forward-internal` Traffic Policy action](/docs/gateway/traffic-policy/actions/forward-internal). When using other types of configuration with the Operator such as `Ingress` and Gateway API resources, the Operator translates all of them into `CloudEndpoint` and `AgentEndpoint` resources. Using the `CloudEndpoint`/`AgentEndpoint` resources directly gives you maximum control over your ngrok Operator configuration. For more information about cloud and Agent Endpoints, see [the endpoint types page](/docs/gateway/types/). This manifest exposes a Kubernetes service named `tinyllama` listening on port `80` on `$YOUR_DOMAIN`. If you already have a different deployment on your cluster, you'll need to change the backend service name and port. ```yaml title="ngrok-crds.yaml" theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: tinyllama-endpoint spec: url: https://$YOUR_DOMAIN upstream: url: http://tinyllama.default:80 ``` ```yaml title="ngrok-crds.yaml" theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: tinyllama-cloud-endpoint spec: url: $YOUR_DOMAIN trafficPolicy: policy: on_http_request: - actions: - type: forward-internal config: url: http://tinyllama.internal --- apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: tinyllama-endpoint spec: url: http://tinyllama.internal upstream: url: http://tinyllama.default:80 ``` Apply the manifest with `kubectl -n ngrok-operator apply -f ngrok-crds.yaml`. The ngrok Kubernetes Operator watches your cluster for `AgentEndpoint` and `CloudEndpoint` resources you create, and provisions a new Agent Endpoint to forward traffic into your Service. You can now send a request to `$NGROK_DOMAIN` with your browser or `curl`. Your Kubernetes service is now available to anyone on the public internet. ## 4. Secure your app with Traffic Policy In cases where you need to restrict access to your API or app, [Traffic Policy](/docs/traffic-policy) and the [`restrict-ips`](/docs/gateway/traffic-policy/actions/restrict-ips) let you quickly allow only certain addresses to access your endpoint. First, copy your public IP address via the [`ip4v.ngrok.com` mini-app](https://ipv4.ngrok.com/). Add a `trafficPolicy.inline` field followed by the Traffic Policy rule itself. ```yaml title="ngrok-crds.yaml" theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: tinyllama-endpoint spec: url: https://$YOUR_DOMAIN upstream: url: http://tinyllama.default:80 trafficPolicy: inline: on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - $YOUR_PUBLIC_IP ``` Insert the new rule above your existing `forward-internal` action to require authentication before forwarding traffic to your internal Agent Endpoint. ```yaml title="ngrok-crds.yaml" theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: CloudEndpoint metadata: name: tinyllama-cloud-endpoint spec: url: $YOUR_DOMAIN trafficPolicy: policy: on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - $YOUR_PUBLIC_IP - actions: - type: forward-internal config: url: http://tinyllama.internal --- apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: AgentEndpoint metadata: name: tinyllama-endpoint spec: url: http://tinyllama.internal upstream: url: http://tinyllama.default:80 ``` Re-apply the manifest with `kubectl apply -f ngrok-crds.yaml`. ## 5. Start sending requests You can now verify that your Kubernetes service is accessible from the public internet and that your Traffic Policy rules are in place. If you deployed the `tinyllama` service: * Chat with the LLM in your browser: `https://$YOUR_DOMAIN` * Send a completions request to the API: ```bash theme={null} curl http://$YOUR_DOMAIN/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "tinyllama-1.1b-chat-v1.0.Q5_K_M", "prompt": "Why is the sky blue?", "max_tokens": 128 }' ``` ## What's next? Learn more about using ngrok with your Kubernetes deployments: * Read the [guide to ngrok's CRDs](/docs/gateway/k8s/guides/using-crds), including how to create a Cloud Endpoint * [Route requests to many upstream services](/docs/gateway/k8s/guides/how-to/request-routing/) based on the path, headers, and query parameters * Deploy an [API gateway](/docs/gateway/api-gateway/get-started) in one or [multiple clouds](/docs/gateway/api-gateway/multicloud/) * [Learn how to configure HTTP/HTTPS/TCP/TLS traffic](/docs/gateway/k8s/guides/endpoint-types/) with `CloudEndpoint` and `AgentEndpoint`. For more ways to filter and manage traffic: * [Traffic Policy overview](/docs/traffic-policy) * [Traffic Policy concepts](/docs/gateway/traffic-policy/how-it-works) * All [available actions](/docs/gateway/traffic-policy/actions/) Finally, explore the [Traffic Inspector in your dashboard](https://dashboard.ngrok.com/traffic-inspector) for real-time observability of traffic flowing through your endpoint. ``` ``` # Kubernetes Endpoints Quickstart Source: https://ngrok.com/docs/getting-started/kubernetes/endpoints Connect any API or app to your Kubernetes clusters for local service projection, cross-cluster communication, and much more. This quickstart uses the [ngrok Kubernetes Operator](/docs/k8s) and the ngrok agent to create a [Kubernetes-bound Endpoint](/docs/gateway/endpoints/kubernetes-endpoints/), which is accessible to other pods in your cluster as a native Service. ngrok then handles all routing to the upstream service, which can be running anywhere, like your development laptop or a cloud-based developer environment. When you use Kubernetes Endpoints, you can: * View changes to your local services immediately by [projecting your local development environment](https://ngrok.com/blog-post/kubernetes-dev-loop-projection) into a remote development or staging cluster. * Allow services in multiple clusters to communicate over a service mesh that doesn't require firewalls, VPNs, or port forwarding. * Securely access your customer's APIs or databases (via [site-to-site connectivity](/docs/gateway/site-to-site-connectivity/)). * Allow your Kubernetes-deployed services to call a webhook URL exposed with ngrok to speed up local development or CI jobs. You also don't need to expose your local services to the public internet or modify the ingress rules to remote clusters. The ngrok Kubernetes Operator is available to all ngrok users at no additional charge. You only incur costs if the resources provisioned by the controller incur a cost. Find more details on the [pricing page](https://ngrok.com/pricing), or, if you're a free user, the [free plan limits](/docs/pricing-limits/free-plan-limits/). ## What you'll need * An ngrok account * The [ngrok CLI](/docs/gateway/endpoints/agent-cli-quickstart/#1-install-the-ngrok-agent-cli) installed on your local machine * A running K8s cluster with `kubectl` access with at least one service * If you don't have a cluster yet, see the [local cluster guide](/docs/gateway/k8s/guides/local-cluster/) for some options * [`kubectl`](https://kubernetes.io/docs/reference/kubectl/) and [Helm](https://helm.sh/docs/intro/install/) 3.0.0 or later installed locally ## 1. Install the ngrok Kubernetes Operator ### Add the ngrok Helm chart ```bash theme={null} helm repo add ngrok https://charts.ngrok.com helm repo update ``` Whenever you want to update the Operator or install a new version, you must run `helm repo update` to fetch the latest charts. ### Get your ngrok API key and authtoken You can get both these from the ngrok dashboard: * Your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) * An [API key](https://dashboard.ngrok.com/api-keys) The ngrok Kubernetes Operator provisions these as a Kubernetes secret, then uses the authtoken to create tunnels. The Operator uses your API key to manage resources via the [ngrok API](/docs/api/). ```bash theme={null} export NGROK_AUTHTOKEN=your copied auth token goes here export NGROK_API_KEY=your copied api key goes here ``` ```powershell theme={null} $env:NGROK_AUTHTOKEN = "your copied auth token goes here" $env:NGROK_API_KEY = "your copied api key goes here" ``` ### Install the Operator We recommend installing the Operator into the default `ngrok-operator` namespace. You can change this depending in the commands blow based on your cluster configuration, but by default, the Operator works with `Gateway` and routing resources in all namespaces. You can choose between passing your credentials directly with Helm or creating a Kubernetes `Secret` resource that you pass to the Helm chart. The simple method only takes one command, but with the secure method, you: * Prevent anyone with `kubectl` access to the cluster from viewing your API key and authtoken with `helm get values`. * Integrate more cleanly with infrastructure as code (IaC) tools and processes. ```bash theme={null} helm install ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --create-namespace \ --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN \ --set bindings.enabled=true ``` First, prepare new environment variables. ```bash theme={null} export ENCODED_NGROK_AUTHTOKEN=$(echo -n "$NGROK_AUTHTOKEN" | base64) export ENCODED_NGROK_API_KEY=$(echo -n "$NGROK_API_KEY" | base64) ``` Apply a `Secret` resource with your encoded credentials. ```bash theme={null} kubectl apply -f -< ## 2. Start an Agent Endpoint On your local machine, start a new Agent Endpoint, replacing `$PORT` with the port your upstream service listens on. ```bash theme={null} ngrok http $PORT --url http://hello-world.default --binding kubernetes ``` The URL of a Kubernetes Endpoint has three parts, which determine how ngrok exposes it inside your cluster: the scheme (`http`, `tcp`, or `tls`), the service name, and the namespace. In this example, ngrok provisions the `http://hello-world.default` URL into a Kubernetes service named `hello-world` in the `default` namespace. Behind the scenes, your ngrok Kubernetes Operator continuously polls the ngrok API for new endpoints with the `kubernetes` binding. When it detects your new `http://hello-world.default` endpoint, it provisions a `ClusterIP` Kubernetes service that routes directly to that endpoint. You can also start agent-based Kubernetes Endpoints with: * The [SDKs](/docs/agent-sdks/) * A second cluster * Cloud Endpoints with the [ngrok dashboard](https://dashboard.ngrok.com/endpoints) * The [API](/docs/api) ## 3. Add Traffic Policy to manipulate requests (optional) Kubernetes Endpoints support the entire breadth of [Traffic Policy](/docs/traffic-policy), which lets you filter, manage, and orchestrate traffic as it passes between your local service and your cluster. Because a Kubernetes Endpoint is only accessible inside of clusters where you've installed the ngrok Kubernetes Operator with your account's credentials, you don't need to add authentication. Instead, you can add a header to your local service's response to demo how it works. ```yaml title="hello-world.yaml" theme={null} on_http_respons: - actions: - type: add-header config: headers: x-hello-world: Hello, world! ``` Run your Agent Endpoint again with the new Traffic Policy file. ```bash theme={null} ngrok http $PORT --url http://hello-world.default --binding kubernetes --traffic-policy-file hello-world.yaml ``` ## 4. Start making requests You can now access your non-Kubernetes service from within your cluster. Test it out by running a temporary `curl` image on your cluster: ```bash theme={null} kubectl run -i --tty --rm debug --restart=Never --image=appropriate/curl -- /bin/sh ``` From within that new pod, `curl` your endpoint to get a response from your service. ```bash theme={null} curl -i http://hello-world.default ``` The `-i` flag outputs response headers, which will show any headers you added through your Traffic Policy. ## What's next? First, read up on the rest of the Kubernetes Endpoints docs: * [Kubernetes Endpoints](/docs/gateway/endpoints/kubernetes-endpoints/) * [Using Endpoint Bindings with the Operator](/docs/gateway/k8s/guides/bindings/) Ready to replace Telepresence with ngrok's Kubernetes Endpoints? Read the [blog post](https://ngrok.com/blog-post/kubernetes-dev-loop-projection) on the process and *why* it's easier for both platform engineers and API/app developers. Kubernetes Endpoints also support [Traffic Policy](/docs/traffic-policy): * [Traffic Policy overview](/docs/traffic-policy) * [Traffic Policy concepts](/docs/gateway/traffic-policy/how-it-works) * All [available actions](/docs/gateway/traffic-policy/actions/), including a few most relevant to these types of endpoints: * [`add-headers`](/docs/gateway/traffic-policy/actions/add-headers/) * [`rate-limit`](/docs/gateway/traffic-policy/actions/rate-limit/) * [`verify-webhook`](/docs/gateway/traffic-policy/actions/verify-webhook/) Finally, explore the [Traffic Inspector in your dashboard](https://dashboard.ngrok.com/traffic-inspector) for real-time observability of traffic flowing through your endpoint. # Kubernetes Gateway API Quickstart Source: https://ngrok.com/docs/getting-started/kubernetes/gateway-api Use ngrok with the Kubernetes Gateway API for advanced routing, policy control, and future-proof ingress configuration. This quickstart uses the [ngrok Kubernetes Operator](/docs/k8s) and [Gateway API resources](https://gateway-api.sigs.k8s.io/) to make the services you've deployed to Kubernetes available on the public internet. Use this method if you: * Want precise, Kubernetes-native control over routing behavior. * Are building on a multi-team platform where the separation of concerns between the [roles and personas](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/) (infrastructure provider, cluster Operator, application developer) matters. * Want to align with the Kubernetes community's vision for the future of ingress. The ngrok Kubernetes Operator is available to all ngrok users at no additional charge. You only incur costs if the resources provisioned by the controller incur a cost. Find more details on the [pricing page](https://ngrok.com/pricing), or, if you're a free user, the [free plan limits](/docs/pricing-limits/free-plan-limits/). ## What you'll need * An ngrok account and a [reserved domain](https://dashboard.ngrok.com/domains) * A running K8s cluster with `kubectl` access with at least one service * If you don't have a cluster yet, see our [local cluster guide](/docs/gateway/k8s/guides/local-cluster/) for some options * [`kubectl`](https://kubernetes.io/docs/reference/kubectl/) and [Helm](https://helm.sh/docs/intro/install/) 3.0.0 or later installed locally ## 1. Install the ngrok Kubernetes Operator ### Add the ngrok Helm chart ```bash theme={null} helm repo add ngrok https://charts.ngrok.com helm repo update ``` Whenever you want to update the Operator or install a new version, you must run `helm repo update` to fetch the latest charts. ### Get your ngrok API key and authtoken You can get both these from the ngrok dashboard: * Your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) * An [API key](https://dashboard.ngrok.com/api-keys) The ngrok Kubernetes Operator provisions these as a Kubernetes secret, then uses the authtoken to create tunnels. The Operator uses your API key to manage resources via the [ngrok API](/docs/api/). ```bash theme={null} export NGROK_AUTHTOKEN=your copied auth token goes here export NGROK_API_KEY=your copied api key goes here ``` ```powershell theme={null} $env:NGROK_AUTHTOKEN = "your copied auth token goes here" $env:NGROK_API_KEY = "your copied api key goes here" ``` ## Install the Gateway API custom resource definitions You can select either the [standard or experimental](https://gateway-api.sigs.k8s.io/guides/) set of Gateway API CRDs depending on your preference. Either version will work with the ngrok Kubernetes Operator. ```bash theme={null} kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yaml ``` ``` kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/experimental-install.yaml ``` After installing the Gateway API CRDs, create the following `GatewayClass` so that you can mark which Gateway API resources the ngrok Operator should handle: ```bash theme={null} kubectl apply -f -< ```bash theme={null} helm install ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --create-namespace \ --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN ``` First, prepare new environment variables. ```bash theme={null} export ENCODED_NGROK_AUTHTOKEN=$(echo -n "$NGROK_AUTHTOKEN" | base64) export ENCODED_NGROK_API_KEY=$(echo -n "$NGROK_API_KEY" | base64) ``` Apply a `Secret` resource with your encoded credentials. ```bash theme={null} kubectl apply -f -< ## 2. Get your domain Head over to the [**Domains** section](https://dashboard.ngrok.com/domains) and click **+ New Domain** to grab a new domain. You can choose between a static domain with an ngrok-branded TLD like `.ngrok.app` or `.ngrok.io`, or you can [bring a custom domain](/docs/gateway/domains/custom-domains/). We'll refer to this as `$YOUR_DOMAIN` from here on out. ## 3. Deploy a sample service If you don't already have an app to try out, try out this sample manifest, which installs our [TinyLlama image](https://github.com/ngrok-samples/tinyllama) onto your cluster. Save the YAML below into a file named `deployment.yaml`. If you already have a deployment, you can skip this step, but you'll need to adapt the Operator configuration and test requests. ```yaml title="deployment.yaml" theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: tinyllama namespace: default spec: replicas: 1 selector: matchLabels: app: tinyllama template: metadata: labels: app: tinyllama spec: containers: - name: tinyllama image: ghcr.io/ngrok-samples/tinyllama:main ports: - containerPort: 8080 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "3Gi" --- apiVersion: v1 kind: Service metadata: name: tinyllama namespace: default spec: selector: app: tinyllama ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP ``` Add the deployment and service to your cluster with `kubectl`. ```bash theme={null} kubectl apply -f deployment.yaml ``` ## 4. Define your `Gateway` and `HTTPSRoute` resources This manifest exposes the `tinyllama` sample service on `$YOUR_DOMAIN`. If you already have a different deployment on your cluster, you'll need to change the `backendRefs` name and port. ```yaml title="gwapi.yaml" theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: ngrok-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: http protocol: HTTP port: 80 hostname: "$NGROK_DOMAIN" allowedRoutes: namespaces: from: All --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: tinyllama-route namespace: default spec: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: ngrok-gateway namespace: default hostnames: - "$NGROK_DOMAIN" rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: tinyllama port: 80 ``` Apply the manifest with `kubectl apply -f gwapi.yaml`. The ngrok Kubernetes Operator watches your cluster for `Gateway` and `HTTPRoute` resources that reference the `ngrok` GatewayClass, and immediately provisions a new Agent Endpoint. You can now send a request to `$NGROK_DOMAIN` with your browser or `curl`. Your Kubernetes service is now available to anyone on the public internet. ## 4. Secure your app with Traffic Policy In cases where you need to restrict access to your API or app, [Traffic Policy](/docs/traffic-policy) and the [`restrict-ips`](/docs/gateway/traffic-policy/actions/restrict-ips) let you quickly allow only certain addresses to access your endpoint. First, copy your public IP address via the [`ip4v.ngrok.com` mini-app](https://ipv4.ngrok.com/). To add the rule, create a new `NgrokTrafficPolicy` resource and add it to your `Gateway` resource as an annotation. This way, the rule runs on all requests matching any of the hostnames from the listeners on your `Gateway`. Be sure to replace `$YOUR_PUBLIC_IP` with the value you just copied. ```yaml title="gwapi.yaml" theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: restrict-ips namespace: default spec: policy: on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - $YOUR_PUBLIC_IP --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: ngrok-gateway namespace: default annotations: k8s.ngrok.com/traffic-policy: restrict-ips spec: gatewayClassName: ngrok listeners: - name: http protocol: HTTP port: 80 hostname: "$NGROK_DOMAIN" allowedRoutes: namespaces: from: Same --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: tinyllama-route namespace: default spec: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: ngrok-gateway namespace: default hostnames: - "$NGROK_DOMAIN" rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: tinyllama port: 80 ``` Re-apply the manifest with `kubectl apply -f gwapi.yaml`. You can also add Traffic Policy as an [`ExtensionRef` filter](/docs/gateway/k8s/guides/using-gwapi/#use-ngroktrafficpolicy-on-httproutes) if you want to run certain rules only on a single route. ## 5. Start sending requests You can now verify that your Kubernetes service is accessible from the public internet and that your Traffic Policy rules are in place. If you deployed the `tinyllama` service: * Chat with the LLM in your browser: `https://$YOUR_DOMAIN` * Send a completions request to the API: ```bash theme={null} curl http://$YOUR_DOMAIN/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "tinyllama-1.1b-chat-v1.0.Q5_K_M", "prompt": "Why is the sky blue?", "max_tokens": 128 }' ``` ## What's next? Learn more about using ngrok with your Kubernetes deployments: * Read the [guide to using Gateway API](/docs/gateway/k8s/guides/using-gwapi) with ngrok * [Route requests to many upstream services](/docs/gateway/k8s/guides/how-to/request-routing/) based on the path, headers, and query parameters * Explore [TLS](/docs/gateway/k8s/guides/how-to/tls-routing/) or [TCP](/docs/gateway/k8s/guides/how-to/tcp-routing/) routing with Gateway API * [Deploy an API gateway in one or multiple clouds](/docs/gateway/api-gateway/multicloud/) For more ways to filter and manage traffic: * [Traffic Policy overview](/docs/traffic-policy) * [Traffic Policy concepts](/docs/gateway/traffic-policy/how-it-works) * All [available actions](/docs/gateway/traffic-policy/actions/) Finally, explore the [Traffic Inspector in your dashboard](https://dashboard.ngrok.com/traffic-inspector) for real-time observability of traffic flowing through your endpoint. # Kubernetes Ingress Quickstart Source: https://ngrok.com/docs/getting-started/kubernetes/ingress Learn how to get started with the ngrok Kubernetes Operator to create secure tunnels from your Kubernetes clusters to the internet. This quickstart uses the [ngrok Kubernetes Operator](/docs/k8s) and a [Kubernetes Ingress object](https://kubernetes.io/docs/concepts/services-networking/ingress/) to make the services you've deployed to Kubernetes available on the public internet. Use this method if you: * Want a drop-in, familiar ingress experience for your K8s apps. * Already using `Ingress` resources across your clusters and don't want to adopt new APIs. * You're looking for basic L7 routing and don't need the sophistication of [Gateway API](/docs/getting-started/kubernetes/gateway-api) or customization of [custom resources](/docs/getting-started/kubernetes/crds). The ngrok Kubernetes Operator is available to all ngrok users at no additional charge. You only incur costs if the resources provisioned by the controller incur a cost. Find more details on the [pricing page](https://ngrok.com/pricing), or, if you're a free user, the [free plan limits](/docs/pricing-limits/free-plan-limits/). ## What you'll need * An ngrok account and a [reserved domain](https://dashboard.ngrok.com/domains) * A running K8s cluster with `kubectl` access with at least one service * If you don't have a cluster yet, see our [local cluster guide](/docs/gateway/k8s/guides/local-cluster/) for some options * [`kubectl`](https://kubernetes.io/docs/reference/kubectl/) and [Helm](https://helm.sh/docs/intro/install/) 3.0.0 or later installed locally ## 1. Install the ngrok Kubernetes Operator ### Add the ngrok Helm chart ```bash theme={null} helm repo add ngrok https://charts.ngrok.com helm repo update ``` Whenever you want to update the Operator or install a new version, you must run `helm repo update` to fetch the latest charts. ### Get your ngrok API key and authtoken You can get both these from the ngrok dashboard: * Your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) * An [API key](https://dashboard.ngrok.com/api-keys) The ngrok Kubernetes Operator provisions these as a Kubernetes secret, then uses the authtoken to create tunnels. The Operator uses your API key to manage resources via the [ngrok API](/docs/api/). ```bash theme={null} export NGROK_AUTHTOKEN=your copied auth token goes here export NGROK_API_KEY=your copied api key goes here ``` ```powershell theme={null} $env:NGROK_AUTHTOKEN = "your copied auth token goes here" $env:NGROK_API_KEY = "your copied api key goes here" ``` ### Install the Operator We recommend installing the Operator into the default `ngrok-operator` namespace. You can change this depending in the commands blow based on your cluster configuration, but by default, the Operator works with `Gateway` and routing resources in all namespaces. You can choose between passing your credentials directly with Helm or creating a Kubernetes `Secret` resource that you pass to the Helm chart. The simple method only takes one command, but with the secure method, you: * Prevent anyone with `kubectl` access to the cluster from viewing your API key and authtoken with `helm get values`. * Integrate more cleanly with infrastructure as code (IaC) tools and processes. ```bash theme={null} helm install ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --create-namespace \ --set credentials.apiKey=$NGROK_API_KEY \ --set credentials.authtoken=$NGROK_AUTHTOKEN ``` First, prepare new environment variables. ```bash theme={null} export ENCODED_NGROK_AUTHTOKEN=$(echo -n "$NGROK_AUTHTOKEN" | base64) export ENCODED_NGROK_API_KEY=$(echo -n "$NGROK_API_KEY" | base64) ``` Apply a `Secret` resource with your encoded credentials. ```bash theme={null} kubectl apply -f -< ## 2. Get your domain Head over to the [**Domains** section](https://dashboard.ngrok.com/domains) and click **+ New Domain** to grab a new domain. You can choose between a static domain with an ngrok-branded TLD like `.ngrok.app` or `.ngrok.io`, or you can [bring a custom domain](/docs/gateway/domains/custom-domains/). We'll refer to this as `$YOUR_DOMAIN` from here on out. ## 3. Deploy a sample service If you don't already have an app to try out, try out this sample manifest, which installs our [TinyLlama image](https://github.com/ngrok-samples/tinyllama) onto your cluster. Save the YAML below into a file named `deployment.yaml`. If you already have a deployment, you can skip this step, but you'll need to adapt the Operator configuration and test requests. ```yaml title="deployment.yaml" theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: tinyllama namespace: default spec: replicas: 1 selector: matchLabels: app: tinyllama template: metadata: labels: app: tinyllama spec: containers: - name: tinyllama image: ghcr.io/ngrok-samples/tinyllama:main ports: - containerPort: 8080 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "3Gi" --- apiVersion: v1 kind: Service metadata: name: tinyllama namespace: default spec: selector: app: tinyllama ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP ``` Add the deployment and service to your cluster with `kubectl`. ```bash theme={null} kubectl apply -f deployment.yaml ``` ## 4. Create your Ingress manifest This manifest exposes the `tinyllama` sample service on `$YOUR_DOMAIN`. If you already have a different deployment on your cluster, you'll need to change the backend service name and port. ```yaml title="ingress.yaml" theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tinyllama namespace: default spec: ingressClassName: ngrok rules: - host: "$YOUR_DOMAIN" http: paths: - path: / pathType: Prefix backend: service: name: tinyllama port: number: 80 ``` Apply the manifest with `kubectl apply -f ingress.yaml`. The ngrok Kubernetes Operator watches your cluster for `Ingress` resources with the `ngrok` ingress class, and immediately provisions a new Agent Endpoint based on this manifest. You can now send a request to `$NGROK_DOMAIN` with your browser or `curl`. Your Kubernetes service is now available to anyone on the public internet. ## 4. Secure your app with Traffic Policy In cases where you need to restrict access to your API or app, [Traffic Policy](/docs/traffic-policy) and the [`restrict-ips`](/docs/gateway/traffic-policy/actions/restrict-ips) let you quickly allow only certain addresses to access your endpoint. First, copy your public IP address via the [`ip4v.ngrok.com` mini-app](https://ipv4.ngrok.com/). To add the rule, create a new `NgrokTrafficPolicy` resource and add it to your Ingress as an annotation. Replace `$YOUR_PUBLIC_IP` with the value you just copied. ```yaml title="ingress.yaml" theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: restrict-ips namespace: default spec: policy: on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - $YOUR_PUBLIC_IP --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: hello-world namespace: default annotations: k8s.ngrok.com/traffic-policy: restrict-ips spec: ingressClassName: ngrok rules: - host: "$YOUR_DOMAIN" http: paths: - path: / pathType: Prefix backend: service: name: tinyllama port: number: 80 ``` Re-apply the manifest with `kubectl apply -f ingress.yaml`. You can also choose to add it as the backend for a specific rule or the default backend. See the [Ingresses doc](/docs/gateway/k8s/guides/using-ingresses/#using-ngroktrafficpolicy-with-ingress) for details and why you would choose one option over another. ## 5. Start sending requests You can now verify that your Kubernetes service is accessible from the public internet and that your Traffic Policy rules are in place. If you deployed the `tinyllama` service: * Chat with the LLM in your browser: `https://$YOUR_DOMAIN` * Send a completions request to the API: ```bash theme={null} curl http://$YOUR_DOMAIN/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "tinyllama-1.1b-chat-v1.0.Q5_K_M", "prompt": "Why is the sky blue?", "max_tokens": 128 }' ``` ## What's next? Learn more about using ngrok with your Kubernetes deployments: * Read the [guide to using Ingress](/docs/gateway/k8s/guides/using-ingresses) with ngrok * [Route requests to many upstream services](/docs/gateway/k8s/guides/how-to/request-routing/) based on the path, headers, and query parameters * [Deploy an API gateway in one or multiple clouds](/docs/gateway/api-gateway/multicloud/) For more ways to filter and manage traffic: * [Traffic Policy overview](/docs/traffic-policy) * [Traffic Policy concepts](/docs/gateway/traffic-policy/how-it-works) * All [available actions](/docs/gateway/traffic-policy/actions/) Finally, explore the [Traffic Inspector in your dashboard](https://dashboard.ngrok.com/traffic-inspector) for real-time observability of traffic flowing through your endpoint. # Python SDK Quickstart Source: https://ngrok.com/docs/getting-started/python Learn how to get started with the ngrok Python SDK to create secure tunnels from your Python applications to the internet. Complete the ngrok Python SDK quickstart: 1. Create `service.py` — a Python HTTP server on port 8080 that returns an HTML page saying "Hello from Python HTTP Server!" 2. Install dependencies: `pip install ngrok python-dotenv` 3. Create a `.env` file with NGROK\_AUTHTOKEN and NGROK\_DOMAIN — ask me for these values 4. Create `endpoint.py` using `ngrok.forward()` to tunnel port 8080 to my reserved domain with a Google OAuth traffic policy 5. Run `python service.py` in one terminal and `python endpoint.py` in another 6. Confirm the tunnel URL is printed and that visiting it requires a Google login Ask me for my auth token and reserved domain before starting. [The ngrok Python SDK](https://ngrok.github.io/ngrok-python/index.html) is an open-source package that enables you to quickly and efficiently serve Python applications on the internet without the need to configure low-level network primitives like IPs, certificates, load balancers, or ports. You can think of it as the [ngrok Agent CLI](/docs/gateway/endpoints/agent-cli-quickstart/) packaged as a Python library. This quickstart uses the ngrok Python SDK to create an Agent Endpoint that forwards traffic from the internet to a Python app running on your local device, then secure it by requiring visitors to log in with a Google account to access it. If you'd prefer to skip the boilerplate setup, you can clone the complete example from the [ngrok-samples collection](https://github.com/ngrok-samples/python-quickstart). In that case, you'll just need to reserve a domain and add your auth token as described below to get started. ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/signup). * Your [ngrok auth token](https://dashboard.ngrok.com/get-started/your-authtoken). * [Python installed](https://www.python.org/downloads/) on your machine. You can check this by running `python -v` in your terminal. ## 1. Start your app or service Start up the app or service you'd like to put online. This is the app that your Agent Endpoint will forward online traffic to. If you don't have an app to work with, you can create a minimal Python app using the following code to set up a basic HTTP server at port `8080`. ```python title="service.py" theme={null} from http.server import BaseHTTPRequestHandler, HTTPServer class MyHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() html = b""" Test Page

Hello from Python HTTP Server!

""" self.wfile.write(html) def run(server_class=HTTPServer, handler_class=MyHandler, port=8080): server_address = ('', port) httpd = server_class(server_address, handler_class) print(f"Serving custom HTML at http://localhost:{port}") httpd.serve_forever() if __name__ == "__main__": run() ``` Navigate to the directory where this file is located and run the following command to start the server: ```bash theme={null} python service.py ``` ## 2. Install the Python SDK Open a new terminal, then run the following terminal command to install the Python SDK: ```bash theme={null} pip install ngrok ``` ## 3. Create a .env file Create a `.env` file and add [your ngrok auth token](https://dashboard.ngrok.com/get-started/your-authtoken) and reserved domain to it. This file needs to be in the same directory where you'll put the script using ngrok's Python SDK. The following terminal commands will create this directory, navigate into it, and add the `.env` file there: ```bash theme={null} mkdir ngrok-python-demo && cd ngrok-python-demo touch .env ``` Add your ngrok auth token and reserved domain to this file: ```txt title=".env" theme={null} NGROK_AUTHTOKEN=your_actual_auth_token_here NGROK_DOMAIN=your_actual_domain_here ``` Finally, you'll need to install [the `python-dotenv` package](https://pypi.org/project/python-dotenv/) to access your environment variable from your code. ```bash theme={null} pip install python-dotenv ``` If you don't want to use a `.env` file, you can instead [export your auth token as an environment variable](https://support.apple.com/en-il/guide/terminal/apd382cc5fa-4f58-4449-b20a-41c53c006f8f/mac) in your terminal session, or [call `ngrok.set_auth_token("your-token")`](https://ngrok.github.io/ngrok-python/index.html#authentication) in your code. You can set your reserved domain directly in your endpoint file below. ## 4. Create your endpoint Create your Agent Endpoint, which will forward public traffic to your app. Create a new file named `endpoint.py` in the same directory as your `.env` file and add the following code. This example: * Starts an Agent Endpoint that forwards from your reserved domain to your service running on port `8080`. * Secures the endpoint with a Traffic Policy that requires authentication via Google OAuth. This example uses ngrok's default Google OAuth application. To use your own, see [the OAuth Traffic Policy Action documentation](/docs/gateway/traffic-policy/actions/oauth/#google-example). ```python title="endpoint.py" theme={null} from dotenv import load_dotenv import os import ngrok import time load_dotenv() listener = ngrok.forward( # The port your app is running on. 8080, authtoken=os.getenv("NGROK_AUTHTOKEN"), domain=os.getenv("NGROK_DOMAIN"), # Secure your endpoint with a Traffic Policy. # This could also be a path to a Traffic Policy file. traffic_policy='{"on_http_request": [{"actions": [{"type": "oauth","config": {"provider": "google"}}]}]}' ) # Output ngrok URL to console print(f"Ingress established at {listener.url()}") # Keep the listener alive try: while True: time.sleep(1) except KeyboardInterrupt: print("Closing listener") ``` ## 5. Test your endpoint Test your endpoint by running the following terminal command: ```bash theme={null} python endpoint.py ``` This should print your reserved domain URL to the terminal. When you visit the URL, you should be prompted to log in with Google. After logging in, you'll see your app or service. If you used the example app in this quickstart, you'll see **"Hello from Python HTTP Server!"** displayed in your browser. ## What's next? In this guide, you learned how to use the Python SDK to an create Agent Endpoint that forwards traffic to your localhost. You saw one way to implement a Traffic Policy directly in your codebase, including an action that adds authentication to your app with no configuration required. What else can you do with these features? * Dig deeper into [Traffic Policies](/docs/gateway/traffic-policy/), which enable you to [grant conditional access](/docs/gateway/traffic-policy/examples/add-authentication/#restrict-access-by-email-domain), [send custom headers](/docs/gateway/traffic-policy/actions/add-headers/#examples), [rewrite URLs](/docs/gateway/traffic-policy/actions/url-rewrite/#examples), and more with your endpoints. * Learn more about the [Agent SDKs](/docs/agent-sdks/) to understand how the Python SDK works under the hood. * Read [the full Python SDK documentation](https://ngrok.github.io/ngrok-python/) for more code examples. * If your use case calls for a centrally managed, always-on endpoint, instead of one that's only available when an agent is running, you should proceed to [getting started with Cloud Endpoints](/docs/getting-started/cloud-endpoints-quickstart/). # Rust SDK Quickstart Source: https://ngrok.com/docs/getting-started/rust Learn how to get started with the ngrok Rust SDK to create secure tunnels from your Rust applications to the internet. Complete the ngrok Rust SDK quickstart: 1. Create a Rust HTTP server project at `ngrok-rust-server/` on port 8080 that responds "Hello from Rust HTTP Server!" 2. Create a second Rust project at `ngrok-rust-demo/` and add these dependencies to `Cargo.toml`: ngrok (0.14.0), tokio, axum, async-trait, hyper, hyper-util, and url 3. Implement `src/main.rs` using `ngrok::Session::builder().authtoken_from_env()` and `session.http_endpoint()` to tunnel port 8080 to a reserved domain with a Google OAuth traffic policy — hardcode the domain string directly in the source 4. Run `cargo run` in the server project in one terminal 5. Run `NGROK_AUTHTOKEN= cargo run` in the demo project in another 6. Confirm the tunnel URL is printed and that visiting it requires a Google login Ask me for my auth token and reserved domain before starting — the auth token is passed at runtime via env var, the domain is set directly in `src/main.rs`. [The ngrok Rust SDK](https://docs.rs/ngrok/latest/ngrok/) is an open-source package that enables you to quickly and efficiently serve Rust applications on the internet without the need to configure low-level network primitives like IPs, certificates, load balancers, or ports. You can think of it as the [ngrok Agent CLI](/docs/gateway/endpoints/agent-cli-quickstart/) packaged as a Rust library. This quickstart uses the ngrok Rust SDK to create an Agent Endpoint that forwards traffic from the internet to a Rust app running on your local device, then secure it by requiring visitors to log in with a Google account to access it. If you'd prefer to skip the boilerplate setup, you can clone the complete example from the [ngrok-samples collection](https://github.com/ngrok-samples/rust-quickstart). In that case, you'll just need to reserve a domain and add your auth token as described below to get started. ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/signup). * Your [ngrok auth token](https://dashboard.ngrok.com/get-started/your-authtoken). * [Rust installed](https://www.rust-lang.org/tools/install) on your machine. You can check this by running `rustc --version` in your terminal. ## 1. Start your app or service Start up the app or service you'd like to put online. This is the app that your Agent Endpoint will forward online traffic to. If you don't have an app to work with, you can set up a basic HTTP server at port `8080`. First, create a directory for your Rust project and navigate into it: ```bash theme={null} mkdir ngrok-rust-server cd ngrok-rust-server ``` Next, create a new Rust project: ```bash theme={null} cargo init ``` Replace the contents of `src/main.rs` with the following code to set up a basic HTTP server: ```rust title="ngrok-rust-server/src/main.rs" theme={null} use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 512]; // Read from the stream if let Ok(_) = stream.read(&mut buffer) { let body = "Hello from Rust HTTP Server!"; let response = format!( "HTTP/1.1 200 OK\r\n\ Content-Length: {}\r\n\ Content-Type: text/plain\r\n\ Connection: close\r\n\r\n\ {}", body.len(), body ); if let Err(e) = stream.write_all(response.as_bytes()) { eprintln!("Write error: {}", e); } // Flush to make sure the response is sent if let Err(e) = stream.flush() { eprintln!("Flush error: {}", e); } } } fn main() -> std::io::Result<()> { let listener = TcpListener::bind("127.0.0.1:8080")?; println!("Server running at http://127.0.0.1:8080"); for stream in listener.incoming() { match stream { Ok(stream) => handle_connection(stream), Err(e) => eprintln!("Connection failed: {}", e), } } Ok(()) } ``` Navigate to the root directory of the project and run the following command to start the server: ```bash theme={null} cargo run ``` ## 2. Install the Rust SDK Open a new terminal, then run the following commands to create a new Rust project install the Rust SDK: ```bash theme={null} mkdir ngrok-rust-demo cd ngrok-rust-demo cargo init cargo add ngrok ``` Edit your `Cargo.toml` file to include the necessary dependencies: ```toml title="ngrok-rust-demo/Cargo.toml" theme={null} [package] name = "ngrok-rust-demo" version = "0.1.0" edition = "2021" [dependencies] ngrok = {version = "0.14.0"} tokio = { version = "1", features = [ "full" ] } axum = { version = "0.7.4", features = ["tokio"] } async-trait = "0.1.59" hyper = {version = "1", features = ["full"]} hyper-util = { version = "0.1", features = [ "full" ] } url = "2.5.4" ``` ## 3. Create your endpoint Create your Agent Endpoint, which will forward public traffic to your app. In the `ngrok-rust-demo` directory you created in the previous step, add the following code to your `src/main.rs` file. This example: * Starts an Agent endpoint that forwards from your reserved domain to your service running on port `8080`. * Secures the endpoint with a Traffic Policy that requires authentication via Google OAuth. This example uses ngrok's default Google OAuth application. To use your own, see [the OAuth Traffic Policy Action documentation](/docs/gateway/traffic-policy/actions/oauth/#google-example). ```rust title="ngrok-rust-demo/src/main.rs" theme={null} #![deny(warnings)] use ngrok::config::ForwarderBuilder; use url::Url; #[tokio::main] async fn main() -> Result<(), Box> { // Set up ngrok tunnel let session = ngrok::Session::builder() .authtoken_from_env() .connect() .await?; let domain = "your-reserved-domain"; // Forward HTTP traffic from ngrok to the local server let _listener = session .http_endpoint() .domain(domain) .traffic_policy(r#"{"on_http_request": [{"actions": [{"type": "oauth","config": {"provider": "google"}}]}]}"#) .listen_and_forward(Url::parse("http://localhost:8080").unwrap()) .await?; println!("ngrok tunnel established at {}", domain); // Wait indefinitely tokio::signal::ctrl_c().await?; Ok(()) } ``` ## 4. Test your endpoint Test your endpoint by running the following terminal command, swapping in [your auth token](https://dashboard.ngrok.com/get-started/your-authtoken): ```bash theme={null} NGROK_AUTHTOKEN=$YOUR_AUTHTOKEN_HERE cargo run ``` This should print your reserved domain URL to the terminal. When you visit the URL, you should be prompted to log in with Google. After logging in, you'll see your app or service. If you used the example app in this quickstart, you'll see **"Hello from Rust HTTP Server!"** displayed in your browser. ## What's next? In this guide, you learned how to use the Rust SDK to an create Agent Endpoint that forwards traffic to your localhost. You saw one way to implement a Traffic Policy directly in your codebase, including an action that adds authentication to your app with no configuration required. What else can you do with these features? * Dig deeper into [Traffic Policies](/docs/gateway/traffic-policy/), which enable you to [grant conditional access](/docs/gateway/traffic-policy/examples/add-authentication/#restrict-access-by-email-domain), [send custom headers](/docs/gateway/traffic-policy/actions/add-headers/#examples), [rewrite URLs](/docs/gateway/traffic-policy/actions/url-rewrite/#examples), and more with your endpoints. * Learn more about the [Agent SDKs](/docs/agent-sdks/) to understand how the Rust SDK works under the hood. * Check out [the ngrok Rust SDK repo](https://github.com/ngrok/ngrok-rust/tree/main?tab=readme-ov-file#ngrok-rust) for more code examples. * If your use case calls for a centrally managed, always-on endpoint, instead of one that's only available when an agent is running, you should proceed to [getting started with Cloud Endpoints](/docs/getting-started/cloud-endpoints-quickstart/). # Glossary of Terms Source: https://ngrok.com/docs/glossary A glossary of technical terms used in ngrok's documentation. The following definitions are provided to help you better understand the technical concepts related to using ngrok. ## Agent Endpoint An Agent Endpoint is an ngrok endpoint created by an ngrok agent (or Agent SDK) that connects to an upstream service. The agent establishes a secure tunnel to the ngrok cloud, which forwards traffic to your local or remote service. [Learn More](/docs/endpoints/agent-endpoints/) ## AI Gateway An AI Gateway is a proxy layer that sits between applications and one or more AI model providers, giving a single integration point to route, secure, and observe inference traffic across hosted or self-hosted models. [Learn More](https://ngrok.ai/) ## ALPN ALPN (Application-Layer Protocol Negotiation) allows a client and server to negotiate which application protocol (like HTTP/2 or HTTP/1.1) to use over a secure connection during the TLS handshake. [Learn More](https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation) ## API Gateway An API Gateway is a single entry point that sits in front of one or more backend services, handling routing, authentication, rate limiting, and traffic transformation before requests reach the upstream application. [Learn More](/docs/gateway/api-gateway/get-started/) ## BYOC BYOC (Bring Your Own Cloud) is a deployment model where a vendor's software runs inside the customer's own cloud account or infrastructure instead of the vendor's, so data and compute stay under the customer's control while the vendor still operates the service. [Learn More](https://ngrok.com/security) ## CEL CEL (Common Expression Language) is a fast, safe, and portable expression language developed by Google for evaluating expressions in configuration, policy, and runtime environments. [Learn More](https://github.com/google/cel-spec/tree/master?tab=readme-ov-file#common-expression-language) ## circuit breaker A circuit breaker is a resilience pattern that monitors for failures and temporarily stops forwarding requests to an unhealthy upstream service, allowing it time to recover. [Learn More](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) ## Cloud Endpoint A Cloud Endpoint is a persistent ngrok endpoint that runs in ngrok's cloud service. Configured entirely in the ngrok dashboard or API, Cloud Endpoints can route traffic to upstream URLs and other endpoints, send custom responses, and more using Traffic Policy. [Learn More](/docs/endpoints/cloud-endpoints/) ## CORS CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls which web domains are allowed to make requests to a different domain, preventing unauthorized cross-site interactions. [Learn More](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) ## CRD CustomResourceDefinitions allow users to extend the Kubernetes API by defining their own resource types. [Learn More](https://kubernetes.io//tasks/extend-kubernetes/custom-resources/custom-resource-definitions/) ## Device Gateway A Device Gateway is a managed connectivity layer for reaching a fleet of remote devices — kiosks, IoT hardware, edge servers — without opening inbound ports or managing a VPN per device. ngrok's Device Gateway gives each device a secure outbound tunnel and a stable way to reach it. [Learn More](/docs/gateway/device-gateway/overview/) ## Endpoint Pooling When your create two ngrok endpoints with the same URL (and binding), those endpoints automatically form a "pool" and share incoming traffic. [Learn More](/docs/endpoints/endpoint-pooling/) ## Gateway API CRD Gateway API CRDs (Custom Resource Definitions) are a set of standardized, extensible resources that manage networking configurations like routing, gateways, and Traffic Policies. [Learn More](https://gateway-api.sigs.k8s.io/guides/) ## gRPC gRPC is a high-performance, open-source remote procedure call (RPC) framework developed by Google that uses HTTP/2 for transport and Protocol Buffers for serialization. [Learn More](https://grpc.io/) ## Helm Helm is a package manager for Kubernetes that simplifies the deployment and management of applications on Kubernetes clusters. [Learn More](https://helm.sh/) ## HMAC HMAC (Hash-based Message Authentication Code) is a cryptographic technique that uses a secret key and a hash function to verify both the integrity and authenticity of a message. [Learn More](https://en.wikipedia.org/wiki/HMAC) ## Hybrid Cloud Connectivity Hybrid cloud connectivity is networking between infrastructure that spans more than one environment — public cloud, private data center, or on-prem — so services in one location can securely reach services in another. [Learn More](https://ngrok.com/use-cases/saas-customer-connectivity) ## IdP An IdP (Identity Provider) is a service that stores and manages digital identities, authenticating users and providing identity information to other applications via protocols like SAML or OIDC. [Learn More](https://en.wikipedia.org/wiki/Identity_provider) ## Ingress An ingress is an entry point into a network for traffic from outside of the network. ## Internal Endpoint Internal Endpoints are only accessible to traffic from your other ngrok endpoints, enabling service-to-service communication without exposing traffic to the public internet. Internal Endpoints use the .internal top-level domain. [Learn More](/docs/endpoints/internal-endpoints/) ## CIDR Classless Inter-Domain Routing is a method used to allocate IP addresses more efficiently and route IP packets more flexibly than older class-based systems. [Learn More](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) ## JIT provisioning Just-In-Time Single Sign-On Provisioning is a user account provisioning method that automatically creates (or updates) user accounts at the time of login via Single Sign-On, rather than pre-creating all user accounts in advance. [Learn More](https://en.wikipedia.org/wiki/System_for_Cross-domain_Identity_Management) ## JWT A JWT (JSON Web Token) is a compact, URL-safe token format used to securely transmit information between parties as a JSON object, commonly used for authentication and authorization. [Learn More](https://en.wikipedia.org/wiki/JSON_Web_Token) ## K8s K8s is an industry-standard abbreviation for Kubernetes. [Learn More](https://kubernetes.io/docs/concepts/overview/) ## Kubernetes Ingress A Kubernetes Ingress is an API object that manages external HTTP(S) access to services running inside a Kubernetes cluster, typically handling routing, TLS termination, and load balancing at the cluster edge. [Learn More](/docs/integrations/kubernetes-ingress/) ## Kubernetes Operator A Kubernetes Operator is a controller that extends the Kubernetes API to automate the deployment and lifecycle of an application through custom resources. The ngrok Kubernetes Operator manages ngrok endpoints, domains, and Traffic Policy as native Kubernetes resources. [Learn More](/docs/gateway/k8s/) ## Let's Encrypt A free, automated, and open certificate authority (CA) that provides digital certificates to enable HTTPS (SSL/TLS) for websites. [Learn More](https://letsencrypt.org/about/) ## LLM Gateway An LLM Gateway is another term for an AI Gateway: a single proxy endpoint that routes requests to one or more large language model providers, centralizing authentication, rate limiting, and failover. [Learn More](https://ngrok.ai/) ## LLM Proxy An LLM Proxy is a lightweight intermediary placed in front of a large language model API that forwards requests while adding logging, rate limiting, or credential management, without changing how the application calls the model. [Learn More](/docs/ai-gateway/overview/) ## LLM Router An LLM Router is the decision layer inside an AI Gateway that picks which model or provider handles a given request — for example based on cost, latency, capability, or a failover rule when a provider is down. [Learn More](/docs/ai-gateway/overview/) ## MCP MCP (Model Context Protocol) is an open standard that allows AI models to access external data, tools, and services, and potentially use them to automate workflows. [Learn More](https://en.wikipedia.org/wiki/Model_Context_Protocol) ## mTLS mTLS (Mutual TLS) is a security protocol where both the client and server authenticate each other using TLS certificates, ensuring both parties are who they claim to be. [Learn More](https://en.wikipedia.org/wiki/Mutual_authentication) ## ngrok Agent The ngrok agent is a lightweight command-line application that you install on your machine or server. It establishes secure, outbound-only connections to the ngrok cloud to create endpoints for your upstream services. [Learn More](/docs/agent/) ## OAuth OAuth is an open standard for authorization that allows users to grant third-party applications limited access to their resources without sharing their credentials. [Learn More](https://en.wikipedia.org/wiki/OAuth) ## OIDC OpenID Connect (OIDC) is an authentication protocol that enables third-party applications to confirm a user's identity and access basic profile details through a single sign-on (SSO) process. [Learn More](https://en.wikipedia.org/wiki/OpenID) ## OWASP The Open Web Application Security Project is a non-profit organization dedicated to improving software security through providing resources, tools, and community support. [Learn More](https://owasp.org/about/) ## Point of Presence A Point of Presence (PoP) is a physical location in ngrok's global network where traffic enters the ngrok cloud. ngrok operates PoPs around the world to minimize latency for end users. [Learn More](/docs/gateway/points-of-presence/) ## RBAC RBAC (Role-Based Access Control) is a method of restricting system access based on the roles assigned to individual users within an organization. [Learn More](https://en.wikipedia.org/wiki/Role-based_access_control) ## Reverse Proxy Reverse proxies are an extra security layer between public traffic and your internal services. They live on servers or cloud services, and they intercept and forward traffic to upstream services. [Learn More](https://en.wikipedia.org/wiki/Reverse_proxy) ## Reverse Tunnel A reverse tunnel is a connection initiated outbound from a private network to a public relay, which then forwards inbound traffic back through that same connection. It lets a service behind a firewall or NAT become reachable without opening inbound ports — the mechanism the ngrok agent uses to expose local services. [Learn More](https://en.wikipedia.org/wiki/Tunneling_protocol) ## Service User A Service User (previously called a Bot User) is a service account that owns a set of credentials (authtokens, API keys, and SSH keys) independently of a person. This is useful for automated systems that programmatically interact with your ngrok accounts. [Learn More](/docs/iam/service-users/) ## SAML SAML (Security Assertion Markup Language) is an open standard for exchanging authentication and authorization data between an identity provider and a service provider, commonly used for enterprise single sign-on. [Learn More](https://en.wikipedia.org/wiki/Security_Assertion_Markup_Language) ## SCIM SCIM (System for Cross-domain Identity Management) is an open standard for automating the exchange of user identity information between identity domains or IT systems. [Learn More](https://en.wikipedia.org/wiki/System_for_Cross-domain_Identity_Management) ## Shadow IT Shadow IT refers to IT systems, software, and cloud services used by individuals within an organization without the IT department's knowledge or approval. [Learn More](https://en.wikipedia.org/wiki/Shadow_IT) ## Site-to-Site VPN A site-to-site VPN is an encrypted tunnel connecting two private networks — for example, a company's data center and a customer's on-prem network — so devices on either side can reach each other as if on the same LAN. It typically requires matching VPN appliances and firewall changes on both ends; ngrok's site-to-site connectivity reaches the same outcome without inbound firewall rules on either side. [Learn More](/docs/gateway/site-to-site-connectivity/overview/) ## SSO SSO (Single Sign-On) is an authentication method that allows users to log in once and gain access to multiple related applications or systems without re-entering credentials. [Learn More](https://en.wikipedia.org/wiki/Single_sign-on) ## SNI SNI (Server Name Indication) is a TLS extension that allows a client to specify the hostname it is trying to connect to during the TLS handshake, enabling servers to present the correct SSL/TLS certificate for that hostname. [Learn More](https://en.wikipedia.org/wiki/Server_Name_Indication) ## TCP-KeepAlive TCP KeepAlive enables TCP connections to remain active even when no data is exchanged between the connected endpoints. [Learn More](https://en.wikipedia.org/wiki/Keepalive) ## TLS Certificate A TLS certificate (or SSL certificate) is a digital certificate that ensure your connection to a website or server is securly encrypted. [Learn More](https://en.wikipedia.org/wiki/Transport_Layer_Security) ## TLS Termination TLS (Transport Layer Security) termination is the process of decrypting incoming TLS traffic at a server or load balancer before passing the unencrypted traffic to internal systems. [Learn More](/docs/domains/tls-termination/) ## Traffic Policy Traffic Policy is a configuration language that enables you to filter, match, manage, and orchestrate traffic to your endpoints. For example, you can add authentication, send custom responses, rate limit traffic, and more. [Learn More](/docs/traffic-policy/) ## upstream An upstream is the service, server, or URL that ngrok forwards incoming traffic to. When you create an ngrok endpoint, the upstream is the destination that ultimately handles the request. ## v2 v2 is shorthand for the second major version of the ngrok Agent. [Learn More](/docs/agent/config/v2) ## v3 v3 is shorthand for the third major version of the ngrok Agent. [Learn More](/docs/agent/config/v3) ## WAF A web application firewall (WAF) is an intermediary service in the cloud or on a server that protects web services by filtering and monitoring HTTP traffic. [Learn More](https://en.wikipedia.org/wiki/Web_application_firewall) ## Webhook Gateway A Webhook Gateway is a managed entry point for receiving webhooks from third-party services and routing them to internal or on-prem systems, typically adding verification, retries, and observability in front of the receiving application. [Learn More](/docs/gateway/webhooks-to-on-prem/overview/) ## WebSocket WebSocket is a communication protocol that provides full-duplex (two-way) communication channels over a single TCP connection, enabling real-time data exchange between a client and server. [Learn More](https://en.wikipedia.org/wiki/WebSocket) ## ZTNA ZTNA (Zero Trust Network Access) is a security model that grants access to specific applications or resources based on verified identity and policy for each request, rather than placing a user on a network and trusting everything reachable from it. [Learn More](https://ngrok.com/use-cases/saas-customer-connectivity) # Account Domain Controls Source: https://ngrok.com/docs/iam/domain-controls Configure account domain controls to claim ownership of email domains and enforce single sign-on for your organization. ## Overview Account Domain Controls enable you to claim ownership of an email domain like 'example.com' and force all log-in and sign-up attempts from emails on that domain to use your account. Domain Controls are a policy tool to enforce all users at your organization to use a single ngrok account that you've set up with appropriate security controls. A Domain Control may only be created on an account that has a Single Sign-On Identity Provider configured. ## Verification Creating an Account Domain Control requires that you verify your ownership of the domain. When the Customer Success team creates your Account Domain Control, you'll be asked to prove your ownership of the domain. There are different verification methods, but the most common is to create a DNS TXT record on the domain you're setting up. ## Behavior changes ### Log in When an Account Domain Control for `example.com` exists, anyone attempting to log in with an `example.com` email address will be required to log in with the Single Sign-On IdPs configured on your account. They will be presented with links to those IdP on the log-in page. ### Account sign-up When an Account Domain Control for `example.com` exists, signing up for a new account with an email address on `example.com` can be disallowed. Users will be redirected instead to log in with the SSO IdPs configured on your existing account. ### Account creation When an Account Domain Control for `example.com` exists, any ngrok users with `example.com` email addresses can be disallowed from creating new ngrok accounts owned by their user. ### Email address changes Users with an email address matching your Account Domain Control are no longer allowed to change their email address to one that doesn't match your domain control. ### Retroactive account consolidation Creating an Account Domain Control does not retroactively consolidate existing accounts created by users with matching email addresses. If you require account consolidation as part of creating an Account Domain Control, please [contact Support](mailto:support@ngrok.com) for pricing. ## Domain controls pricing [Contact Support](mailto:support@ngrok.com) to enquire about pricing for Account Domain Controls. # Identity and Access Management Overview Source: https://ngrok.com/docs/iam/index Learn about ngrok's identity and access management system for managing credentials, enforcing access controls, and federating identity. ngrok IAM helps you manage access to your account. Give each person or system their own credentials so you can rotate or revoke access when needed, and see every change tied to a specific identity in audit logs. You can also set up single sign-on (SSO) with your identity provider and manage multiple ngrok accounts from one user. ## Concepts Manage human users who can log into the dashboard, start agents, create endpoints, and access the API. Create dedicated credentials for automated processes that interact with your ngrok account programmatically. Enforce least-privilege access by restricting what actions each user can take within your account. Federate identity with your IdP and enable SSO authentication for dashboard access. Enforce organization-wide account usage by requiring users with your email domain to use your account. ## Use cases Grant secure access to customer network resources like REST APIs and databases without exposing them to the public internet. Enable technicians and IT admins to maintain remote devices and servers via SSH or RDP through edge gateways. Access smart factory IoT devices, telemetry sensors, and monitoring dashboards from remote networks. Embed ngrok connectivity into Python applications to access APIs running on IoT devices. Connect from your Kubernetes cluster to customer on-premises systems like inventory databases and payment APIs. ## What's next? * Manage team access with [Users](/docs/iam/users/) and invitations. * Create credentials for automation with [Service Users](/docs/iam/service-users/). * Federate identity and require IdP login with [Single Sign-On](/docs/iam/sso/). # Role-Based Access Control Source: https://ngrok.com/docs/iam/rbac Configure role-based access control in ngrok to restrict actions that users and service users can perform in your account. ## Overview Use role-based access control (RBAC) to restrict what actions each member of your account can take. You can apply RBAC to Users and to an [Invitation](/docs/iam/users/#invitations) when you invite a collaborator to join your ngrok account. Configure RBAC from the [team members page](https://dashboard.ngrok.com/team-members) of the dashboard. ## Roles All Users on your account have a Developer, Team, and Billing role. You can also make a User an administrator, which gives them full account control. ### Developer The Developer role controls whether a user can create, modify, or delete objects such as Domains, TCP Addresses, Edges, Log Destinations, Authtokens, and API Keys. There are two possible values: * **Read/Write**: A user can create, modify, and delete any developer feature on the account. This doesn't include team management, billing, or account settings. * **Read-only**: A user can view but not create, modify, or delete developer features on the account. Users with a read-only developer role can still see their personal authtoken and use the ngrok agent to create endpoints for applications. ### Team The Team role controls whether a user can invite, remove, and manage other team members. There are three possible values: * **Manager**: A user that can invite and remove other users from the account. They can also modify other users' RBAC settings. They can't modify or remove Administrators. * **Invite-only**: A user can see other members of the account and invite new teammates to join the account. They can't remove other account members or modify the privilege levels of other users. * **Read-only**: A user can see other members of the account but can't manage or invite other users. ### Billing The Billing role controls whether a user can update the account's billing details and subscription plan. There are two possible values: * **Billing access**: A user can change the subscription plan, payment method, billing address, and other details. Users with this permission can also view billing history. * **No billing access**: A user can't view or modify any billing details. ### Administrator Users with the Administrator role can take all actions within an account. It's the only role that can configure Account settings like SSO and SCIM. Administrators can remove other administrators from the account. Accounts must always have at least one administrator. ## Service Users [Service Users](/docs/iam/service-users/) hold a smaller set of roles than Users: the read/write or read-only [Developer](#developer) role, and the Manager [Team](#team) role. New Service Users get the read/write Developer role. Service Users can't be Administrators or have billing access. See [Service Users](/docs/iam/service-users/#role-based-access-control) for details. ## Additional restrictions with ACLs You can also use [ACLs](/docs/gateway/agent/authtokens#authtoken-acls) to limit what an Authtoken or SSH Public Key credential can do in your account. For example, you can restrict which endpoints a credential can listen on. ## RBAC pricing RBAC is available on the [Pay-as-you-go plan](https://ngrok.com/pricing) with the SSO & RBAC add-on enabled. # Service Users Source: https://ngrok.com/docs/iam/service-users Service Users are ngrok service accounts that own credentials independently of a person, so automated systems keep working when people leave. ## Overview Service Users (previously called Bot Users) are similar to [Users](/docs/iam/users/), but they're for automated systems that interact with your ngrok account, either by starting ngrok Agents or by calling the API. Service Users own credentials (authtokens, API keys, and SSH keys) that they use to authenticate with ngrok. Other platforms sometimes call this concept a *service account*. You can see and manage your Service Users in the [ngrok Dashboard](https://dashboard.ngrok.com/service-users). ## When to use a Service User Use Service Users whenever an automated process uses your ngrok account. Service Users keep your agents and programs working after the person who set them up leaves your account. When a user leaves an account, ngrok revokes all of their credentials, and any service still using them stops working. ## Differences from users Service Users work like Users, with a few exceptions: * Service Users can't log into the Dashboard. * Service Users can hold only a few [roles](#role-based-access-control), and never the Administrator role. * Service Users can't be a member of more than one Account. If you're on a free plan, ngrok doesn't count Service Users when it computes how many seats to charge you for. ### Role-based access control Every Service User holds at least one role. New Service Users get the `developer` role unless you choose another. A Service User can hold these roles: * `developer`, the read/write [Developer](/docs/iam/rbac/#developer) role. * `developer-readonly`, the read-only [Developer](/docs/iam/rbac/#developer) role. * `team-manager`, the Manager [Team](/docs/iam/rbac/#team) role. A Service User can't hold `developer` and `developer-readonly` at the same time. Service Users can't be Administrators or have billing access. Only Administrators can set a Service User's roles. Set them with the `roles` field when you create or update a Service User through the [API](/docs/api-reference/service-users). Any role other than `developer` requires a plan that includes RBAC. A Service User's roles limit what its personal access tokens can do. They don't restrict its API keys. ## Event logs When a Service User takes actions against your ngrok account, the event log records it as the [Principal](/docs/obs/events/#principal-object). This shows you what each automated service is doing in your account. ## Service Users pricing Service Users are available on Pay-as-you-go plans only. For more information, see the [ngrok Pricing page](https://ngrok.com/pricing). # Single Sign-On and Provisioning to the ngrok Dashboard Source: https://ngrok.com/docs/iam/sso Learn how to configure single sign-on for the ngrok dashboard using SAML or OpenID Connect identity providers. This page relates to using SSO and OAuth to sign in to the ngrok dashboard. To require OAuth for traffic visiting your endpoints, see [the OAuth Traffic Policy action documentation](/docs/gateway/traffic-policy/actions/oauth). You may configure your account to use one or more Single Sign-On (SSO) Identity Providers (IdPs). Once enabled, users can use the IdP to log into your ngrok dashboard. You may also configure enforcement settings to require that users must use SSO to log into your ngrok account. Once you have configured SSO, you may also enable [SCIM](#scim) to automate user provisioning of your ngrok account. Set up SSO on the [Account Settings page](https://dashboard.ngrok.com/settings) of your ngrok dashboard. ## Verified identity providers ngrok supports identity providers that support either SAML or OpenID Connect for SSO. The following table lists the identity providers that are verified to work with SSO for your ngrok account. | Identity Provider | Guides to get started | | - | - | | Okta | [ngrok's Dashboard SSO with Okta SAML guide](/docs/integrations/dashboard-sso/okta-dashboard-sso-saml) and [SCIM provisioning](/docs/integrations/dashboard-sso/okta-dashboard-sso-scim) | | Microsoft AzureAD | [ngrok's Dashboard SSO with SAML guide](/docs/integrations/dashboard-sso/microsoft-sso-saml) and the [official documentation](https://learn.microsoft.com/en-us/entra/architecture/auth-saml) | | Salesforce | [ngrok's Dashboard SSO with Salesforce OAuth and OIDC guide](/docs/integrations/dashboard-sso/salesforce-dashboard-sso-oidc) and the [official documentation](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_oauth_and_connected_apps.htm) | | Auth0 | [ngrok's Dashboard SSO with Auth0 SAML guide](/docs/integrations/dashboard-sso/auth0-dashboard-sso-saml) and the [official documentation](https://auth0.com/docs/quickstart/native/device/interactive) | ## Enforcement Your account sets an SSO Enforcement policy which controls whether users are required to log in with SSO. **Mixed Mode**: In mixed mode, users who existed on your account before you set up SSO may continue using their existing credentials to log in. All new users will be required to use SSO. **SSO Enforced**: In SSO enforced mode, all users must use your SSO IdP to log in and their existing credentials will no longer allow them to log into your account. Keep in mind that after you add an IdP, your account is still in Mixed Mode and users can continue to log in with their previous credentials. Once you are confident that your SSO integration is configured correctly, you can switch to SSO Enforced mode. This helps you avoid inadvertently locking yourself or your users out of the account. ## IdP-initiated login ngrok supports IdP-initiated login flows for SAML IdPs. An IdP-initiated login flow is one in which users can log into your ngrok account by clicking on a link in your IdP's dashboard. You may enable IdP-initiated login on a per-IdP basis. The OpenID Connect protocol does not support IdP-Initiated login so it is not supported for IdPs you connect that way. ## User provisioning When using SSO, you may configure how users are provisioned and deprovisioned from your ngrok account. You may configure your account to provision users in one of three modes: * Explicitly invited by an existing member of your account * Just-in-time (JiT) provisioned after they successfully log in with SSO * Managed via your IdP's [SCIM integration](#scim). It is recommended that you choose either JiT or SCIM. See [User Provisioning](/docs/iam/users/#provisioning) for more details. ## Multiple IdPs You may configure multiple IdPs to use for SSO. If you do, when a user tries to log in, they will be presented with a choice of which provider to use to log in. The description provided when configuring the IdP in your account will be displayed to the user here and can be used to distinguish similar providers. ## SCIM ngrok supports a limited subset of [RFC 7644](https://datatracker.ietf.org/doc/html/rfc7644) to enable SCIM provisioning and deprovisioning. ngrok's SCIM implementation works with major IdPs like Okta and Microsoft Entra ID. ngrok's SCIM API Base URL is: ``` https://api.ngrok.com/scim/v2/ ``` Your IdP will authenticate to ngrok's SCIM API with [API Keys](/docs/api/#authentication) like any other ngrok API client. If you use [API IP Restrictions](/docs/api/#ip-restrictions) with SCIM, ensure that your IdP's SCIM client IPs are allowed. ngrok does not support SCIM provisioning of users with passwords for authentication. For that reason, you must first configure an SSO Identity Provider before you can configure a SCIM integration. ngrok's SCIM implementation is limited to the operations necessary to support user provisioning and deprovisioning. For instance, it does not implement bulk operations or change password behavior, sorting, or etags. It does not support Groups. ngrok's SCIM implementation supports three properties on the User schema: `userName`, `displayName`, and `active`. The `userName` property must be mapped to a user's email address. The `active` property can be used to control whether a [user is disabled](/docs/iam/users/#disabling). `displayName` should be mapped to a user's full name. Consult the [SCIM Provisioning documentation](/docs/iam/users/#scim) for additional details on how users are provisioned with SCIM. ## SSO pricing Dashboard Single Sign-On is available on the Pay-as-you-go plan with the SSO & RBAC add-on enabled. [Contact Support](mailto:support@ngrok.com) for SCIM availability. # Users Source: https://ngrok.com/docs/iam/users Understand how users work in ngrok accounts including authentication, credentials, dashboard access, and multi-account membership. ## Users Users are members of your Account that may log into the dashboard, start agents, create endpoints and access the API. * Users have one or more [Credentials](#credentials) that they use to authenticate with the ngrok service. * Users are not uniquely owned by an account. A User may be a member of more than one account. * Users are subject to [Role Based Access Control](/docs/iam/rbac) that allows you to restrict what actions Users may take within the account. * Whenever a User takes an action within an account, the corresponding [Event Log](/docs/obs/) will attribute the event to the User by recording it as the Principal. ## Dashboard access Users may log into the [ngrok dashboard](https://dashboard.ngrok.com). You may configure your account to restrict how users authenticate to the dashboard. ngrok supports dashboard authentication via an email and password, an IdP like GitHub or Google or your own [Single Sign-On IdP](/docs/iam/sso) like Okta or Azure AD. Users may log into your ngrok Account's dashboard. Normally, users enter an email and password to log into the ngrok dashboard but you may configure your ngrok account to require additional factors or require SSO. [Service Users](/docs/iam/service-users/), by contrast, may not log into the dashboard. ### Password management You can sign up for an ngrok account using an email and password, or using an SSO provider. #### Reset a forgotten password If you've forgotten your password, it can be reset at any time via the [login page](https://dashboard.ngrok.com/login). ngrok login screen with GitHub, Google, and SSO options and an arrow pointing to the Forgot password? link From there, enter the email address you used to sign in to your ngrok account, and select **recover password**. ngrok Reset Password form with an email field and a Recover Password button If you use SSO to sign in, and haven't also added a password to your account, you won't be able to reset your password. Ensure you enter the correct email, and check your spam folder to confirm you've received the password reset email. If you can't remember what email you used, you may want to try other possible addresses, or review past invoices to reference the correct email. If you receive an `ERR_NGROK_4301` error, you'll need to try signing in with SSO, as you didn't set a password for the account. ngrok ERR_NGROK_4301 error dialog indicating the account has no password and must log in with Google or GitHub If you don't receive the email after completing these steps, contact support for further assistance. #### Change your password If you already have access to your account already, but want to change the password, you can do so via your user settings. Select your username in the upper-left corner of the dashboard and selecting **User Settings**. From there, you'll enter your old password, and the new one you would like to use. ngrok user settings Login Methods section showing a Password form with New Password, Current Password fields and a Change password button ### MFA Multi Factor Authentication (MFA) allows you to add an extra layer of security to your ngrok account by requiring an additional authentication factor to log in. At the moment, MFA only supports one-time passwords ([TOTP](https://en.wikipedia.org/wiki/Time-based_one-time_password)) as an additional factor. You may configure your ngrok account to enforce that all users have MFA enabled. #### Set up MFA during signup When signing up for an ngrok account for the first time, you'll be prompted to set up Multi Factor Authentication with a QR code from ngrok to use with your preferred authenticator. ngrok Multi-factor Authentication setup screen showing a QR code to scan with an authenticator app From here, you'll want to open up your preferred authenticator (Google Authenticator, Microsoft Authenticator, Authy, Duo, Okta Authenticator, etc) and utilize the QR code provided. Once you set up your preferred authenticator, you'll be asked to enter a code from that authenticator each time you log into your ngrok account. #### Recovery codes You will also be provided a list of recovery codes, which you can copy and paste elsewhere or download directly. Store these recovery codes in a secure location, as they can be used to access your account if you lose access to your authenticator device. ngrok MFA recovery codes screen warning that codes must be saved securely to avoid losing account access #### Manage MFA settings You can enable or disable MFA at any time once logged into your account by selecting on your username in the upper left corner of your dashboard, and then **User Settings**. Scroll down to **Multi Factor Authentication** and select **Enable** or **Disable**, as well as generate new recovery codes if needed. ngrok Multi-factor authentication settings showing the TOTP section with an Enable TOTP button highlighted ngrok Multi-factor authentication settings with TOTP enabled showing Disable TOTP and Regenerate codes buttons #### Lost access or recovery If you lose access to your authenticator or device and do not have recovery codes downloaded, or have used all available codes, email [support@ngrok.com](mailto:support@ngrok.com) with the email you use to sign in to your ngrok account. ### Single sign-on Users may also log in with a federated IdP via single sign-on. Your ngrok account may be configured to require the use of single sign-on for all of your users to log in. Consult the [Single Sign-On documentation](/docs/iam/sso) for additional details on configuring it. ### IP restrictions In addition to the normal authentication factors required to log into the ngrok dashboard, you may also configure your ngrok account to further restrict dashboard access to a set of IP CIDR blocks. Dashboard IP Restrictions should always be used in a warning mode first to test that you won't accidentally lock yourself out of your account if you restrict access to IPs that you can't use. IP Restrictions can be configured manually on the ngrok dashboard or programmatically via API with a `type` of `dashboard`. * **[IP Restrictions on your ngrok dashboard](https://dashboard.ngrok.com/ip-restrictions)** * **[IP Restrictions API Resource](/docs/api-reference/iprestrictions/list/)** ## Credentials Users own one or more credentials that enable them to start [Agents](/docs/gateway/agent/) and make API requests. Credentials are assigned an owner when they are created and the owner cannot be changed. All Credentials have a Principal owner which is either a User or a Service User. Credentials are [Authtokens](/docs/gateway/agent/authtokens), [API Keys](/docs/api/#authentication) and [SSH Public Keys](/docs/gateway/agent/ssh-reverse-tunnel-agent/#authentication). ## Disabling You may 'disable' users in your account. When a User is disabled it cannot log into the dashboard, start an ngrok agent or make API requests. All of its [Credentials](#credentials) remain but cannot be used. A User may be re-enabled at any time. Users may disabled and enabled [Administrators](/docs/iam/rbac/#administrator) or [Team Managers](/docs/iam/rbac/#team). ## Deletion When a User is deleted from an account, all of its [Credentials](#credentials) for that account are revoked and any agents using them will stop working. If you have an automated process or agent using ngrok, use a [Service User](/docs/iam/service-users) instead. When you delete a User from your account, keep in mind that the User itself is not deleted, it is only removed from the Account. It may continue to use and access other Accounts it is a member of. Users may deleted by [Administrators](/docs/iam/rbac/#administrator) or [Team Managers](/docs/iam/rbac/#team). ## Provisioning Provisioning is about how you add new users to your account. You may configure your ngrok account with your preferred provisioning method on your [Account Settings page](https://dashboard.ngrok.com/settings). By default, you provision new users by inviting them to join your Account with Invitations. If you have configured [SSO](#single-sign-on), you may also add users to your account via the SCIM or JIT provisioning methods. ### Invitations Invitations are the default user provisioning mechanism. You invite new users by email address. They must log in or sign up with that email address to accept the invitation. When you issue the invitation, if you are using [RBAC](/docs/iam/rbac) you may also choose the permissions they will receive when they join the account by accepting the invitation. ### Just-in-time If you have configured a federated SSO IdP on your account, you may configure Just-in-Time (JIT) provisioning. When JIT provisioning is enabled, if a user signs in with your configured IdP and they do not have a matching ngrok User, one will automatically be provisioned for them the first time they log in and it that user will be added as a member of your account. Users who are JIT provisioned are always assigned a Read/Write [Developer role](/docs/iam/rbac#developer) and an Invite [Team role](/docs/iam/rbac#team). ### SCIM If you have configured a federated SSO IdP on your account, you may configure SCIM provisioning. When SCIM provisioning is enabled, the IdP is responsible for making calls to ngrok's SCIM API Users provisioned by SCIM are always assigned a Read/Write [Developer role](/docs/iam/rbac#developer) and an Invite [Team role](/docs/iam/rbac#team). Users provisioned by SCIM are not permitted to change their name or email address because it is managed by the IdP. Consult the [SCIM documentation](/docs/iam/sso/#scim) for additional details. # How to secure your ngrok CLI credentials with 1Password Source: https://ngrok.com/docs/integrations/cli-credentials/1password Use the 1Password CLI and ngrok shell extension to store authtokens and switch credentials at runtime. This guide walks you through setting up the 1Password CLI with the ngrok shell extension to store and access your ngrok credentials via 1Password. This combination gives you the speed and simplicity of ngrok protected by the power of 1Password's vault. By integrating ngrok with 1Password, you can: * Store your credentials in one place and avoid outdated tokens across environments after a reset * Share credentials between environments securely without copy-pasting or reusing them across projects * Switch between credentials at runtime to test different ngrok configurations, feature sets, or accounts ## What you'll need * A 1Password account. * [1Password desktop app](https://support.1password.com/get-the-apps/) (v8 or later) and [1Password CLI](https://app-updates.agilebits.com/product_history/CLI2) (v2.14.0 or later) installed on your machine. * [ngrok](https://download.ngrok.com) installed on your machine. * Basic familiarity with the command line. One of the easiest mistakes is failing to connect the app to the CLI. In the 1Password app, go to **Settings** > **Developer** and select **Connect with the 1Password CLI**. ## 1. Initialize the 1Password ngrok shell plugin To configure the ngrok shell extension, open a terminal and run this command: ```bash theme={null} op plugin init ngrok ``` If you're logged into the 1Password app, you'll be presented with a permission request. Otherwise you'll need to authenticate first, then you'll see the request. Select **Allow Access** and you'll see a command line option. For initialization, select **Import into 1Password**, copy and paste your ngrok authtoken into the command line, and press **Enter**. Next, give it a memorable name. Finally, choose when you want to use it. ## 2. Clean up your ngrok.yml file Now that your credentials are in your 1Password vault, you can remove them from your local environment. Use your text editor to update `.ngrok2/ngrok.yml` and remove the authtoken value: ```bash theme={null} vim .ngrok2/ngrok.yml ``` You can delete the entire authtoken line, or to test safely, prefix the line with a pound sign (`#`). Either way, ngrok will no longer find and use that value. ## 3. Start your tunnel Now that the environment is configured, start the tunnel. 1Password will intercept the request, determine which credential to use, and start a tunnel as requested. By following this guide, you've integrated the 1Password CLI with ngrok. You can securely manage your ngrok credentials and choose between multiple credentials at runtime to demonstrate and test under different configurations. # Configure Auth0 Single Sign-On with SAML Source: https://ngrok.com/docs/integrations/dashboard-sso/auth0-dashboard-sso-saml Configure single sign-on (SSO) for your ngrok dashboard using Auth0 as the Identity Provider. This guide walks you through configuring the ngrok dashboard to use Auth0 as an identity provider and enable single sign-on (SSO) with SAML for your ngrok account. It does not cover configuring an ngrok endpoint to allow your application users to log in using Auth0. ## What you'll need * Admin access to create new applications in Auth0. * Admin access to edit your ngrok account settings. * An [ngrok Pay-as-you-go account](https://ngrok.com/pricing). ## 1. Create an Application in Auth0 * From the **Applications** menu, click **Create Application**. * Name your application, select the application type of Native, and click **Create**. * Select your application and choose the **Addons** tab. * Turn SAML2 support on. * In the popup window on the **Usage** tab, download the Identity Provider Metadata from the provided link. ## 2. Configure SSO for your ngrok account * Log into your ngrok dashboard and navigate to **Settings > Account**. * Click **+ New Identity Provider** to add a new SAML identity provider. * Add a helpful description, and then upload the `metadata.xml` file from Auth0 into the ngrok dashboard. * In the Options section, select whether to allow users to log into the dashboard directly from their Auth0 dashboard. * Click **Save**. This creates the integration and generates the required URLs for your Auth0 Application. ## 3. Add ngrok URLs to your Auth0 SAML application * Back in your Auth0 account, select your Application and click the **Addons** tab. * Select the SAML2 Web App. * In the popup window, select the **Settings** tab. * Paste in the Application Callback URL (ACS) obtained from the ngrok IdP settings (SAML Provider/Service Provider, ACS URL). * In the settings code block window, paste in the following and click **Save**: ```json theme={null} { "nameIdentifierFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "nameIdentifierProbes": [ "email" ], "mappings": { "email": "email" } } ``` You can now log into your ngrok account using Auth0. By default, your ngrok account allows users to log in with their existing credentials as well as through Auth0 ("Mixed Mode"). After you verify that the integration works, enable **SSO Enforced** in the ngrok dashboard to require all new users to log in through Auth0. # Microsoft Entra ID SSO (SAML) Source: https://ngrok.com/docs/integrations/dashboard-sso/microsoft-sso-saml Use Microsoft Entra ID SAML to secure access to the ngrok Dashboard with single sign-on (SSO). This guide walks you through configuring Microsoft Entra ID as the primary Identity Provider for the ngrok Dashboard. By integrating Microsoft Entra ID with ngrok, you can: * Restrict access to the ngrok Dashboard to only users authenticated via Microsoft Entra ID * Use Microsoft Entra ID's dashboard to facilitate access to the ngrok app ## What you'll need * An ngrok account with administrative rights to modify account settings. * An ngrok Pay-as-you-go account with access to configure SAML SSO. ## 1. Configure Microsoft Entra ID In the Microsoft Entra admin center, add an enterprise application for the ngrok Dashboard, then enable SAML single sign-on for it. See Microsoft's guides to [adding an enterprise application](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/add-application-portal) and [enabling SAML single sign-on](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/add-application-portal-setup-sso). As you configure the app: * Enter temporary values such as `https://temporary` for the **Identifier (Entity ID)** and the **Reply URL (Assertion Consumer Service URL)**. You replace them with ngrok's values in a later step. * Set the **Unique User Identifier** to `user.mail`. This value can vary based on your setup, but it must be an email address. Then download the app's **Federation Metadata XML** from the **SAML Certificates** section of its **Single sign-on** settings. You upload this file to ngrok in the next step. Microsoft Entra ID restricts access to the app through assignments, and new apps start with none. Assign the users and groups who should reach the ngrok Dashboard, and make sure your own account is assigned so you can test the integration. ## 2. Configure ngrok To configure ngrok Dashboard SSO with Microsoft Entra ID: * Go to the [ngrok Dashboard](https://dashboard.ngrok.com). * Click **Settings > Account**. * In the **Single Sign-On (SSO)** section, click **New Identity Provider > New SAML Provider**. * In the **SAML Provider** settings, provide a **Description** and click **Upload XML**. * Select the metadata file you downloaded and click **Save**. * After you save, the **SP Metadata** appears. Copy these values into the Microsoft Entra ID app's **Single sign-on** settings, replacing the temporary values you entered earlier. * Save the changes in Microsoft Entra ID. ## 3. Test the integration * Navigate to the Microsoft Entra ID app's **Single sign-on** settings. * Click **Test > Test sign in**. * Microsoft Entra ID redirects you to the ngrok Dashboard. * If the current user isn't a member of the ngrok account and just-in-time (JIT) provisioning is enabled, ngrok prompts the user to join. * If the current user is already a member, ngrok signs them in to the ngrok Dashboard. # Configure Okta Single Sign-On (SSO) Using OpenID Connect (OIDC) Source: https://ngrok.com/docs/integrations/dashboard-sso/okta-dashboard-sso-oidc Configure Okta single sign-on (SSO) with OpenID Connect (OIDC) to sign in to the ngrok dashboard. This guide explains how to use Okta as an identity provider for single sign-on (SSO) into the ngrok dashboard, using OpenID Connect (OIDC). If you want to use SAML instead, see [Configure Okta Single Sign-On (SSO)](./okta-dashboard-sso-saml). It does not cover configuring an ngrok endpoint so that your [application users can log in using Okta](../endpoint-sso/okta-sso-oidc). ## What you'll need * Admin access to create new applications in Okta. * Admin access to edit your ngrok account settings. * An [ngrok Pay-as-you-go account](https://ngrok.com/pricing), with the [SSO/RBAC add-on](https://ngrok.com/pricing#identity-and-acces) ## 1. Create a new OIDC app integration in Okta * From the **Applications** menu, click **Create App Integration**. * Select **OIDC - OpenID Connect**, then click **Next**. * Select **Web Application**, then click **Next**. * Give your app a name, then click **Next**. * Under **Sign-in redirect URIs**, enter `https://idp.ngrok.com/oauth2/callback`. * Click **Save**. * On the **Assignments** tab, assign the users or groups that need dashboard access. * On the **General** tab, copy the **Client ID** and **Client Secret**. You'll need these values for the ngrok side. ## 2. Configure SSO for your ngrok account * Log into your ngrok dashboard and go to **Settings > Account**. * Click **+ New Identity Provider**, then select **New OpenID Connect Provider**. * Set **Issuer URL** to your Okta org URL, for example `https://{yourOktaDomain}.okta.com`. * Enter the **Client ID** and **Client Secret** from Okta. * In the **Scopes** field, add the required `email` scope. You can optionally add the `profile` scope too to show a user's name instead of their email address. * Click **Save**. You can now log in to your ngrok account by authenticating with Okta. By default, your ngrok account allows users to log in with their existing credentials and through Okta ("Mixed Mode"). After you confirm the integration works, turn on **SSO Enforced** in the ngrok dashboard to require all new users to log in with Okta SSO. # Configure Okta Single Sign-On (SSO) Source: https://ngrok.com/docs/integrations/dashboard-sso/okta-dashboard-sso-saml Configure Okta single sign-on (SSO) to sign in to the ngrok dashboard. This guide explains how to use Okta as an identity provider to enable single sign-on (SSO) when signing into the ngrok dashboard. It does not cover configuring an ngrok endpoint so [application users can log in using Okta](../endpoint-sso/okta-sso-oidc). ## What you'll need * Admin access to create new applications in Okta. * Admin access to edit your ngrok account settings. * An [ngrok Pay-as-you-go account](https://ngrok.com/pricing). ## 1. Create a new SAML App Integration in Okta * From the **Applications** menu, click **Create App Integration**. * Select **SAML 2.0** and click **Next**. * Give your app a name, and click **Next**. * Enter temporary values for **Single sign on URL** and **Audience URI**, select **EmailAddress** for **Name ID format**, and then click **Next**. ngrok requires the username to be in email format. * Select **This is an internal app that has been created** and click **Finish**. ## 2. Download your SAML App metadata XML * Navigate to the **Sign On** tab on the new app and click **Actions** under the Active SHA-2 certificate. * Click **View IdP metadata**. * In that new window, select **Save As** from the File menu to save your `metadata.xml` file for uploading into ngrok in a later step. ## 3. Configure SSO for your ngrok account * Log into your ngrok dashboard and navigate to **Settings > Account**. * Click **+ New Identity Provider** to add a new identity provider. * Add a helpful description, and then upload the `metadata.xml` file from Okta into the ngrok dashboard. * In the Options section, select whether to allow users to log into the dashboard directly from their Okta dashboard. * Click **Save**. This creates the integration and generates the required URLs for your Okta Application. ## 4. Add ngrok URLs to your Okta SAML application * Back in your Okta account, on the **General** tab of your Okta app, click **Edit** under **SAML Settings**. * Click **Next** on the **General Settings** tab to get to the **Configure SAML** tab. * Replace the placeholder values with the values ngrok provided. Put the ngrok **ACS URL** value into Okta's **Single sign on URL** field. Put the ngrok **SP Entity ID** value into Okta's **Audience URI (SP Entity ID)** field. * Click **Next**. * Click **Finish**. You can now log into your ngrok account using Okta. By default, your ngrok account allows users to log in with their existing credentials as well as through Okta ("Mixed Mode"). After you verify that the integration works, enable **SSO Enforced** in the ngrok dashboard to require all new users to log in through Okta. # Automate user provisioning via SCIM with Okta Source: https://ngrok.com/docs/integrations/dashboard-sso/okta-dashboard-sso-scim Enable automated user provisioning for your ngrok account via SCIM with Okta. This guide walks you through enabling automated user provisioning for your ngrok account via SCIM with Okta. ## 1. Configure ngrok Log into your ngrok dashboard as an admin user. * Navigate to **Settings > Account**. * Go to the **User Provisioning / SCIM** section and toggle on the **Automated User Provisioning via SCIM 2.0** option. This opens a drawer and prompts you to create a new API key. Use a unique key just for SCIM. * Give the API key a descriptive name and assign it either to an administrator user or a Service User. Service Users are ideal for this integration because they are not deactivated when a user leaves the account. * Save the API key somewhere safe or keep this window open while you complete the next steps in Okta. * Go to the **Account Domain Controls** section and click **Submit New Domain**. * Submit a request to ngrok support to allow the domain sync with SCIM provisioning in your account. ## 2. Configure Okta SCIM Open a new browser tab and log in to the Okta admin console as a user who can manage the ngrok application. * In the Okta admin console, navigate to **Applications > Applications**. * Open the ngrok app you created. * Enable SCIM on the **General** tab in the ngrok application you created under the **Provisioning** section. * Switch to the **Provisioning** tab in your Okta application configuration and configure the following: * SCIM connector base URL: `https://api.ngrok.com/scim/v2/` * Unique identifier field for users: `email` * Supported provisioning actions: Push new users, Push profile updates * Authentication mode: **HTTP Header** * Authorization: The ngrok API key that you saved or copied earlier * Click **Test Connect Configuration** and you should see a success message. If you get an error message that states no users were returned, there's an error in the configuration. Check for typos and leading or trailing spaces. # Salesforce Dashboard Single Sign-On (SSO) Using OIDC Source: https://ngrok.com/docs/integrations/dashboard-sso/salesforce-dashboard-sso-oidc Use Salesforce's managed applications to add single sign-on (SSO) for your ngrok dashboard using the OIDC integration. This guide explains how to use Salesforce as an Identity Provider to allow single sign-on (SSO) into your ngrok dashboard. This should not be confused with securing your ngrok edge to allow your application users to log in using Salesforce. ## What you'll need * Admin access to create new applications in Salesforce. * Admin access to edit your ngrok account settings. * An [ngrok Pay-as-you-go account](https://ngrok.com/pricing) or an SSO/Account Governance license. The OIDC flow is initiated from your Applications login page. (OIDC Service Provider flow is not supported.) ## 1. Configure Salesforce Create a new External Client App in Salesforce, then configure it as follows. In the **Policies** tab: * Go to **App Policies**. * Set **Start Page** to **Custom**. * Set **Custom Start URL** to `https://dashboard.ngrok.com/login/sso`. In the **Settings** tab: * Go to **Basic Information** and set the External Client App Name. * Go to **OAuth Settings > App Settings** and set: * OAuth Scopes: Access the Identity URL Service (id, profile, email, address, phone), Access unique identifiers (openid), Access custom permissions (custom\_permissions) * **Configure ID Token**: Set **ID Token Audience** to `https://idp.ngrok.com`, **Include Standard Claims**, and **Custom Attributes** * Flow Enablement: **Enable Authorization Code and Credentials Flow** * Security: **Require Secret for Web Server Flow** and **Require Secret for Refresh Token Flow** Note your Client ID (Consumer Key) and Secret from **OAuth Settings > App Settings**. ## 2. Configure ngrok * Log into your ngrok dashboard and navigate to **Settings > Account**. * Click **+ New Identity Provider** and select **New OpenID Connect Provider**. * Add a description and set the following details: * Issuer URL, in the format `[yourdomain]-dev-ed.develop.lightning.force.com` * Client ID (from Salesforce) * Client Secret (from Salesforce) You can now log into your ngrok account using Salesforce. By default, users can log into ngrok with their existing credentials as well as through Salesforce ("Mixed Mode"). After you verify that the integration works, enable **SSO Enforced** in the ngrok dashboard to require all new users to log in through Salesforce. # Auth0 Endpoint SSO (OpenID Connect) Source: https://ngrok.com/docs/integrations/endpoint-sso/auth0-sso-oidc Use Auth0 OpenID Connect (OIDC) to secure access to ngrok endpoints with single sign-on. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide shows you how to configure Auth0 as the primary Identity Provider for ngrok endpoints using single sign-on (SSO). By integrating Auth0 SSO with ngrok, you can: * Restrict access to ngrok endpoints only to users authenticated via Auth0 * Use Auth0 security policies and multi-factor authentication (MFA) authenticators * Use Auth0's Dashboard to facilitate access to ngrok apps The ngrok integration with Auth0 supports service provider-initiated (SP-initiated) SSO. In this mode, users access ngrok endpoints and are redirected to Auth0 for authentication. ## What you'll need * An Auth0 account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with OpenID Connect. ## 1. Configure Auth0 In the Auth0 Dashboard, create an application that uses OpenID Connect and supports a client secret (a Regular Web Application). For the full steps, follow Auth0's [Create Applications](https://auth0.com/docs/get-started/auth0-overview/create-applications) guide. While configuring the application, set these ngrok-specific values: * **Allowed Callback URLs**: `https://idp.ngrok.com/oauth2/callback` * **Application Login URI**: the ngrok endpoint URL that exposes your application (for example, `https://myexample.ngrok.app`). From the application **Settings** tab, copy the **Domain**, **Client ID**, and **Client Secret**. You use these values to configure ngrok. Auth0 lets administrators restrict access to the ngrok app through user and group assignments, so make sure the users who need access are assigned to the application before you test. ## 2. Configure ngrok Secure your endpoint with the [OpenID Connect Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc). This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok agent installed. Create a `policy.yaml` file with the following content, replacing the placeholder values with those copied from Auth0: ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: 'https://dev-abcd1234.us.auth0.com/' client_id: 'YOUR_AUTH0_CLIENT_ID' client_secret: 'YOUR_AUTH0_CLIENT_SECRET' scopes: - openid - profile - email ``` Start an ngrok endpoint with this policy: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding** (for example, `https://auth0-sso-test.ngrok.app`). ## 3. Test the integration * In your browser, open an incognito window. * Access your ngrok endpoint (for example, `https://auth0-sso-test.ngrok.app` or using the copied endpoint URL). * When prompted, log in with your Auth0 credentials. * After you log in, you see your web app. # Azure Active Directory B2C SSO (SAML) Source: https://ngrok.com/docs/integrations/endpoint-sso/azure-sso-saml Use Microsoft Azure AD B2C to secure access to ngrok endpoints with single sign-on. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide walks you through configuring Azure AD B2C as an Identity Provider (IdP) for your ngrok endpoints using single sign-on (SSO). By integrating Azure AD B2C with ngrok, you can: * Restrict access to ngrok endpoints to only users who authenticated via Azure AD B2C * Allow users to authenticate via a local account or federated accounts via Azure AD B2C ## What you'll need * An [ngrok Pay-as-you-go account](https://ngrok.com/pricing) with an authtoken or admin access to configure endpoints with SAML. * A Microsoft Azure account with access to an Azure AD B2C tenant. * An understanding of Azure AD B2C custom policies. ## 1. Configure ngrok The SAML Traffic Policy action is currently in [developer preview](/docs/gateway/traffic-policy/actions/saml/). [Request access](https://dashboard.ngrok.com/early-access) to configure SAML via Traffic Policy. Once you have developer preview access, create a `policy.yaml` file with the following content. Leave `YOUR_IDP_METADATA_XML` as a placeholder for now; you replace it with the metadata XML from Azure AD B2C in a later step. ```yaml theme={null} on_http_request: - actions: - type: saml config: idp_metadata: 'YOUR_IDP_METADATA_XML' ``` The SAML action generates your ngrok service provider (SP) Entity ID and Assertion Consumer Service (ACS) URL based on your endpoint URL. Refer to the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/) for how to retrieve these values, which you enter into Azure AD B2C in the next step. Launch an endpoint with this policy: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding**. You use this URL to test the integration. ## 2. Create a SAML application in Azure AD B2C Azure AD B2C doesn't support SAML applications through user flows, so you configure it as a SAML identity provider with custom policies. Follow Microsoft's guide, [Configure Azure AD B2C as a SAML IdP to your applications](https://learn.microsoft.com/en-us/azure/active-directory-b2c/saml-service-provider), to set up the custom policies, register your SAML application, and expose the Azure AD B2C SAML metadata endpoint. When you register the application, use the following mapping to enter the ngrok values you retrieved in the previous step: | Azure AD B2C | ngrok | | - | - | | `RedirectURI` | `ACS URL` | | `identifierUri` | `EntityID` | | `samlMetadataUrl` | `SP Metadata` | ## 3. Update the ngrok configuration with the IdP metadata * Retrieve the IdP metadata XML from the Azure AD B2C SAML metadata endpoint. The URL follows this format: `https://.b2clogin.com/.onmicrosoft.com//Samlp/metadata`. * Update the `idp_metadata` value in your `policy.yaml` with the metadata XML, replacing the placeholder value you set earlier. * Restart the endpoint so it picks up the updated policy. ## 4. Test the integration This step assumes you have an app running locally (for example, at `localhost:3000`) with the ngrok agent installed. Run: `ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN` * Access your ngrok endpoint via your forwarding URL. * In your browser, launch an incognito window. * When prompted, log in or sign up. * After you log in, you see the application. # Secure Your Endpoints with Curity Identity Server SSO (OpenID Connect) Source: https://ngrok.com/docs/integrations/endpoint-sso/curity-sso-oidc Secure access to your ngrok endpoints with Curity Identity Server single sign-on (SSO) using OpenID Connect. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide walks you through configuring Curity as the primary Identity Provider for ngrok endpoints using single sign-on (SSO). By integrating Curity Identity Server with ngrok, you can: * Restrict access to ngrok endpoints to only users who authenticated via Curity * Use Curity security policies and multi-factor authentication (MFA) authenticators, including BankId, SITHS, and WebAuthn, to control access to ngrok endpoints * Use Curity's Dashboard to facilitate access to ngrok apps ## What you'll need * A Curity account with administrative rights to create clients. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with OpenID Connect. ## 1. Configure Curity In the Curity Identity Server admin UI, create an OAuth client that uses the code flow and OpenID Connect. For the full steps, including setting up an authenticator and generating a client secret, follow Curity's [Configure a Client](https://curity.io/resources/learn/configure-client/) tutorial. While configuring the client, set these values so it works with ngrok: * Add `https://idp.ngrok.com/oauth2/callback` as a **Redirect URI**. * Under **Client Authentication**, select **secret** and generate a secret. Copy the secret, since you can't retrieve it later. * Under **Scopes and Claims**, add the `openid` scope. * On the **Token Service** > **General** page, enable the **Expose Metadata** toggle. ngrok calls the `/.well-known/openid-configuration` endpoint at Curity to pull the configuration for your Authorization Server. Commit the changes via the **Changes** menu, then copy the **Client ID** and **Client Secret**. You use these values, along with your Curity issuer address (for example, `https://acme.com/oauth/v2/oauth-anonymous`), to configure ngrok. ## 2. Configure ngrok Secure your endpoint with the [OpenID Connect Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc). This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok agent installed. Create a `policy.yaml` file with the following content, replacing the placeholder values with those copied from Curity Identity Server: ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: 'https://acme.com/oauth/v2/oauth-anonymous' client_id: 'YOUR_CURITY_CLIENT_ID' client_secret: 'YOUR_CURITY_CLIENT_SECRET' scopes: - openid - profile - email ``` Start an ngrok endpoint with this policy: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding** (for example, `https://curity-sso-test.ngrok.app`). ## 3. Test the integration * In your browser, open an incognito window. * Access your ngrok endpoint (for example, `https://curity-sso-test.ngrok.app` or using a copied URL). * When prompted, log in with your Curity credentials. * After you log in, you see your web app. # Secure Your Endpoints with Descope SSO (OpenID Connect) Source: https://ngrok.com/docs/integrations/endpoint-sso/descope-sso-oidc Secure access to your ngrok endpoints with Descope single sign-on (SSO) using OpenID Connect. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide explains how to configure Descope as the primary Identity Provider for your ngrok endpoints using single sign-on (SSO). By integrating Descope SSO with ngrok, you can: * Restrict access to ngrok endpoints only to users authenticated via Descope * Use Descope security policies and multi-factor authentication (MFA) authenticators * Use the Descope Console to facilitate access to ngrok apps The ngrok integration with Descope supports service provider-initiated (SP-initiated) SSO. In this mode, users access ngrok endpoints and are redirected to Descope for authentication. ## What you'll need * A Descope account with administrative rights to a Descope Project. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with OpenID Connect. ## 1. Configure Descope In the Descope Console, create an OIDC application so Descope can act as the identity provider for ngrok. For the full steps, follow Descope's [OIDC Federated Applications](https://docs.descope.com/sso-integrations/applications/oidc-apps) guide. Gather the following values to configure ngrok: * **Issuer URL**: shown on the application page after you create the OIDC application. * **Client ID**: your Descope Project ID, found under **Project Settings** in the Descope Console. * **Client Secret**: a Descope Access Key. Go to **Access Keys**, click **+ Access Key**, name it `ngrok`, set an expiration, and click **Generate Key**. Copy the key, since it's shown only once. ## 2. Configure ngrok Secure your endpoint with the [OpenID Connect Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc). This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok agent installed. Create a `policy.yaml` file with the following content, replacing the placeholder values with those copied from Descope: ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: 'YOUR_DESCOPE_ISSUER_URL' client_id: 'YOUR_DESCOPE_PROJECT_ID' client_secret: 'YOUR_DESCOPE_ACCESS_KEY' scopes: - openid - profile - email ``` Start an ngrok endpoint with this policy: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding** (for example, `https://descope-sso-test.ngrok.app`). ## 3. Test the integration * In your browser, open an incognito window. * Access your ngrok endpoint (for example, `https://descope-sso-test.ngrok.app` or using the copied URL). * When prompted, log in with Descope. * After you log in, you see your web app. # Secure Your Endpoints with Descope SSO (SAML) Source: https://ngrok.com/docs/integrations/endpoint-sso/descope-sso-saml Secure access to your ngrok endpoints with Descope single sign-on (SSO) using SAML. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide walks you through configuring Descope as the primary Identity Provider (IdP) for ngrok endpoints using single sign-on (SSO). By integrating Descope SSO with ngrok, you can: * Restrict access to ngrok endpoints only to users authenticated via Descope * Use Descope security policies and multi-factor authentication (MFA) authenticators * Use the Descope Console to facilitate access to ngrok apps The ngrok integration with Descope supports service provider-initiated (SP-initiated) SSO. In this mode, users access ngrok endpoints and are redirected to Descope for authentication. ## What you'll need * A Descope project with administrative rights and an Enterprise license to be able to create SAML applications. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with SAML. ## 1. Create a SAML application in Descope Create a SAML application in Descope and download its IdP metadata by following Descope's guide, [SAML Federated Applications](https://docs.descope.com/identity-federation/applications/saml-apps). Name the application `ngrok`, and download the **Descope Metadata (XML)** file. You use this metadata in the next step, and you return to the application's **Service Provider (SP)** settings in step 3. ## 2. Configure ngrok The SAML Traffic Policy action is currently in [developer preview](/docs/gateway/traffic-policy/actions/saml/). [Request access](https://dashboard.ngrok.com/early-access) to configure SAML via Traffic Policy. Once you have developer preview access, create a `policy.yaml` file with the following content, replacing `YOUR_IDP_METADATA_XML` with the IdP metadata XML from Descope: ```yaml theme={null} on_http_request: - actions: - type: saml config: idp_metadata: 'YOUR_IDP_METADATA_XML' ``` The SAML action generates your ngrok service provider (SP) Entity ID and Assertion Consumer Service (ACS) URL based on your endpoint URL. Refer to the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/) for how to retrieve these values to complete your Descope configuration. ## 3. Configure Descope * Back in the Descope Console, under your **ngrok** application, select **Enter the connection details manually** under **Service Provider (SP)**. * Enter the following values in the input fields and save the configuration. Retrieve both values as described in the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/): * `ACS URL`: The ACS URL generated by the ngrok SAML action for your endpoint. * `Entity ID`: The SP Entity ID generated by the ngrok SAML action for your endpoint. ## 4. Start an endpoint This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok agent installed. Run the following command, replacing `3000` with your local web app port and `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding**. You use this URL to test the Descope authentication. ## 5. Test the integration * In your browser, launch an incognito window. * Access your ngrok endpoint (for example, `https://descope-sso-test.ngrok.app` or use the copied URL). * When prompted, log in with Descope. * After you log in, you see your web app. # Secure Your Endpoints with Frontegg SSO (OpenID Connect) Source: https://ngrok.com/docs/integrations/endpoint-sso/frontegg-sso-oidc Secure access to your ngrok endpoints with Frontegg single sign-on (SSO) using OpenID Connect. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide walks you through configuring Frontegg as the primary Identity Provider (IdP) for ngrok endpoints using single sign-on (SSO). By integrating Frontegg SSO with ngrok, you can: * Restrict access to ngrok endpoints only to users authenticated via Frontegg * Use Frontegg security policies and multi-factor authentication (MFA) authenticators * Use Frontegg's Dashboard to facilitate access to ngrok apps The ngrok integration with Frontegg supports service provider-initiated (SP-initiated) SSO. In this mode, users access ngrok endpoints and are redirected to Frontegg for authentication. ## What you'll need * A Frontegg account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with OpenID Connect. ## 1. Configure Frontegg In the Frontegg Portal, set up Frontegg's hosted login to act as an OIDC identity provider for ngrok. For the full steps, follow Frontegg's [Use OIDC](https://developers.frontegg.com/ciam/guides/management/frontegg-idp/via-oidc) guide. While configuring hosted login, set these values so it works with ngrok: * Under **Authentication** > **Login method**, add `https://idp.ngrok.com/oauth2/callback` to the **Hosted login** allowed redirect URLs and save. * Under **Env settings**, copy the **Client ID** and the **API key**. * On the **Domains** tab, copy the **Domain name**. You use the Client ID, API key, and Domain name to configure ngrok. Make sure the users who need access are added to the environment before you test. ## 2. Configure ngrok Secure your endpoint with the [OpenID Connect Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc). This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok agent installed. Create a `policy.yaml` file with the following content, replacing the placeholder values with those copied from Frontegg: ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: 'https://app-123456.frontegg.com' client_id: 'YOUR_FRONTEGG_CLIENT_ID' client_secret: 'YOUR_FRONTEGG_API_KEY' scopes: - openid - profile - email ``` Start an ngrok endpoint with this policy: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding** (for example, `https://frontegg-sso-test.ngrok.app`). ## 3. Test the integration * In your browser, open an incognito window. * Access your ngrok endpoint (for example, `https://frontegg-sso-test.ngrok.app` or using the copied endpoint URL). * When prompted, log in with your Frontegg credentials. * After you log in, you see your web app. # Secure Your Endpoints with Frontegg SSO (SAML) Source: https://ngrok.com/docs/integrations/endpoint-sso/frontegg-sso-saml Secure access to your ngrok endpoints with Frontegg single sign-on (SSO) using SAML. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide walks you through configuring Frontegg as the primary Identity Provider (IdP) for ngrok endpoints using single sign-on (SSO). By integrating Frontegg SSO with ngrok, you can: * Restrict access to ngrok endpoints only to users authenticated via Frontegg * Use Frontegg security policies and multi-factor authentication (MFA) authenticators * Use Frontegg's Dashboard to facilitate access to ngrok apps ## What you'll need * A Frontegg account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with SAML. ## 1. Create a SAML application in Frontegg and download the IdP metadata Configure Frontegg as a SAML identity provider by following Frontegg's guide, [Frontegg as IDP via SAML](https://developers.frontegg.com/ciam/guides/management/frontegg-idp/via-saml). ngrok generates the service provider (SP) Entity ID and Assertion Consumer Service (ACS) URL from your endpoint URL, which you don't have yet, so enter temporary values (such as `https://temporary`) for the entity ID and ACS URL for now. You replace them with the ngrok-generated values in step 3. Download the IdP metadata XML that Frontegg returns. You use it in the next step. ## 2. Configure ngrok The SAML Traffic Policy action is currently in [developer preview](/docs/gateway/traffic-policy/actions/saml/). [Request access](https://dashboard.ngrok.com/early-access) to configure SAML via Traffic Policy. Once you have developer preview access, create a `policy.yaml` file with the following content, replacing `YOUR_IDP_METADATA_XML` with the IdP metadata XML from Frontegg: ```yaml theme={null} on_http_request: - actions: - type: saml config: idp_metadata: 'YOUR_IDP_METADATA_XML' ``` The SAML action generates your ngrok SP Entity ID and ACS URL based on your endpoint URL. Refer to the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/) for how to retrieve these values to complete your Frontegg configuration. Launch an endpoint with this policy: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding**. You use this URL to test the integration. ## 3. Update the Frontegg SAML configuration Back in Frontegg, update the SAML configuration you created in step 1, replacing the temporary values with the ngrok values you retrieved from the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/): * Set the **Entity ID** to your ngrok **SP Entity ID**. * Set the **ACS URL** to your ngrok **ACS URL**. If your Frontegg environment uses hosted login, also add your ngrok forwarding URL (for example, `https://123456789.ngrok.app/`) as an allowed login URL. Refer to the same Frontegg guide for where to configure this. ## 4. Start an endpoint This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok agent installed. Run the following command, replacing `3000` with your local web app port and `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding**. You use this URL to test the Frontegg authentication. ## 5. Grant access to Frontegg users Frontegg controls who can sign in through the accounts and users you manage in the Frontegg Portal Backoffice. Create an account and add the users who need access to ngrok, then confirm those users are assigned to your environment. ## 6. Test the integration * In your browser, launch an incognito window. * Access your ngrok endpoint (for example, `https://frontegg-sso-test.ngrok.app` or using the copied endpoint URL). * When prompted, log in with your Frontegg credentials. * After you log in, you see your web app. # FusionAuth Endpoint SSO (OpenID Connect) Source: https://ngrok.com/docs/integrations/endpoint-sso/fusionauth-sso-oidc Use FusionAuth single sign-on (SSO) to secure access to ngrok endpoints. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide explains how to configure FusionAuth as the primary Identity Provider for ngrok endpoints using single sign-on (SSO). By integrating FusionAuth SSO with ngrok, you can: * Restrict access to ngrok endpoints only to users authenticated via FusionAuth * Use FusionAuth premium features and login methods, including Advanced Registration Forms, Passwordless Login, WebAuthn, and Advanced Threat Detection, to control access to ngrok endpoints The ngrok integration with FusionAuth supports OIDC-based SSO. In this mode, users access ngrok endpoints and are redirected to FusionAuth for authentication. ## What you'll need * A public-facing FusionAuth instance (for example, FusionAuth Cloud or a self-hosted installation). * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with OpenID Connect. * A local web app or application that needs to be protected by FusionAuth. ## 1. Configure FusionAuth In your FusionAuth instance, create an application that uses OAuth and OpenID Connect. For the full steps, follow FusionAuth's [Applications](https://fusionauth.io/docs/get-started/core-concepts/applications) guide. While configuring the application, set these values so it works with ngrok: * Under **Tenants** > **Your Tenant**, set the issuer to the URL of your FusionAuth instance (for example, `https://acme.fusionauth.io`). * On the application's **OAuth** tab, enable the **Authorization Code** and **Refresh Token** grants. * Add `https://idp.ngrok.com/oauth2/callback` to the **Authorized redirect URLs** field. Save the application, then copy the **Client Id** and **Client secret**. You use these values to configure ngrok. ## 2. Configure ngrok Secure your endpoint with the [OpenID Connect Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc). This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok agent installed. Create a `policy.yaml` file with the following content, replacing the placeholder values with those copied from FusionAuth: ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: 'https://acme.fusionauth.io' client_id: 'YOUR_FUSIONAUTH_CLIENT_ID' client_secret: 'YOUR_FUSIONAUTH_CLIENT_SECRET' scopes: - openid - profile - email ``` Start an ngrok endpoint with this policy: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding** (for example, `https://fusionauth-sso-test.ngrok.app`). ## 3. Test the integration * In your browser, open an incognito window. * Access your ngrok endpoint (for example, `https://fusionauth-sso-test.ngrok.app` or using a copied URL). * When prompted, log in with your FusionAuth credentials. * After you log in, you see your web app. # JumpCloud Endpoint SSO (OpenID Connect) Source: https://ngrok.com/docs/integrations/endpoint-sso/jumpcloud-sso-oidc Use JumpCloud OIDC to secure access to ngrok endpoints with single sign-on. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide explains how to configure JumpCloud as the primary Identity Provider for ngrok endpoints using single sign-on (SSO). By integrating JumpCloud SSO with ngrok, you can: * Restrict access to your ngrok endpoints to only users authenticated via JumpCloud * Use JumpCloud security policies and multi-factor authentication (MFA) authenticators * Use JumpCloud's Dashboard to facilitate access to ngrok apps The ngrok integration with JumpCloud supports service provider-initiated (SP-initiated) SSO. In this mode, users access ngrok endpoints and are redirected to JumpCloud for authentication. ## What you'll need * A JumpCloud account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with OpenID Connect. ## 1. Configure JumpCloud Create a custom OIDC application in JumpCloud by following [JumpCloud's SSO with OIDC guide](https://jumpcloud.com/support/sso-with-oidc). When you configure the application, use these ngrok-specific values: * **Redirect URI**: `https://idp.ngrok.com/oauth2/callback` * **Login URL**: the URL of your ngrok endpoint (for example, `https://myexample.ngrok.app`) * **Client Authentication Type**: Client Secret Basic After you activate the application, copy the **Client ID** and **Client Secret**. You use these values when you configure ngrok, along with the JumpCloud OIDC issuer URL `https://oauth.id.jumpcloud.com/`. Assign at least one user group to the application so those users can sign in through JumpCloud. Nobody can reach your endpoint through JumpCloud SSO until you assign a group. ## 2. Configure ngrok Use the [OpenID Connect Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc) to enforce JumpCloud authentication on your endpoint. This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok client installed. Create a `policy.yaml` file with the following content, replacing the placeholder values with those copied from JumpCloud: ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: 'https://oauth.id.jumpcloud.com/' client_id: 'YOUR_JUMPCLOUD_CLIENT_ID' client_secret: 'YOUR_JUMPCLOUD_CLIENT_SECRET' scopes: - openid - profile - email ``` Start an ngrok endpoint with this policy, replacing `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding** (for example, `https://jumpcloud-sso-test.ngrok.app`). ## 3. Test the integration * In your browser, launch an incognito window. * Access your ngrok endpoint (for example, `https://jumpcloud-sso-test.ngrok.app` or using the copied endpoint URL). * When prompted, log in with your JumpCloud credentials. * After you log in, you see your web app. # JumpCloud SSO (SAML) Source: https://ngrok.com/docs/integrations/endpoint-sso/jumpcloud-sso-saml Use JumpCloud SAML to secure access to ngrok endpoints with single sign-on. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide walks you through configuring JumpCloud as the primary Identity Provider (IdP) for ngrok endpoints using single sign-on (SSO). By integrating JumpCloud SSO with ngrok, you can: * Restrict access to ngrok endpoints only to users authenticated via JumpCloud * Use JumpCloud security policies and multi-factor authentication (MFA) authenticators * Use JumpCloud's Dashboard to facilitate access to ngrok apps ## What you'll need * A JumpCloud account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with SAML. ## 1. Create a custom SAML application in JumpCloud Create a custom SAML application and export its IdP metadata by following JumpCloud's guide, [SSO using Custom SAML Application Connectors](https://jumpcloud.com/support/sso-using-custom-saml-application-connectors). Name the application `ngrok saml`. ngrok generates the service provider (SP) Entity ID and Assertion Consumer Service (ACS) URL from your endpoint URL, which you don't have yet, so enter temporary values (such as `https://temporary`) for the **SP Entity ID** and **ACS URL** for now. You replace them with the ngrok-generated values in step 3. Export the metadata XML from JumpCloud. You use it in the next step. ## 2. Configure ngrok The SAML Traffic Policy action is currently in [developer preview](/docs/gateway/traffic-policy/actions/saml/). [Request access](https://dashboard.ngrok.com/early-access) to configure SAML via Traffic Policy. Once you have developer preview access, create a `policy.yaml` file with the following content, replacing `YOUR_IDP_METADATA_XML` with the IdP metadata XML from JumpCloud: ```yaml theme={null} on_http_request: - actions: - type: saml config: idp_metadata: 'YOUR_IDP_METADATA_XML' ``` The SAML action generates your ngrok SP Entity ID and ACS URL based on your endpoint URL. Refer to the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/) for how to retrieve your SP metadata XML, which you upload to JumpCloud in the next step. ## 3. Link JumpCloud with ngrok Back in JumpCloud, open your custom SAML application and upload the ngrok SP metadata XML you retrieved in the previous step. This replaces the temporary values with the ngrok-generated SP Entity ID and ACS URL. Save the application. ## 4. Start an endpoint This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok agent installed. Run the following command, replacing `3000` with your local web app port and `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding**. You use this URL to test the JumpCloud authentication. ## 5. Grant access to JumpCloud users JumpCloud restricts access to SSO apps through user group assignments. By default, apps created in JumpCloud have no group assignments, so nobody can use JumpCloud SSO to access ngrok until you assign a group to the app. Open your ngrok custom SAML app, assign the group whose members need access under the **User Groups** tab, and save. Add JumpCloud users to that group whenever they need access to the ngrok app. ## 6. Test the integration * In your browser, launch an incognito window. * Access your ngrok endpoint (for example, `https://jumpcloud-sso-test.ngrok.app` or using the copied endpoint URL). * When prompted, log in with your JumpCloud credentials. * After you log in, you see your web app. # miniOrange Endpoint SSO (OpenID Connect) Source: https://ngrok.com/docs/integrations/endpoint-sso/miniorange-sso-oidc Use miniOrange OIDC to secure access to ngrok endpoints with single sign-on. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide explains how to configure miniOrange as the primary Identity Provider for ngrok endpoints using single sign-on (SSO). By integrating miniOrange SSO with ngrok, you can: * Restrict access to your ngrok endpoints to only users authenticated via miniOrange * Use miniOrange security policies and multi-factor authentication (MFA) authenticators * Use miniOrange's Dashboard to facilitate access to ngrok apps The ngrok integration with miniOrange supports service provider-initiated (SP-initiated) SSO. In this mode, users access ngrok endpoints and are redirected to miniOrange for authentication. ## What you'll need * A miniOrange account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with OpenID Connect. ## 1. Configure miniOrange Add an OAuth2/OpenID Connect application in miniOrange by following [miniOrange's guide to adding an OAuth app](https://www.miniorange.com/iam/content-library/admin-docs/how-to-add-oauth-app). When you configure the application, enter `https://idp.ngrok.com/oauth2/callback` as the **Redirect-URL**. After you save the application, copy the following values, which you use when you configure ngrok: * **Client ID** and **Client Secret** from the application's edit page. * The **Discovery Endpoint** URL from the application's **OAuth Endpoints** page. Use this URL as the issuer URL in the next step, without the `/.well-known/openid-configuration` suffix (for example, `https://login.xecurify.com/moas/discovery/v2.0/abcd1234`). Create at least one miniOrange user and grant it access to the application so that user can sign in through miniOrange. ## 2. Configure ngrok Use the [OpenID Connect Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc) to enforce miniOrange authentication on your endpoint. This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok client installed. Create a `policy.yaml` file with the following content, replacing the placeholder values with those copied from miniOrange: ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: 'YOUR_MINIORANGE_DISCOVERY_ENDPOINT_URL' client_id: 'YOUR_MINIORANGE_CLIENT_ID' client_secret: 'YOUR_MINIORANGE_CLIENT_SECRET' scopes: - openid - profile - email ``` Start an ngrok endpoint with this policy, replacing `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding** (for example, `https://miniorange-sso-test.ngrok.app`). ## 3. Test the integration * In your browser, launch an incognito window. * Access your ngrok endpoint using the copied endpoint URL (for example, `https://miniorange-sso-test.ngrok.app`). * When prompted, log in with your miniOrange credentials. * After you log in, you see your web app. # miniOrange SSO (SAML) Source: https://ngrok.com/docs/integrations/endpoint-sso/miniorange-sso-saml Use miniOrange SAML to secure access to ngrok endpoints with single sign-on. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide walks you through configuring miniOrange as the primary Identity Provider (IdP) for ngrok endpoints using single sign-on (SSO). By integrating miniOrange SSO with ngrok, you can: * Restrict access to ngrok endpoints only to users authenticated via miniOrange * Use miniOrange security policies and multi-factor authentication (MFA) authenticators * Use miniOrange's Dashboard to facilitate access to ngrok apps ## What you'll need * A miniOrange account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with SAML. ## 1. Configure miniOrange In the miniOrange admin console, add a custom SAML application for ngrok. For the full steps, see miniOrange's guide to [adding a SAML app](https://www.miniorange.com/iam/content-library/admin-docs/how-to-add-saml-app). As you configure the app: * Enter temporary values such as `https://temporary` in both the **SP Entity ID or Issuer** and **ACS URL** fields. You replace them with ngrok's values in a later step. * Download the miniOrange IdP metadata XML. You use it in the ngrok policy below. ## 2. Configure ngrok The SAML Traffic Policy action is currently in [developer preview](/docs/gateway/traffic-policy/actions/saml/). [Request access](https://dashboard.ngrok.com/early-access) to configure SAML via Traffic Policy. Once you have developer preview access, create a `policy.yaml` file with the following content, replacing `YOUR_IDP_METADATA_XML` with the IdP metadata XML from miniOrange: ```yaml theme={null} on_http_request: - actions: - type: saml config: idp_metadata: 'YOUR_IDP_METADATA_XML' ``` The SAML action generates your ngrok service provider (SP) Entity ID and Assertion Consumer Service (ACS) URL from your endpoint URL. See the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/) for how to retrieve these values. This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok agent installed. Start an endpoint with this policy, replacing `3000` with your local web app port and `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding**. You use it to test miniOrange authentication. ## 3. Link miniOrange with ngrok * In the miniOrange admin console, open your ngrok SAML app and click **Edit**. * Replace the temporary values with the SP Entity ID and ACS URL that the SAML action generated. * Save the app. To sign in, use a miniOrange account that is assigned to the app. Create a test user in miniOrange if you need one. ## 4. Test the integration * In your browser, launch an incognito window. * Access your ngrok endpoint using the copied URL (for example, `https://miniorange-sso-test.ngrok.app`). * When prompted, log in with your miniOrange credentials. * After you log in, you see your web app. # Secure Your Endpoints with Okta SSO (OpenID Connect) Source: https://ngrok.com/docs/integrations/endpoint-sso/okta-sso-oidc Secure access to your ngrok endpoints with Okta single sign-on (SSO) using OpenID Connect. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide explains how to configure Okta as the primary Identity Provider for ngrok endpoints using single sign-on (SSO). By integrating Okta SSO with ngrok, you can: * Restrict access to your ngrok endpoints to only users authenticated via Okta * Use Okta security policies, multi-factor authentication (MFA) authenticators such as Okta Verify, FastPass, and FIDO2, and ThreatInsights to control access to ngrok endpoints * Use Okta's Dashboard to facilitate access to ngrok apps The ngrok integration with Okta supports service provider-initiated (SP-initiated) SSO. In this mode, users access ngrok endpoints and are redirected to Okta for authentication. ## What you'll need * An Okta account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with OpenID Connect. ## 1. Configure Okta Create an OpenID Connect app integration in Okta by following [Okta's guide to creating an app integration](https://developer.okta.com/docs/guides/create-an-app-integration/openidconnect/main/). When you configure the application, use these ngrok-specific values: * **Sign-in redirect URI**: `https://idp.ngrok.com/oauth2/callback` * **Login initiated by**: Login initiated by app After you create the application, open its **Sign On** tab and copy the **Client ID** and **Client Secret**. You use these values when you configure ngrok, along with your Okta org URL as the issuer URL (for example, `https://acme.okta.com`). Assign the users and groups that need access to the application so those users can sign in through Okta. To test the integration yourself, make sure you're assigned to the app. ## 2. Configure ngrok Use the [OpenID Connect Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc) to enforce Okta authentication on your endpoint. This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok client installed. Create a `policy.yaml` file with the following content, replacing the placeholder values with those copied from Okta: ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: 'https://acme.okta.com' client_id: 'YOUR_OKTA_CLIENT_ID' client_secret: 'YOUR_OKTA_CLIENT_SECRET' scopes: - openid - profile - email ``` Start an ngrok endpoint with this policy, replacing `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding** (for example, `https://okta-sso-test.ngrok.app`). ## 3. Test the integration * In your browser, launch an incognito window. * Access your ngrok endpoint (for example, `https://okta-sso-test.ngrok.app` or using a copied URL). * When prompted, log in with your Okta credentials. * After you log in, you see your web app. # Secure Your Endpoints with Okta SSO (SAML) Source: https://ngrok.com/docs/integrations/endpoint-sso/okta-sso-saml Secure access to your ngrok endpoints with Okta single sign-on (SSO) using SAML. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide shows you how to configure Okta as the primary Identity Provider (IdP) for ngrok endpoints using single sign-on (SSO). By integrating Okta SSO with ngrok, you can: * Restrict access to ngrok endpoints only to users authenticated via Okta * Use Okta security policies, multi-factor authentication (MFA) authenticators (Okta Verify, FastPass, FIDO2), and ThreatInsights to control access to ngrok endpoints * Use Okta's Dashboard to facilitate access to ngrok apps ## What you'll need * An Okta account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with SAML. ## 1. Configure Okta In the Okta Admin Console, create a SAML 2.0 app integration for ngrok with the App Integration Wizard. For the full steps, see Okta's guide to [creating SAML app integrations](https://help.okta.com/en-us/content/topics/apps/apps_app_integration_wizard_saml.htm). As you configure the app: * Enter temporary values such as `https://temporary` for the **Single sign-on URL** and the **Audience URI (SP Entity ID)**, and set **Name ID format** to **EmailAddress**. You replace the temporary values with ngrok's values in a later step. * After you finish the wizard, open the app's **Sign On** tab and download the IdP metadata XML from the active SHA-2 certificate's **View IdP metadata** action. You use it in the ngrok policy below. Okta restricts access to the app through assignments, and new apps start with none. On the app's **Assignments** tab, assign the users and groups who should reach ngrok, and make sure your own account is assigned so you can test the integration. ## 2. Configure ngrok The SAML Traffic Policy action is currently in [developer preview](/docs/gateway/traffic-policy/actions/saml/). [Request access](https://dashboard.ngrok.com/early-access) to configure SAML via Traffic Policy. Once you have developer preview access, create a `policy.yaml` file with the following content, replacing `YOUR_IDP_METADATA_XML` with the IdP metadata XML from Okta: ```yaml theme={null} on_http_request: - actions: - type: saml config: idp_metadata: 'YOUR_IDP_METADATA_XML' ``` The SAML action generates your ngrok service provider (SP) Entity ID and Assertion Consumer Service (ACS) URL from your endpoint URL. See the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/) for how to retrieve these values. Start an endpoint with this policy, replacing `3000` with your local web app port and `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding**. You use it to test the integration. ## 3. Update the Okta app settings * In Okta, open your ngrok app and go to the **General** tab. * In **SAML Settings**, click **Edit**, then replace the temporary values with the generated ngrok values: * Set **Single sign-on URL** to your ngrok **ACS URL**. * Set **Audience URI (SP Entity ID)** to your ngrok **SP Entity ID**. * Save the changes. ## 4. Test the integration * In your browser, launch an incognito window. * Access your ngrok endpoint (for example, `https://okta-sso-test.ngrok.app` or a copied URL). * When prompted, log in with your Okta credentials. * After you log in, you see your web app. # Secure ngrok Endpoints Using Salesforce OpenID Connect Source: https://ngrok.com/docs/integrations/endpoint-sso/salesforce-sso-oidc Use Salesforce's managed applications to add single sign-on (SSO) for your ngrok resources. This guide shows you how to use Salesforce as: * An OAuth provider, along with the ngrok OIDC Traffic Policy action, to secure your ngrok endpoints * The primary identity provider (IdP) for your endpoints The OIDC Traffic Policy action supplements an ID token that identifies users who log in to your endpoint. Because Salesforce isn't currently a supported OAuth provider in ngrok, this approach lets you use its OAuth capabilities and add an extra layer of authorization to your endpoints. ## Why use OIDC to secure your endpoints The [OIDC Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc) lets you control access to your upstream services and configure routing based on the information that ngrok stores about a user's authentication and authorization status. ## Why use Salesforce as an IdP Using Salesforce OAuth lets you provide single sign-on (SSO) for users of your application, API, or database without separate credentials. ngrok handles the authentication transparently. The people in charge of security and administration at your organization can manage all external application integrations from one place. ## What you'll need * A Salesforce developer account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure an endpoint with Traffic Policy and OpenID Connect. ## 1. Create a Salesforce external client app Create an external client app in Salesforce by following [Salesforce's guide to creating an external client app](https://help.salesforce.com/s/articleView?id=xcloud.create_a_local_external_client_app.htm\&language=en_US\&type=5). When you enable and configure OAuth for the app, use these ngrok-specific settings: * **Callback URL**: `https://idp.ngrok.com/oauth2/callback` * **OAuth Start URL**: the URL of the Cloud Endpoint you create in the next step * Select the `openid`, `profile`, and `email` scopes. * Under **Permitted Users**, allow all users to self authorize. * Require the secret for the Web Server flow and the Refresh Token flow. * Leave **Require Proof Key for Code Exchange (PKCE)** disabled, because it causes an error when you log in to your endpoint. After you save the app, open its **OAuth Settings** and copy the **Client ID** (consumer key) and **Client Secret** (consumer secret). Your issuer URL follows the format `https://YOUR_DOMAIN-dev-ed.develop.my.salesforce.com`. You use these three values in the next step. ## 2. Create a Cloud Endpoint and add the OIDC Traffic Policy action Log in to your ngrok dashboard and go to the **Endpoints** section. Click **Create Endpoint**, select **Cloud Endpoint**, and follow the steps in the [Cloud Endpoints quickstart](/docs/getting-started/cloud-endpoints-quickstart/#3-create-your-cloud-endpoint-in-the-dashboard) to create your endpoint. Add the following Traffic Policy to your endpoint, replacing the placeholder values with the ones you copied from Salesforce: ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: 'https://YOUR_DOMAIN-dev-ed.develop.my.salesforce.com' client_id: 'YOUR_SALESFORCE_CLIENT_ID' client_secret: 'YOUR_SALESFORCE_CLIENT_SECRET' scopes: - openid - profile - email ``` Click **Save**. Your Cloud Endpoint now uses the OIDC Traffic Policy action with Salesforce as the IdP. ## (Optional) Configure an Agent Endpoint from the CLI You can start an Agent Endpoint with the same policy instead of a Cloud Endpoint. To learn when to use Agent versus Cloud Endpoints, see the [documentation on endpoints](/docs/gateway/endpoints/). Save the Traffic Policy above to a `policy.yaml` file, then start an Agent Endpoint with it, replacing `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` # Wallix Trustelem Endpoint SSO (OpenID Connect) Source: https://ngrok.com/docs/integrations/endpoint-sso/trustelem-sso-oidc Use Wallix Trustelem OIDC to secure access to ngrok endpoints with single sign-on. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide explains how to configure Wallix Trustelem as the primary Identity Provider for ngrok endpoints using single sign-on (SSO). By integrating Wallix Trustelem SSO with ngrok, you can: * Restrict access to your ngrok endpoints to only users authenticated via Wallix Trustelem * Use Wallix Trustelem security policies and multi-factor authentication (MFA) authenticators * Use Wallix Trustelem's Dashboard to facilitate access to ngrok apps The ngrok integration with Wallix Trustelem supports service provider-initiated (SP-initiated) SSO. In this mode, users access ngrok endpoints and are redirected to Wallix Trustelem for authentication. ## What you'll need * A Wallix Trustelem account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with OpenID Connect. ## 1. Configure Wallix Trustelem Create an OpenID Connect application in Wallix Trustelem by following [Trustelem's OpenID Connect documentation](https://trustelem-doc.wallix.com/books/trustelem-applications/page/openid-connect). When you configure the application, use these ngrok-specific values: * **RedirectURI**: `https://idp.ngrok.com/oauth2/callback` * **Login URL**: the URL of your ngrok endpoint (for example, `https://trustelem-sso-test.ngrok.app`) After you save the application, copy the **ClientID**, **ClientSecret**, and **Issuer** URL. You use these values when you configure ngrok. Create at least one Wallix Trustelem user and grant it access to the application so that user can sign in through Wallix Trustelem. ## 2. Configure ngrok Use the [OpenID Connect Traffic Policy action](/docs/gateway/traffic-policy/actions/oidc) to enforce Wallix Trustelem authentication on your endpoint. This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok client installed. Create a `policy.yaml` file with the following content, replacing the placeholder values with those copied from Wallix Trustelem: ```yaml theme={null} on_http_request: - actions: - type: openid-connect config: issuer_url: 'YOUR_TRUSTELEM_ISSUER_URL' client_id: 'YOUR_TRUSTELEM_CLIENT_ID' client_secret: 'YOUR_TRUSTELEM_CLIENT_SECRET' scopes: - openid - profile - email ``` Start an ngrok endpoint with this policy, replacing `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding** (for example, `https://trustelem-sso-test.ngrok.app`). ## 3. Test the integration * In your browser, launch an incognito window. * Access your ngrok endpoint (for example, `https://trustelem-sso-test.ngrok.app` or using the copied endpoint URL). * When prompted, log in with your Wallix Trustelem credentials. * After you log in, you see your web app. # Wallix Trustelem SSO (SAML) Source: https://ngrok.com/docs/integrations/endpoint-sso/trustelem-sso-saml Use Wallix Trustelem SAML to secure access to ngrok endpoints with single sign-on. This guide refers to using SSO to authenticate access to your **endpoints**. You cannot use these instructions to set up SSO for logging into your ngrok account in the dashboard. This guide walks you through configuring Wallix Trustelem as the primary Identity Provider (IdP) for ngrok endpoints using single sign-on (SSO). By integrating Wallix Trustelem SSO with ngrok, you can: * Restrict access to ngrok endpoints only to users authenticated via Wallix Trustelem * Use Wallix Trustelem security policies and multi-factor authentication (MFA) authenticators * Use Wallix Trustelem's Dashboard to facilitate access to ngrok apps ## What you'll need * A Wallix Trustelem account with administrative rights to create apps. * An ngrok Pay-as-you-go account with an authtoken or admin access to configure endpoints with SAML. ## 1. Configure Wallix Trustelem In the WALLIX Trustelem administration console, add a SAML 2 application from the generic models and download its metadata XML. See WALLIX's [SAML 2 application reference](https://trustelem-doc.wallix.com/books/trustelem-applications/page/saml-2) for the configuration details. You upload this metadata to ngrok in the next step, then return to the app to enter ngrok's service provider (SP) Entity ID and Assertion Consumer Service (ACS) URL. ## 2. Configure ngrok The SAML Traffic Policy action is currently in [developer preview](/docs/gateway/traffic-policy/actions/saml/). [Request access](https://dashboard.ngrok.com/early-access) to configure SAML via Traffic Policy. Once you have developer preview access, create a `policy.yaml` file with the following content, replacing `YOUR_IDP_METADATA_XML` with the IdP metadata XML from Wallix Trustelem: ```yaml theme={null} on_http_request: - actions: - type: saml config: idp_metadata: 'YOUR_IDP_METADATA_XML' ``` The SAML action generates your ngrok SP Entity ID and ACS URL from your endpoint URL. See the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/) for how to retrieve these values. This step assumes you have an app running locally (for example, on `localhost:3000`) with the ngrok agent installed. Start an endpoint with this policy, replacing `3000` with your local web app port and `YOUR_DOMAIN` with your ngrok domain: ```bash theme={null} ngrok http 3000 --traffic-policy-file policy.yaml --url YOUR_DOMAIN ``` Copy the URL next to **Forwarding**. You use it to test Wallix Trustelem authentication. ## 3. Link Wallix Trustelem with ngrok * In the WALLIX Trustelem administration console, click **Apps** and then click your application. * On the **Settings** popup, click **Edit**, then paste your ngrok **SP Entity ID** in the **EntityID** field and your ngrok **ACS URL** in the **Assertion Consumer Service** field. * Click **Save**. To sign in, use a Wallix Trustelem account that can reach the app. Create a test user in Trustelem if you need one. ## 4. Test the integration * In your browser, launch an incognito window. * Access your ngrok endpoint (for example, `https://trustelem-sso-test.ngrok.app` or the copied URL). * When prompted, log in with your Wallix Trustelem credentials. * After you log in, you see your web app. # Send Network Logs from ngrok to AWS CloudWatch Source: https://ngrok.com/docs/integrations/event-destinations/amazon-cloudwatch-event-destination Send network traffic logs from ngrok to AWS CloudWatch. This guide walks you through sending ngrok events, including network traffic logs, to AWS CloudWatch. You may want to keep an audit log of configuration changes in your ngrok account, record all traffic to your endpoints for active monitoring and troubleshooting, or use AWS CloudWatch as a SIEM for security inspections. By integrating ngrok with CloudWatch, you can: * Quickly identify application issues in real time using ngrok request events and CloudWatch visibility. * Historically audit changes occurring in an account. * Profile usage of your service using CloudWatch queries and analytic charts. * Identify security issues using ngrok events. ## 1. Obtain CloudWatch log group ARN For ngrok to send events to CloudWatch, a log group ARN is required. To create or retrieve the ARN, see the [AWS CloudWatch documentation for working with log groups and streams](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html). You may also want to reference the [AWS IAM documentation on using external IDs with cross-account roles](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html). ## 2. Create a log export * In a browser, go to [**Log Exporting**](https://dashboard.ngrok.com/log-exporting) in the ngrok dashboard. * Click **+ New Log Export**. * Enter a description in the **Description** field. * In the **Sources** tab, click **Add Source** to choose which events to send to CloudWatch. * Make your selections from the list, then click **Add Event Sources** to confirm. To capture traffic events only from specific endpoints or tunnels, add a CEL filter expression when configuring the source. For example, to filter by hostname: `ev.conn.server_name == "your-tunnel.ngrok.app"`. See [Log Sources filters](/docs/obs/events#filters) for more examples and filter syntax. ## 3. Create event destination To send events to CloudWatch, assign an Event Destination to the Log Export. * In the **Destinations** tab, click **Add Destination**. * Choose AWS CloudWatch Logs as the target and fill in the required information: * **Log Group ARN** * **Description** (optional) * **Create IAM Role:** An IAM role is required so ngrok can stream logs to CloudWatch. The dashboard provides two options: download and run the **CLI Script** to create the role automatically (the fastest option), or use the **API** tab's displayed policy JSON to create it manually via the AWS console or tools like Terraform or Pulumi. Enter the resulting role ARN to complete the destination setup. **Security best practice:** If configuring your IAM role manually, ensure that you configure the Trust Policy with a condition that includes the ExternalId. This ensures that the only data AWS can ingest is from your ngrok account. If you configure the IAM role with the CLI script, this is done automatically. * When all required inputs have values, click **Send Test Event**. * You should see a Success message. * Click **Done** to complete the CloudWatch Event Destination setup. # Send Network Logs from ngrok to AWS Firehose Source: https://ngrok.com/docs/integrations/event-destinations/amazon-firehose-event-destination Send network traffic logs from ngrok to AWS Firehose. This guide explains how to send ngrok events, including network traffic logs, to AWS Firehose. You may want to keep an audit log of configuration changes in your ngrok account, record all traffic to your endpoints for active monitoring and troubleshooting, or use AWS Firehose as a SIEM for security inspections. By integrating ngrok with AWS Firehose, you can: * Quickly identify application issues in real time using ngrok request events and Firehose data processing. * Historically audit changes occurring in an account. * Profile usage of your service using Firehose queries and real-time data analytics. * Identify security issues using ngrok events. ## 1. Obtain Firehose delivery stream ARN For ngrok to send events to AWS Firehose, a delivery stream ARN is required. To create the stream, see the [AWS Firehose documentation for creating a delivery stream](https://docs.aws.amazon.com/firehose/latest/dev/basic-create.html). ngrok is a **Direct PUT** source type, and you can choose any Firehose destination you want. For testing, creating an S3 bucket as the destination may be easiest. After you create your AWS Firehose delivery stream, keep the Delivery Stream ARN for [Step 3](#3-create-event-destination). ## 2. Create a log export * In a browser, go to [**Log Exporting**](https://dashboard.ngrok.com/log-exporting) in the ngrok dashboard. * Click **+ New Log Export**. * Enter a description in the **Description** field. * In the **Sources** tab, click **Add Source** to choose which events to send to Firehose. For information about events, see the [ngrok event documentation](/docs/obs/events/reference/). * Make your selections from the list, then click **Add Event Sources** to confirm. To capture traffic events only from specific endpoints or tunnels, add a CEL filter expression when configuring the source. For example, to filter by hostname: `ev.conn.server_name == "your-tunnel.ngrok.app"`. See [Log Sources filters](/docs/obs/events#filters) for more examples and filter syntax. ## 3. Create event destination To send events to Firehose, assign an Event Destination to the Log Export. * In the **Destinations** tab, click **Add Destination**. * Choose AWS Firehose as the target and fill in the required information: * **Delivery Stream ARN** * **Description** (optional) * **Create IAM Role:** An IAM role is required so ngrok can stream logs to Firehose. The dashboard provides two options: download and run the **CLI Script** to create the role automatically (the fastest option), or use the **API** tab's displayed policy JSON to create it manually via the AWS console or tools like Terraform or Pulumi. Enter the resulting role ARN to complete the destination setup. **Security best practice:** If configuring your IAM role manually, ensure that you configure the Trust Policy with a condition that includes the ExternalId. This ensures that the only data AWS can ingest is from your ngrok account. If you configure the IAM role with the CLI script, this is done automatically. * When all required inputs have values, click **Send Test Event**. * You should see a Success message. * Click **Done** to complete the Firehose Event Destination setup. # Send Network Logs from ngrok to AWS Kinesis Source: https://ngrok.com/docs/integrations/event-destinations/amazon-kinesis-event-destination Send network traffic logs from ngrok to AWS Kinesis. This guide shows you how to send ngrok events, including network traffic logs, to AWS Kinesis. You may want to keep an audit log of configuration changes in your ngrok account, record all traffic to your endpoints for active monitoring and troubleshooting, or use AWS Kinesis as a SIEM for security inspections. By integrating ngrok with Kinesis, you can: * Quickly identify application issues in real time using ngrok request events and Kinesis data processing. * Historically audit changes occurring in an account. * Profile usage of your service using Kinesis queries and real-time data analytics. * Identify security issues using ngrok events. ## 1. Obtain Kinesis data stream ARN For ngrok to send events to Kinesis, a data stream ARN is required. To create the AWS Kinesis Data Stream and retrieve the ARN, see the [AWS documentation for creating a Kinesis Data Stream](https://docs.aws.amazon.com/streams/latest/dev/amazon-kinesis-streams.html). ## 2. Create a log export * In a browser, go to [**Log Exporting**](https://dashboard.ngrok.com/log-exporting) in the ngrok dashboard. * Click **+ New Log Export**. * Enter a description in the **Description** field. * In the **Sources** tab, click **Add Source** to choose which events to send to Kinesis. * Make your selections from the list, then click **Add Event Sources** to confirm. To capture traffic events only from specific endpoints or tunnels, add a CEL filter expression when configuring the source. For example, to filter by hostname: `ev.conn.server_name == "your-tunnel.ngrok.app"`. See [Log Sources filters](/docs/obs/events#filters) for more examples and filter syntax. ## 3. Create event destination To send events to Kinesis, assign an Event Destination to the Log Export. * In the **Destinations** tab, click **Add Destination**. * Choose AWS Kinesis as the target and fill in the required information: * **Stream ARN** * **Description** (optional) * **Create IAM Role:** An IAM role is required so ngrok can stream logs to Kinesis. The dashboard provides two options: download and run the **CLI Script** to create the role automatically (the fastest option), or use the **API** tab's displayed policy JSON to create it manually via the AWS console or tools like Terraform or Pulumi. Enter the resulting role ARN to complete the destination setup. **Security best practice:** If configuring your IAM role manually, ensure that you configure the Trust Policy with a condition that includes the ExternalId. This ensures that the only data AWS can ingest is from your ngrok account. If you configure the IAM role with the CLI script, this is done automatically. * When all required inputs have values, click **Send Test Event**. * You should see a Success message. * Click **Done** to complete the Kinesis Event Destination setup. # Integrate with the Azure Logs Ingestion API Source: https://ngrok.com/docs/integrations/event-destinations/azure-logs-event-destination Send network traffic logs from ngrok to Azure Logs Ingestion. This guide explains how to send ngrok events, including network traffic logs, to Azure via the Logs Ingestion API. This is useful if you want to keep an audit log of configuration changes in your ngrok account, record all traffic to your endpoints for active monitoring and troubleshooting, or use it as a SIEM for security inspections. By integrating ngrok with Azure, you can: * Quickly identify application issues in real time using ngrok request events and Azure logs processing. * Historically audit changes occurring in an account. * Profile usage of your service using logs queries and real-time data analytics. * Identify security issues using ngrok events. ## 1. Create a Log Analytics workspace These steps were adapted from the Microsoft documentation for creating a Log Analytics workspace. * In a browser, sign in to the Azure portal. * In the search bar, type **Log Analytics Workspaces**. * Click the **Services** entry (not the Marketplace entry). * Click **Create** on the top bar of the Log Analytics Workspace page. * Follow the wizard to create your Log Analytics Workspace, filling in the region, name, and resource group, then click **Review + Create**. These values can be anything you like and do not affect ngrok's ability to send logs to your Azure account. * Click **Create** at the bottom of the review step to provision the Log Analytics Workspace. You now have a **Log Analytics Workspace**, which will be the home for your data collection endpoint, tables, and rules. ## 2. Create a data collection endpoint These steps were adapted from the Microsoft documentation for creating a data collection endpoint. * In the search bar, type **Data Collection Endpoints**. * Click the **Services** entry. * Click **Create** on the top bar of the Data Collection Endpoints page. * Follow the wizard to create your Data Collection Endpoint, filling in the region, name, and resource group, then click **Review + Create**. These fields can be anything you like and do not affect ngrok's ability to send logs to your Azure account. * Click **Create** at the bottom of the review step to provision the Data Collection Endpoint. You now have a **Data Collection Endpoint**, which is the network-accessible service that ngrok uses to send events to Azure. ## 3. Create a DCR-based custom table in the workspace These steps were adapted from the Microsoft documentation for creating a new table in a Log Analytics workspace. * Navigate to the **Log Analytics Workspaces** list again. * Click the workspace you created in [Step 1](#1-create-a-log-analytics-workspace). * In the sidebar of the selected workspace, go to **Settings** > **Tables**. * Click **Create** on the top bar and click **New custom log (DCR-based)**. * Populate the table name with a name of your choice and the DCE field with the existing DCE you created in [Step 2](#2-create-a-data-collection-endpoint). * Under the Data collection rule field, click **Create a new data collection rule**, which opens a drawer. Fill out the resource group and name, then click **Done** in the drawer. * Click **Next** in the table creation wizard. * Upload the following JSON file using the wizard. After uploading, you will see a warning header "TimeGenerated field is not found in the sample provided"; this is expected. ```json theme={null} { "event_id": "ev_2iKcXvmLJoZojWzrCuLbstBCle0", "event_type": "test", "event_timestamp": "2024-06-24T15:25:52Z", "object": {} } ``` **Not to worry!** You will see a warning header "TimeGenerated field is not found in the sample provided"; this is expected. You can fix this using the **Transformation Editor**. * Click the **Transformation editor** button on the top bar of the wizard to open a drawer. * Paste in the following transformation and click **Run**. ``` source | extend TimeGenerated = event_timestamp ``` * Click **Apply**, then **Next**, then **Create**. You now have a **Data Collection Rule** configured for ngrok events, along with a table where the data will be stored. ## 4. Create a Microsoft Entra Application These steps were adapted from the Microsoft documentation for creating a Microsoft Entra Application. * In the search bar, type **Microsoft Entra ID**. * Under **Services**, click **Microsoft Entra ID** (not the Marketplace item). * In the sidebar, go to **Manage** > **App registrations**. * Click **New registration**. * Name the application **ngrok-events** or something similar to clarify its use; this entity is what ngrok uses to authenticate with your data collection endpoint. * For account type, choose **Accounts in this organizational directory only**. * Click **Register**. You have now created a **Microsoft Entra ID App Registration**, which is a service principal that grants roles and access to services like ngrok. ## 5. Assign IAM permissions to the Application for the DCR These steps were adapted from the Microsoft documentation for assigning permissions to the DCR. * In the search bar, type **Data collection rules**. * Under **Services**, click **Data collection rules**. * Click the Data collection rule you created in [Step 3](#3-create-a-dcr-based-custom-table-in-the-workspace). * In the sidebar, click **Access control (IAM)**. * Click **Add** on the top bar. * Click **Add role assignment**. * Under **Job function roles**, search for **Monitoring**. * Click **Monitoring Metrics Publisher**. * Click **Next** to go to Members. * Click **Select members**. * Search for the app registration you created in [Step 4](#4-create-a-microsoft-entra-application). * Click the service principal and click **Select**. * Click **Review + assign**, then **Review + assign** again to confirm the role assignment. You have now granted the ngrok application access to ingest logs into the DCR, which is the final step before creating an Event Destination. ## 6. Gather necessary data for event destination To create an event destination in the ngrok dashboard, gather the following from what you created in Azure: * Tenant ID * Application's Client ID * Application's Client Secret * DCR immutable ID * DCR stream name * DCE log ingestion URI * To get the first three fields, go back to **Microsoft Entra ID**. In the top search bar, search for **Entra** and click **Microsoft Entra ID**. * In the sidebar, go to **Manage** > **App registrations** and click the application you created in [Step 4](#4-create-a-microsoft-entra-application). * On the Overview page, copy **Application (client) ID** and **Directory (tenant) ID**. * In the sidebar, go to **Manage** > **Certificates & secrets**. * Click **New client secret**. * Fill in the description and expiry date, then click **Add**. * Copy the secret value provided by Azure. This value is only shown once; it will not be available after you navigate away. * To get the DCR immutable ID and stream name, navigate to **Data collection rules** using the top search bar. * Click the Data collection rule you created in [Step 3](#3-create-a-dcr-based-custom-table-in-the-workspace). * In the **Overview** tab, copy the **Immutable Id** value. * In the sidebar, go to **Configuration** > **Data sources**. * Copy the **Data source** name, which starts with "Custom-" (for example, `Custom-NgrokEvents_CL`). This is the **DCR stream name**. Use this value exactly as shown in Azure; passing a different format (such as the table name) results in a `400 InvalidStream` error from the Logs Ingestion API. * In the top search bar, navigate to **Data collection endpoints**. * Click the Data collection endpoint you created in [Step 2](#2-create-a-data-collection-endpoint). * In the **Overview** tab, copy the **Logs Ingestion URI**. You now have all the required data to create an event destination with ngrok. ## 7. Create the new event destination in ngrok At this point, you can create the event destination [through the ngrok dashboard](#creating-through-the-ngrok-dashboard) or [via the ngrok API](#creating-via-the-ngrok-api). ### Creating through the ngrok dashboard * Sign in to the ngrok dashboard and go to [**Log Exporting**](https://dashboard.ngrok.com/log-exporting). * Click **+ New Log Export**. * In the **Sources** tab, click **Add Source** to choose which events to send to Azure. To capture traffic events only from specific endpoints or tunnels, add a CEL filter expression when configuring the source. For example, to filter by hostname: `ev.conn.server_name == "your-tunnel.ngrok.app"`. See [Log Sources filters](/docs/obs/events#filters) for more examples and filter syntax. * In the **Destinations** tab, click **Add Destination**. * Click **Azure Logs Ingestion API**. * Fill in the fields using the values you collected in Step 6. * Click **Send Test Event** and look for an example event in your Azure account. It may take a minute to appear due to propagation delays. * Click **Done** and save the new Log Export. ### Creating via the ngrok API * Create an API key with ngrok (for example via the ngrok dashboard). * Using your REST API tool of choice (cURL, Postman, etc.), run the following API calls. * To create the Azure Logs Ingestion event destination, run: ```bash theme={null} curl --location 'https://api.ngrok.com/event_destinations' \ --header 'Accept: application/json' \ --header 'Ngrok-Version: 2' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer {API_KEY}' \ --data '{ "format": "json", "target": { "azure_logs_ingestion": { "tenant_id": "{TENANT_ID}", "client_id": "{CLIENT_ID}", "client_secret": "{CLIENT_SECRET}", "logs_ingestion_uri": "{DCE_LOGS_INGESTION_URI}", "data_collection_rule_id": "{DCR_RULE_ID}", "data_collection_stream_name": "{DCR_STREAM_NAME}" } } }' ``` Copy the returned event destination ID from the response. * To create a log export (event subscription) using the event destination you created, run: ```bash theme={null} curl \ -X POST \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{ "description":"ip policy creations", "destination_ids":["{EVENT_DESTINATION_ID}"], "sources":[ {"type":"ip_policy_created.v0"}, {"type":"{MORE_EVENT_TYPES}"} ] }' \ https://api.ngrok.com/event_subscriptions ``` After you get a 200 response, your event destination is configured and subscribed to the set of event types you specified. # Send Network Logs from ngrok to Datadog Source: https://ngrok.com/docs/integrations/event-destinations/datadog-event-destination Send network traffic logs from ngrok to Datadog. This guide walks you through sending ngrok events, including network traffic logs, to Datadog. You may want to keep an audit log of configuration changes in your ngrok account, record all traffic to your endpoints for active monitoring and troubleshooting, or use Datadog as a SIEM for security inspections. By integrating ngrok with Datadog, you can: * Quickly identify application issues in real time using ngrok Traffic Events with Datadog ML-based monitoring. * Historically audit changes occurring in an account. * Visualize usage of your service using Datadog queries and analytic charts. * Identify security issues using ngrok events and Datadog SIEM. ngrok sends logs to the Datadog Logs API and automatically compresses them with gzip. ngrok does not support the events or metrics APIs at this time. ## What you'll need * Familiarity with the ngrok [events](/docs/obs/events/reference) and which ones you want to send to Datadog. * Your Datadog site confirmed. * A Datadog API key. ## 1. Create a log export * In a browser, go to [**Log Exporting**](https://dashboard.ngrok.com/log-exporting) in the ngrok dashboard. * Click **+ New Log Export**. * Enter a description in the **Description** field. * In the **Sources** tab, click **Add Source** to choose which events to send to Datadog. * Make your selections from the list, then click **Add Event Sources** to confirm. To capture traffic events only from specific endpoints or tunnels, add a CEL filter expression when configuring the source. For example, to filter by hostname: `ev.conn.server_name == "your-tunnel.ngrok.app"`. See [Log Sources filters](/docs/obs/events#filters) for more examples and filter syntax. ## 2. Create event destination To send events to Datadog, assign an Event Destination to the Log Export. * In the **Destinations** tab, click **Add Destination**. * Choose Datadog as the target and fill in the required information, including which Datadog site your data uses. **Target**, **Region**, and **API Key** are required; other values are optional: * **Service Name** is added as a key-value pair to the event payload as `service:value`. * **DD Tags** values are added as tags to the event data sent to Datadog. * When all required inputs have values, click **Send Test Event**. * You should see a Success message. * Click **Done** to complete the Datadog Event Destination setup. **Troubleshooting:** If you receive a 403 error instead of a Success message, double-check your API key and confirm that you selected the correct Datadog site. # How to access Home Assistant from anywhere with ngrok Source: https://ngrok.com/docs/integrations/guides/home-assistant-with-ngrok Expose your Home Assistant instance with ngrok and access your dashboard over the internet. This guide walks you through setting up Home Assistant with ngrok. This combination lets you access your Home Assistant dashboard over the public internet. ## What you'll need * [Docker Desktop](https://www.docker.com/products/docker-desktop/) and [Docker Compose](https://docs.docker.com/compose/install/) installed. * [ngrok](https://download.ngrok.com) installed on your machine. * Basic familiarity with the command line. ## 1. Run Home Assistant This guide runs Home Assistant in Docker, but the same ngrok setup works with any installation method. If you're starting from scratch, follow the [Home Assistant installation docs](https://www.home-assistant.io/installation/) and choose the Container method to run it with Docker Compose. Create a directory called `home-assistant`, then inside it create a `config` directory and a `compose.yaml` file similar to this: ```yaml theme={null} services: homeassistant: image: "ghcr.io/home-assistant/home-assistant:stable" volumes: - /etc/localtime:/etc/localtime:ro - /run/dbus:/run/dbus:ro # point this at the config directory you created above - /path/to/home-assistant/config:/config restart: unless-stopped ports: - 8123:8123 privileged: true ``` Start the container: ```bash theme={null} docker compose up -d ``` Visit `localhost:8123` in your browser to confirm you see the Home Assistant login page. ## 2. Configure ngrok to expose Home Assistant Add ngrok to your `compose.yaml` under the services section: ```yaml theme={null} services: ngrok: image: ngrok/ngrok:latest command: - "http" - "http://host.docker.internal:8123" ports: # you can choose different ports here if you want - 4040:4040 environment: # find this at https://dashboard.ngrok.com/get-started/your-authtoken NGROK_AUTHTOKEN: YOUR_AUTH_TOKEN ``` In your terminal, restart your Docker container with this `compose.yaml` file: ```bash theme={null} docker compose up -d ``` Visit your [ngrok dashboard](https://dashboard.ngrok.com/endpoints) to see the endpoint URL. If you visit the ngrok URL, you'll get a 4xx response and see an error log line in your Docker Desktop logs for the Home Assistant container: ``` ERROR (MainThread) [homeassistant.components.http.forwarded] A request from a reverse proxy was received from 192.168.65.1, but your HTTP integration is not set-up for reverse proxies ``` That's because the Home Assistant configuration still needs to be updated to allow trusted proxies. ## 3. Allow trusted proxies in Home Assistant Because ngrok forwards requests to Home Assistant as a reverse proxy, Home Assistant blocks them until you list the proxy as trusted. In your `config` directory, open `configuration.yaml` and add this snippet to the bottom: ```yaml theme={null} http: use_x_forwarded_for: true trusted_proxies: # this IP may be different on your network; grab the IP from the error message # in your logs - 192.168.65.1 - 127.0.0.1 - ::1 ``` For details on these settings, see the [Home Assistant HTTP integration docs](https://www.home-assistant.io/integrations/http/). Restart the Docker container so the updated Home Assistant configuration can take effect, then visit your ngrok endpoint URL to see your Home Assistant instance online. # MQTT Source: https://ngrok.com/docs/integrations/guides/mqtt Learn how to expose your MQTT server to the internet using ngrok TCP tunnels and connect remotely with MQTT Explorer. This guide shows you how to expose your MQTT server to the internet using ngrok. It covers setting up a local MQTT server, creating an ngrok TCP tunnel, and connecting to it using MQTT Explorer. ## What you'll need * An [ngrok account](https://ngrok.com/signup). * The [ngrok agent](https://download.ngrok.com) installed. * [MQTT Explorer](https://mqtt-explorer.com/) installed. * The [Mosquitto binary](https://mosquitto.org/download/) installed. ## 1. Start an MQTT server This guide uses [Eclipse Mosquitto](https://mosquitto.org/) as the MQTT server. Follow the [Mosquitto download and install instructions](https://mosquitto.org/download/) for your operating system, then start the server: ```bash theme={null} mosquitto ``` Mosquitto listens on port `1883` by default and starts in local-only mode, so connections are only possible from the same machine until you expose it. For listener and authentication options, see the [Mosquitto documentation](https://mosquitto.org/documentation/). ## 2. Launch ngrok With your MQTT server running on `localhost`, put it online with an ngrok TCP endpoint. If you haven't already, add your authtoken from the [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken): ```bash theme={null} ngrok config add-authtoken TOKEN ``` Start a TCP endpoint that forwards to Mosquitto's port: ```bash theme={null} ngrok tcp 1883 ``` ngrok displays a `tcp://` forwarding address where your server is exposed to the internet. Copy this address for use with MQTT Explorer. ## 3. Connect with MQTT Explorer Open [MQTT Explorer](https://mqtt-explorer.com/) and click the plus sign to create a new MQTT connection. Enter the TCP address from the last step and click **Connect**. You'll see a few new lines in your MQTT server logs: ```bash theme={null} 1734410153: New connection on port 1883. 1734410153: New client connected as mqtt-explorer-ba9b56f9 (p2, c1, k60). ``` MQTT Explorer is now connected to your server over the public internet. To get messages from the client, subscribe to a topic. In your terminal, open a tab and enter: ```bash theme={null} mosquitto_sub -t 'example/topic' -v ``` Back in MQTT Explorer, publish a message to `example/topic`. You'll see the message in the terminal tab where you subscribed to the topic: ```bash theme={null} example/topic hello world ``` # Integrations Hub Source: https://ngrok.com/docs/integrations/index Browse integration guides and tutorials for connecting ngrok with third-party services, platforms, and development tools. The ngrok Integrations Hub provides guides and tutorials for connecting ngrok with a wide range of third-party services and platforms. These integrations enable you to extend ngrok's capabilities, implement enterprise-grade security, and streamline your development workflows. The guides in this section are organized by use case: * General Guides * CLI Credentials * Dashboard SSO * Databases * Endpoints SSO * Event Destinations * Frameworks * JWT Validation * Kubernetes Ingress * OAuth * Protocols * Webhooks * Web Servers # Enable JWT validation with Auth0 Source: https://ngrok.com/docs/integrations/jwt-validation/auth0 Configure JWT Validation for tokens issued from Auth0. This guide walks you through configuring the [`jwt-validation` Traffic Policy action](/docs/gateway/traffic-policy/actions/jwt-validation/) to validate tokens you issue from Auth0 on an ngrok-managed domain (referred to as `` below). ## What you'll need * A cloud or agent endpoint running with a Traffic Policy rule applied. * If you need to set these up first, see the [Quickstart](/docs/gateway/endpoints/agent-cli-quickstart/), [Cloud Endpoints Quickstart](/docs/getting-started/cloud-endpoints-quickstart/), or [Traffic Policy](/docs/gateway/traffic-policy/) docs. ## 1. Define your API in Auth0 If you've already done this, skip to the next section. * Sign in to your Auth0 tenant dashboard. * Select **Applications** > **APIs**, then **+ Create API**. * Name your API whatever you like. * Replace the value of the **Identifier** field with ``. * Leave the default values for **JSON Web Token (JWT) Profile** and **JSON Web Token Signing Algorithm**. * Click **Create**. After creating your API, you can define and add scopes in the **Permissions** tab. Those values will be populated in the `scope` claim of your JWT. ## 2. Access your JWT When you create a new API, Auth0 creates an associated application under **Applications** > **APIs** in the left navigation. Open that application and click the **Test** tab. There you will find a signed, fully functional JWT and examples for generating others programmatically. ## 3. Create a Machine to Machine application When you create your API in Auth0, it may automatically create a new Machine to Machine application and authorize it to use your new API. If you added scopes for your API, add them to the new application in the **Machine to Machine Applications** tab of your new API. If no Machine to Machine app was created, create one and select your new API when prompted. ## 4. Configure the `jwt-validation` Traffic Policy action You now have what you need to configure JWT validation in Traffic Policy. The following is an example [Traffic Policy](/docs/gateway/traffic-policy/) configuration for enabling the action with your Auth0 settings. Customize the variables in `issuer.allow_list.value`, `audience.allow_list.value`, and `jws.keys.sources.additional_jkus` as needed. * Find `` and `` in your Auth0 dashboard. * `` must match what you entered in Auth0 as the **Identifier**. You can take the example token from the **Test** tab of your Auth0 application and decode it (for example using a JWT decoder) to discover and customize the values as follows: | Field | Claim | Description | | - | - | - | | `issuer.allow_list.value` | `iss` | The issuer is usually the full URL of the Auth0 application. It can be found in your `.well-known/openid-configuration` URL, which is usually at `https://..auth0.com/.well-known/openid-configuration`. | | `audience.allow_list.value` | `aud` | The audience claim is the identifier of your API and can be found in the **APIs** section of your Auth0 dashboard. It's usually the same as `https://`. | | `http.tokens` | n/a | This is the location of the token in the incoming request. You do not need to change this value. | | `jws.allowed_algorithms` | `alg` | The algorithm your API uses to decode the JWT. You can find this in Auth0 in your API, on the **Settings** tab, under **Token Settings**. | | `jws.keys.sources.additional_jkus` | n/a | This is the URL used to verify the JWT signature. It can be found in your `.well-known/openid-configuration` URL, usually at `https://..auth0.com/.well-known/openid-configuration`. | ## 5. Test JWT validation on your endpoint Start your endpoint with the new Traffic Policy rule applied, then test it with `curl`: ```bash theme={null} curl --request GET \ --url https:/// \ --header 'authorization: Bearer YOUR-FULL-JWT-HERE-FROM-STEP-1' ``` If you see the response from your API, the integration is working. You can confirm by sending a malformed token. If you get an error with a valid token, double-check that your issuer and audience values are correct in your Traffic Policy rule. # Deploy a production API and gateway with APIOps using Argo CD and ngrok Source: https://ngrok.com/docs/integrations/kubernetes-ingress/apiops Deploy an API with APIOps and Argo CD using ngrok. This guide walks you through deploying a demo API to a Kubernetes cluster using an APIOps workflow. It consolidates the backend API service, Kubernetes configurations, and API policy definitions into a single repository that defines the *desired state* of your deployment. It covers: * ngrok's out-of-the-box [API Gateway](https://ngrok.com/use-cases/api-gateway) * The ngrok [Kubernetes Operator](https://github.com/ngrok/ngrok-operator/), which adds secure public ingress and middleware execution with declarative CRDs * The [Kubernetes Gateway API](https://kubernetes.io/docs/concepts/services-networking/gateway/), for role-oriented load balancing and routing with [developer-defined](https://ngrok.com/blog-post/developer-defined-golden-path-kubernetes-gateway-api) paths to production APIs * [Argo CD](https://argo-cd.readthedocs.io/en/stable/), a declarative GitOps tool for Kubernetes that version-controls definitions, configurations, and environments, including an API gateway ## What you'll need * An [ngrok account](https://ngrok.com/signup). * A Kubernetes cluster. For a local cluster, use [minikube](https://minikube.sigs.k8s.io/docs/start/); this guide refers to your cluster as ``. * [Argo CD](https://argo-cd.readthedocs.io/en/stable/getting_started/) installed on your cluster, with the `argocd` CLI installed and logged in on your local workstation. * [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) and [Helm 3.0.0+](https://helm.sh/docs/intro/install/) installed on your local workstation. * The [ngrok Kubernetes Operator](/docs/gateway/k8s/) installed on your cluster. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list). You can use an ngrok subdomain or a custom branded domain like `https://api.example.com`; this guide refers to it as ``. ## Deploy Argo CD This guide uses Argo CD to sync your Git repository to your cluster. If you don't have it yet, follow Argo CD's [getting started guide](https://argo-cd.readthedocs.io/en/stable/getting_started/) to install it on your cluster and log in with the `argocd` CLI. Confirm Argo CD is running before continuing: ```bash theme={null} kubectl get pods --namespace argocd ``` ## Set up the demo API Next, you need to set up the Git repository for the API you'll deploy behind your ngrok API gateway. GitOps (and thus APIOps) requires declarative and version-controlled configuration, and that includes the hostname for your deployment. You can't simply clone the demo API repository and apply it to your cluster, as the ngrok-supplied hostname will already be in use. If you have an existing API and GitOps configuration, you can [skip to Deploy your demo API with Argo CD](#deploy-your-demo-api-with-argo-cd) and adapt the Argo CD CLI commands to your Git repository. 1. Reserve a static domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list). This static domain (for example, `example.ngrok.app`) is your `NGROK_DOMAIN` for the remainder of this guide. 2. Fork the repository for the demo API at [ngrok-samples/apiops-demo](https://github.com/ngrok-samples/apiops-demo). 3. In your fork, open `gateway.yaml` and replace the values of lines 18 and 37 with the ngrok domain you just created (for example, `one-two-three.ngrok.app`). 4. Add, commit, and push these changes to your fork. ## Deploy your demo API with Argo CD Now that your demo API is forked and properly configured on GitHub, you can connect it to Argo CD to sync, reconcile, and deploy. 1. Register the demo app with Argo CD, replacing `` with your fork of the demo API repository. ```bash theme={null} argocd app create apiops-demo \ --repo https://github.com//apiops-demo.git \ --path . \ --dest-server https://kubernetes.default.svc \ --dest-namespace apiops-demo ``` Refresh the Argo CD UI to see your app. The **Missing** and **OutOfSync** status report are not errors; they reflect that Argo CD doesn't automatically sync and deploy a newly registered app. You can click the app to view additional details about the deployment and the Git repository on which it is based. 2. Use the Argo CD CLI to perform a manual first sync of your registered app against your Git repository. ```bash theme={null} argocd app sync apiops-demo ``` Refresh the UI to see that your demo API is properly synced and deployed. You can also navigate to the [**Endpoints** section](https://dashboard.ngrok.com/endpoints) of the ngrok dashboard to see the endpoint the ngrok Kubernetes Operator created via secure tunnel. You can `curl` your deployed API; ngrok's API gateway handles ingress and TLS automatically. You'll only see `null` in response, but that confirms your demo API is working. ```bash theme={null} curl \ -X GET \ -H "Content-Type: application/json" \ https:///legend ``` ## Enable APIOps in Argo CD A fundamental component of GitOps, and thus APIOps, is that because your Git repository contains the latest version of your desired state, your deployment toolkit should automatically update the production deployment without any manual processes. 1. Enable auto sync of your app to the Git repository that stores your desired state. ``` argocd app set apiops-demo --sync-policy automated ``` You can confirm this change in the Argo CD UI. The default interval at which Argo CD looks for changes to the desired state in your Git repo is 3 minutes. You can alter this in Argo CD's configuration. 2. Optionally, test auto sync by editing the number of replicas of the demo API in your cluster. In your Git repository, open the `deployment.yaml` file and edit the `replicas` value: ```yaml theme={null} spec: replicas: 5 ``` To push these changes to your production cluster, add, commit, and push them to your Git repository. ```bash theme={null} git add . git commit -m "Increase replicas to 5" git push origin main ``` Argo CD will soon poll your repository, identify changes, and reconcile the deployed state to increase the number of replicas. ## Configure your API gateway with Traffic Policies The [Traffic Policy module](/docs/gateway/traffic-policy/) can be used alongside the Kubernetes Gateway API. This lets you place all your Traffic Policy actions into a single `NgrokTrafficPolicy` CRD and control your ngrok-powered API gateway with an APIOps workflow using version-controlled, declarative manifests. The project includes a basic example in [`traffic-policy.yaml`](https://github.com/ngrok-samples/apiops-demo/blob/main/traffic-policy.yaml). The steps below add rate limiting with the [`rate-limit` action](/docs/gateway/traffic-policy/actions/rate-limit), a common first step in protecting an API from abuse. 1. In your fork of the demo API project, create a new file called `rate-limiting.yaml`. Open the file and add the following YAML: ```yaml theme={null} kind: NgrokTrafficPolicy apiVersion: ngrok.k8s.ngrok.com/v1alpha1 metadata: name: traffic-policy-rate-limiting spec: policy: on_http_request: - actions: - type: "rate-limit" config: name: "Only allow 10 requests per minute" algorithm: "sliding_window" capacity: 10 rate: "60s" bucket_key: - "req.headers['host']" ``` This example policy includes a low limit for demonstration purposes, but you can change the `capacity` value to your needs in production. 2. Insert the YAML below into the `HTTPRoute` in your `deployment.yaml`. This defines a filter that runs during the request or response lifecycle, letting you inject policy into all traffic arriving at your API through your ngrok API gateway. ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: apiops-demo-route namespace: apiops-demo spec: parentRefs: - kind: Gateway name: apiops-demo-gateway namespace: apiops-demo hostnames: - "apiops-demo.ngrok.app" rules: - matches: - path: type: PathPrefix value: / filters: - type: ExtensionRef extensionRef: group: ngrok.k8s.ngrok.com kind: NgrokTrafficPolicy name: traffic-policy-rate-limiting backendRefs: - name: apiops-demo-service port: 80 kind: Service ``` 3. Add, commit, and push this change to your Git repository. Argo CD will auto sync and reconcile the deployed state with the changes to the `NgrokTrafficPolicy` CRD and `HTTPRoute`. Once the ngrok Kubernetes Operator picks up those changes, it will push them, and you can verify them directly in your [ngrok dashboard](https://dashboard.ngrok.com/endpoints). 4. Optionally, test your new rate limiting policy by `curl`-ing your API in a quick loop. ```bash theme={null} for i in `seq 1 50`; do \ curl -s -o /dev/null \ -w "\n%{http_code}" \ -X GET https://apiops-demo.ngrok.app/legend ; \ done ``` You should see [`429` response codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) as your ngrok API gateway rate-limits your IP address. ## What's next? You've now built a proof of concept for deploying an API to production using ngrok and Argo CD. You can use this combination to deploy real-world APIs behind a production-grade gateway without complex configuration or extra external infrastructure. In a real-world deployment, you would also integrate your Git repository with a proper CI/CD pipeline, which would run tests for security, governance, and code quality. By only merging changes that pass your CI/CD pipeline, you can better guarantee the production-readiness of your API. For more Traffic Policy options, see the [Auth0 JWT integration](/docs/integrations/jwt-validation/auth0/) and the [Traffic Policy docs](/docs/gateway/traffic-policy/) for the full list of actions and syntax. The [library of "drop-in" examples](https://ngrok.com/blog-post/api-gateway-policy-management-examples) contains additional use cases like blocking traffic from specific countries, deprecating API versions, [event logging](/docs/obs/), and more. # Ingress to apps secured by Microsoft Entra ID in Kubernetes Source: https://ngrok.com/docs/integrations/kubernetes-ingress/azure-ad-k8s Use ngrok to add ingress to Kubernetes apps and authenticate users with Microsoft Entra ID. This guide explains how to add ingress to any app running in Kubernetes and restrict access to users authorized through Microsoft Entra ID (formerly Azure Active Directory), using the ngrok Kubernetes Operator. It walks you through (optionally) creating a Kubernetes cluster and example deployment, installing the Operator to expose your app through ngrok, and using the ngrok Kubernetes Operator's Traffic Policy to enable SAML with Microsoft Entra ID as your identity provider for single sign-on. The [ngrok Kubernetes Operator](/docs/gateway/k8s/) is ngrok's official controller for adding secure public ingress and middleware execution to your Kubernetes apps with ngrok. Microsoft Entra ID is an identity and access management platform that helps administrators and DevOps engineers safeguard their organization's multicloud environment with strong authentication and unified identity management. The Operator and Microsoft Entra ID integrate so you can route public traffic to an app on a Kubernetes cluster and restrict access to users you authorize through Microsoft Entra ID. ## What you'll need * An ngrok account. * A Microsoft Azure account with access to an existing Microsoft Entra ID tenant or the ability to create a new tenant with a Microsoft Entra ID P1 or P2 license. * A Kubernetes cluster (local or in a public cloud) with an app you want to make publicly accessible to specific users. * kubectl and Helm 3.0.0+ installed on your local workstation. * The [ngrok Kubernetes Operator](/docs/gateway/k8s/) installed on your cluster. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as ``. ## 1. Create a cluster and deploy an example app This guide needs an app on a Kubernetes cluster to route traffic to. It uses the Online Boutique demo, which exposes a `frontend` service on port `80`. If you already have a cluster and app, skip to [2. Add the ngrok Kubernetes Operator](#2-add-the-ngrok-kubernetes-operator). Otherwise, deploy Online Boutique by following Google's [microservices-demo instructions](https://github.com/GoogleCloudPlatform/microservices-demo). For a local cluster, [minikube](https://minikube.sigs.k8s.io/docs/start/) works. Confirm your workloads are running before continuing: ```bash theme={null} kubectl get pods ``` ## 2. Add the ngrok Kubernetes Operator If you haven't already, install the [ngrok Kubernetes Operator](/docs/gateway/k8s/) so ngrok can route public traffic to your app. Follow the [Kubernetes ingress quickstart](/docs/getting-started/kubernetes/ingress) to install the Operator with your ngrok credentials. You also need a reserved domain to serve as your `NGROK_DOMAIN`. Reserve one from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list). Create a `boutique-ingress.yaml` file that tells the Operator to route traffic on your `NGROK_DOMAIN` to the Online Boutique `frontend` service. Replace `NGROK_DOMAIN` in the manifest below with the domain you reserved (for example, `one-two-three.ngrok.app`). To expose a different app, change `metadata.name`, `service.name`, and `service.port.number` to match it. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: boutique-ingress namespace: default spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: frontend port: number: 80 ``` Apply the manifest: ```bash theme={null} kubectl apply -f boutique-ingress.yaml ``` Give your cluster a few moments to launch the necessary resources and for ngrok to pick up the new endpoint. If you see an error when applying the manifest, confirm you updated the `NGROK_DOMAIN` value and re-apply. Open your ngrok domain (for example, `https://one-two-three.ngrok.app`) in your browser to see the app, now served through ngrok. ## 3. Enable SAML with a Traffic Policy The SAML Traffic Policy action is currently in [developer preview](/docs/gateway/traffic-policy/actions/saml/). [Request access](https://dashboard.ngrok.com/early-access) to configure SAML via Traffic Policy. Your Kubernetes-based app is now publicly accessible through ngrok. To restrict access to authorized users with Microsoft Entra ID credentials, create a `NgrokTrafficPolicy` custom resource with the SAML action and apply it to your Ingress. * Create a file named `saml-policy.yaml` with the following content: ```yaml theme={null} apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: saml namespace: default spec: policy: on_http_request: - actions: - type: saml config: idp_metadata: 'YOUR_IDP_METADATA_XML' ``` * Apply the Traffic Policy CR: ```bash theme={null} kubectl apply -f saml-policy.yaml ``` * Update your `boutique-ingress.yaml` to reference this policy with an annotation: ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: boutique-ingress namespace: default annotations: k8s.ngrok.com/traffic-policy: saml spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: frontend port: number: 80 ``` * Re-apply the Ingress manifest: ```bash theme={null} kubectl apply -f boutique-ingress.yaml ``` * Refer to the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/) to retrieve your SP Entity ID and ACS URL values, which you'll need in the next step. ## 4. Create an enterprise app in Microsoft Entra ID With a SAML Traffic Policy applied to your Ingress, configure Microsoft Entra ID to operate as your identity provider (IdP). * Open your Microsoft Entra ID tenant in the Azure console. * Click **Enterprise applications** in the left-hand sidebar, then **+ New application**, then **+ Create your own application**. Give your app a name (for example, `online-boutique`) and select **Integrate any other application you don't find in the gallery (Non-gallery)**. * Click **Create** to create your enterprise app; you'll be taken to its Overview dashboard. * To give specific users or groups access to your app, click **Assign users and groups** and follow the steps, then go back to **Overview**. * Click **Set up single sign on** so users can sign in with their Microsoft Entra credentials, then choose **SAML** as the single sign-on protocol. * Click **Edit** in the **Basic SAML Configuration** box. Refer to the [SAML action documentation](/docs/gateway/traffic-policy/actions/saml/) to retrieve your SP Entity ID and ACS URL, then copy them into the **Identifier (Entity ID)** and **Reply URL (Assertion Consumer Service URL)** fields respectively. Click **Save** before closing the modal. * In the **SAML Certificates** box, download the **Federation Metadata XML**. ## 5. Update the Traffic Policy with the IdP metadata With Microsoft Entra ID configured as your identity provider, update your Traffic Policy with the real IdP metadata. * Open your `saml-policy.yaml` and replace `YOUR_IDP_METADATA_XML` with the contents of the Federation Metadata XML file you downloaded. * Re-apply the updated Traffic Policy: ```bash theme={null} kubectl apply -f saml-policy.yaml ``` ## 6. Test authorization to your app using Microsoft Entra ID You've finished integrating the ngrok Kubernetes Operator and Microsoft Entra ID. Your app (the Online Boutique or a custom deployment) is now publicly accessible only to users authorized with their Microsoft Entra ID credentials. * Open an incognito or private browser window (or a different browser) and go to your `NGROK_DOMAIN`. You should see a single sign-on screen from Microsoft. * Enter credentials for a Microsoft account you assigned to your enterprise application in Microsoft Entra ID in [step 4](#4-create-an-enterprise-app-in-microsoft-entra-id). Behind the scenes, ngrok redirects you to your identity provider, Microsoft Entra ID, for authentication. Once you sign in, or are already logged in, Microsoft Entra ID then returns a SAML assertion to ngrok, telling ngrok your authentication is confirmed and you have authorization to access the app. Microsoft Entra ID will then redirect you back to your app. ## What's next? You've now integrated the ngrok Kubernetes Operator with Microsoft Entra ID to restrict access to your app to only authenticated users. With ngrok operating as middleware, handling both ingress to your Kubernetes cluster and the handshake with Microsoft Entra ID as an identity provider, you can deploy and secure new apps in a multi-cloud environment using your existing Microsoft/Azure identity and access management settings. From an end user perspective, they only need to sign in once, using their Microsoft credentials, to authenticate themselves and access any number of applications you manage using Microsoft Entra ID. From here, you have a few options: ### Clean up If you created a local cluster for the demo app, delete it (for example, `minikube delete`). ngrok will delete your endpoint automatically. To restore your previous authorization settings, open your Microsoft Entra ID tenant and delete your enterprise application. ### Extend your ngrok Kubernetes Operator and Microsoft Entra ID integration ngrok's [Traffic Policy system](/docs/gateway/traffic-policy/) can secure multiple apps with single sign-on. Combine [`NgrokTrafficPolicy` resources](/docs/gateway/k8s/guides/using-ingresses/#using-ngroktrafficpolicy-with-ingress) with [Ingress fan-out](/docs/gateway/k8s/guides/using-ingresses/#fan-out-versus-multiple) to route and secure several apps. [Custom domains](/docs/gateway/k8s/guides/custom-domain/) and a [circuit breaker](/docs/gateway/traffic-policy/actions/circuit-breaker/) are good next steps for production. See the [ngrok Kubernetes Operator GitHub repository](https://github.com/ngrok/ngrok-operator) and the [Kubernetes Operator docs](/docs/gateway/k8s/) for more details. # Ingress to Kubernetes apps deployed on Azure Kubernetes Service (AKS) Source: https://ngrok.com/docs/integrations/kubernetes-ingress/azure-aks-k8s Add public ingress to an app running on Azure Kubernetes Service (AKS) using ngrok's Kubernetes Operator, then secure it with Traffic Policy. This guide shows you how to add public ingress to an app running on Azure Kubernetes Service (AKS). You'll use the ngrok Kubernetes Operator to route public traffic to the app through an encrypted tunnel, then secure it with OAuth. ## What you'll need * An AKS cluster with a demo app running. If you don't have one, Microsoft's [AKS quickstart](https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-portal) covers creating a cluster and deploying the AKS Store demo app this guide uses. * An ngrok account. * `kubectl` and Helm 3.0.0+ installed on your local workstation. * The [ngrok Kubernetes Operator](/docs/gateway/k8s/) installed on your cluster. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as `NGROK_DOMAIN`. ## Set up your cluster and demo app This guide assumes an AKS cluster reachable with `kubectl` and a `store-front` service listening on port `80`, which the AKS Store demo app provides. If you don't have this yet, follow Microsoft's [AKS quickstart](https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-portal) to create a cluster, connect to it with `kubectl`, and deploy the demo app. To expose a different app, deploy it now and adjust the service name and port in the ingress below. Confirm your workloads are running before continuing: ```bash theme={null} kubectl get pods ``` ## Add ngrok's Kubernetes ingress to your demo app Deploy an `Ingress` resource that tells the [ngrok Kubernetes Operator](/docs/gateway/k8s/) to route traffic arriving on your reserved domain to the `store-front` service on port `80`. Save the following manifest as `store-ingress.yaml`, replacing `NGROK_DOMAIN` with the domain you reserved: ```yaml showLineNumbers theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: store-ingress spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: store-front port: number: 80 ``` Apply it to your cluster: ```bash theme={null} kubectl apply -f store-ingress.yaml ``` Confirm the ingress was created: ```bash theme={null} kubectl get ingress store-ingress ``` Open your reserved domain (for example, `https://NGROK_DOMAIN`) in a browser to see the demo app. ngrok routes requests to the ngrok Kubernetes Operator, which forwards them to the `store-front` service. ## Add OAuth authentication to your demo app Now that your demo app is publicly accessible through ngrok, you can add [capabilities](/docs/gateway/endpoints/http/) like authentication without deploying extra infrastructure. This section restricts access to Google accounts under a specific domain. With the [Traffic Policy system](/docs/gateway/traffic-policy/) and the [`oauth` action](/docs/gateway/traffic-policy/actions/oauth), ngrok handles OAuth entirely on its network. You don't need to add services to your cluster or change any routes, because ngrok authenticates and authorizes requests before they reach your endpoint. To enable the `oauth` action, create an `NgrokTrafficPolicy` custom resource and apply it to your `Ingress` with an annotation. You can also apply the policy to a specific backend or as the default backend for an `Ingress`. See the doc on using the [Operator with Ingresses](/docs/gateway/k8s/guides/using-ingresses/#using-ngroktrafficpolicy-with-ingress) for details. * Edit your existing ingress YAML with the following. Note the new `annotations` field and the `NgrokTrafficPolicy` CR. ```yaml theme={null} ... --- # Configuration for ngrok's Kubernetes Operator apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: store-ingress namespace: default annotations: k8s.ngrok.com/traffic-policy: oauth spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: store-front port: number: 80 --- # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: default spec: policy: on_http_request: - type: oauth config: provider: google ``` * When you open your demo app again, ngrok asks you to log in with Google. To limit access to just yourself or your colleagues, tighten the policy. * Use [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) and [CEL interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation) to reject OAuth logins that aren't under `example.com`. Update the `NgrokTrafficPolicy` portion of your manifest after changing `example.com` to your domain. ```yaml theme={null} # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: default spec: policy: on_http_request: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@example.com')" actions: - type: custom-response config: body: You are not authorized, ${actions.ngrok.oauth.identity.name}. status_code: 400 ``` * Check your deployed app again. If you log in with an email that isn't under your domain, ngrok rejects the request. ## What's next? You've used the open source ngrok Kubernetes Operator to add public ingress to an app on AKS without managing complex Kubernetes networking. Because ngrok handles ingress and middleware execution, you can follow the same process for your production apps. To go further, explore the [Kubernetes docs](/docs/gateway/k8s/) for how the Operator works and how to integrate ngrok with an existing production cluster, or try [bindings](/docs/gateway/k8s/guides/bindings/) and [endpoint pooling](/docs/gateway/k8s/guides/pooling/). # Kubernetes ingress to services in a Consul service mesh Source: https://ngrok.com/docs/integrations/kubernetes-ingress/consul-k8s Set up a local Consul service mesh to use the ngrok Kubernetes Operator with Consul. This guide walks you through setting up a Consul service mesh on Kubernetes and using the ngrok Kubernetes Operator to provide ingress to your services. The [ngrok Kubernetes Operator](https://github.com/ngrok/ngrok-operator) is the official open-source controller for adding public and secure ingress traffic to your k8s services. Consul is a secure and resilient service mesh that provides service discovery, configuration, and segmentation; Consul Connect provides service-to-service authorization and encryption with mutual TLS. Together, Consul secures communication between services in a cluster while ngrok provides public ingress to those services. ## What you'll need * A remote or local Kubernetes cluster with Consul installed or minikube to set up a demo cluster locally. * An ngrok account. * kubectl and Helm 3.0.0+ installed on your local workstation. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as ``. ## Set up a local Consul service mesh on Kubernetes This guide needs a Kubernetes cluster running a Consul service mesh with Consul Connect and the sidecar injector enabled. If you already have one, skip to [Configure the ngrok Kubernetes Operator](#configure-the-ngrok-kubernetes-operator). If you don't, follow HashiCorp's [Get Started with Consul on Kubernetes](https://developer.hashicorp.com/consul/tutorials/get-started-kubernetes) tutorial to create a cluster and install Consul. For a local cluster, [minikube](https://minikube.sigs.k8s.io/docs/start/) works. Make sure Consul Connect is enabled so the mesh can inject sidecars. Verify Consul is installed and all its pods are healthy before continuing: ```bash theme={null} kubectl get pods --namespace consul ``` ## Configure the ngrok Kubernetes Operator Consul requires a bit of extra configuration to work with ngrok's Operator for Kubernetes ingress. You'll need to use a pod annotation to enable the Consul Connect sidecar injector. This allows using Consul Connect to secure the traffic between the ngrok Kubernetes Operator and your services. * First, create a Kubernetes Service for the ngrok Kubernetes Operator in the `consul` namespace. Consul relies on this to name services to declare Service Intention `source` and `destination` values. ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: ngrok-operator namespace: consul spec: ports: - name: http port: 80 protocol: TCP targetPort: 80 selector: app.kubernetes.io/name: ngrok-operator ``` * Next, install the ngrok Kubernetes Operator into your cluster. Its pods must join the Consul service mesh so they can proxy traffic to your other services. You do this with two pod annotations: one to enable the Consul Connect sidecar injector and one to allow outbound traffic through the mesh. Consul documents these annotations in [Configure Operators for Consul on Kubernetes](https://developer.hashicorp.com/consul/docs/k8s/connect/ingress-controllers). ```yaml theme={null} # Enables the Consul Connect sidecar injector consul.hashicorp.com/connect-inject: "true" # The CIDR of your Kubernetes API, from: kubectl get svc kubernetes --output jsonpath='{.spec.clusterIP}' consul.hashicorp.com/transparent-proxy-exclude-outbound-cidrs: "10.96.0.1/32" ``` Follow the [Kubernetes ingress quickstart](/docs/getting-started/kubernetes/ingress) to install the Operator with your ngrok credentials. Then set the annotations with a Helm upgrade, replacing the CIDR with your own: ```bash theme={null} helm upgrade ngrok-operator ngrok/ngrok-operator \ --namespace ngrok-operator \ --reuse-values \ --set-string 'podAnnotations.consul\.hashicorp\.com/connect-inject=true' \ --set-string 'podAnnotations.consul\.hashicorp\.com/transparent-proxy-exclude-outbound-cidrs=YOUR_KUBERNETES_API_CIDR' ``` **Consul annotation:** HashiCorp's docs also mention the annotation `consul.hashicorp.com/transparent-proxy-exclude-inbound-ports`. This does not apply to the ngrok Kubernetes Operator, which creates an outbound connection for ingress rather than exposing ports. **Helm:** The backslashes escape the dots in the annotation keys, and `--set-string` keeps each value as a string rather than a boolean, which pod annotations require. **Production:** To manage your credentials with Infrastructure as Code or source control, see the [Helm configuration reference](/docs/gateway/k8s/installation/helm/). ## Install a sample application Install a sample application to try out the service mesh and Operator combination. This guide uses the [HashiCups Demo Application](https://github.com/hashicorp-demoapp) provided by HashiCorp. This application is a simple e-commerce application that allows users to order coffee cups. It has a `frontend` and public API services that are also backed by a private API and database. These communicate with each other through the Consul service mesh. It comes with nginx installed as a proxy for the `frontend` and Public API services. Replace this with ngrok to provide public access and other features. For this demo, everything is installed in the `consul` namespace. The ngrok Kubernetes Operator can send traffic to services across different namespaces, but [Consul Service Intentions](https://developer.hashicorp.com/consul/docs/connect/config-entries/service-intentions) across namespaces [require an enterprise account](https://developer.hashicorp.com/consul/docs/connect/config-entries/service-intentions#sources-namespace). For now, keep everything in the same namespace. * Clone the [HashiCorp Learning Consul repo](https://github.com/hashicorp/learn-consul-kubernetes), which includes example applications for Consul and Kubernetes. ```bash theme={null} git clone https://github.com/hashicorp/learn-consul-kubernetes ``` * Install the HashiCups sample app in the `consul` namespace. It consists of several Services and Deployments that make up a tiered application. ```bash theme={null} kubectl apply -f learn-consul-kubernetes/service-mesh/deploy/hashicups -n consul ``` * Replace the existing Service Intentions with new ones. [Service Intentions](https://developer.hashicorp.com/consul/docs/connect/config-entries/service-intentions) are how Consul configures the mesh to allow or deny traffic between services. HashiCups ships with `nginx` intentions to the `frontend` and `public-api` services. Remove these and add new intentions that allow traffic from the ngrok Kubernetes Operator to the `frontend` and `public-api` services. ```bash theme={null} kubectl delete serviceintentions public-api -n consul kubectl delete serviceintentions frontend -n consul ``` Create Service Intentions that allow ngrok to reach the `frontend` and `public-api` services. Save the following manifests to a file and apply them with `kubectl apply -f -n consul`. ```yaml theme={null} apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions metadata: name: ngrok-consul-frontend namespace: consul spec: destination: name: frontend sources: - action: allow name: ngrok-operator ``` ```yaml theme={null} apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions metadata: name: ngrok-consul-api namespace: consul spec: sources: - name: frontend action: allow - name: ngrok-operator action: allow destination: name: public-api ``` ## Configure Public Ingress for the sample application Now that the ngrok Kubernetes Operator can communicate with the `frontend` service and `public-api` service through the Consul Service Mesh via Service Intentions, create an ingress to route traffic to the app. Create ingress objects to route traffic to the `frontend` service and the `public-api` service. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-consul namespace: consul spec: ingressClassName: ngrok rules: - host: http: paths: - path: / pathType: Prefix backend: service: name: frontend port: number: 3000 - path: /api pathType: Prefix backend: service: name: public-api port: number: 8080 ``` This ingress object: * Uses the `ngrok` ingress class * The host is the ngrok domain name you selected that is static * There is a route for `/` that routes to the `frontend` service on port `3000` * There is a route for `/api` that routes to the `public-api` service on port `8080` Open your `` domain in your browser to see the HashiCups application. ## Add OAuth protection to the app Now that HashiCups is publicly accessible through ngrok, you can add Google OAuth to restrict access without deploying extra infrastructure. With the [Traffic Policy system](/docs/gateway/traffic-policy/) and the [`oauth` action](/docs/gateway/traffic-policy/actions/oauth), ngrok handles OAuth entirely on its network. You don't need to add services to your cluster or change any routes, because ngrok authenticates and authorizes requests before they reach your endpoint. To enable the `oauth` action, create an `NgrokTrafficPolicy` custom resource and apply it to your `Ingress` with an annotation. You can also apply the policy to a specific backend or as the default backend for an `Ingress`. See the doc on using the [Operator with Ingresses](/docs/gateway/k8s/guides/using-ingresses/#using-ngroktrafficpolicy-with-ingress) for details. Edit your existing ingress configuration with the following. Note the new `annotations` field and the `NgrokTrafficPolicy` CR, which must be in the same namespace as the `Ingress`. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-consul namespace: consul annotations: k8s.ngrok.com/traffic-policy: oauth spec: ingressClassName: ngrok rules: - host: http: paths: - path: / pathType: Prefix backend: service: name: frontend port: number: 3000 - path: /api pathType: Prefix backend: service: name: public-api port: number: 8080 --- # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: consul spec: policy: on_http_request: - type: oauth config: provider: google ``` Re-apply your configuration. When you open your demo app again, ngrok asks you to log in with Google. To limit access to just yourself or your colleagues, tighten the policy. Use [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) and [CEL interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation) to reject OAuth logins that aren't under `example.com`. Update the `NgrokTrafficPolicy` portion of your manifest after changing `example.com` to your domain. ```yaml theme={null} # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: consul spec: policy: on_http_request: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@example.com')" actions: - type: custom-response config: body: You are not authorized, ${actions.ngrok.oauth.identity.name}. status_code: 400 ``` Check your deployed HashiCups app again. If you log in with an email that isn't under your domain, ngrok rejects the request. ## What's next? You've used the ngrok Kubernetes Operator to provide public ingress to services inside a Consul service mesh and secured them with OAuth. To go further, explore the [Kubernetes docs](/docs/gateway/k8s/) or try [bindings](/docs/gateway/k8s/guides/bindings/) and [endpoint pooling](/docs/gateway/k8s/guides/pooling/). # Ingress to Kubernetes apps on clusters managed by DigitalOcean Source: https://ngrok.com/docs/integrations/kubernetes-ingress/digitalocean-k8s Add Kubernetes ingress to any app running in a cluster managed by DigitalOcean using the ngrok Kubernetes Operator. This guide shows you how to add public ingress to an app running on DigitalOcean Kubernetes (DOKS). You'll use the ngrok Kubernetes Operator to route public traffic to the app, then secure it with OAuth. ## What you'll need * A DOKS cluster reachable with `kubectl`. If you don't have one, DigitalOcean's [guide to creating clusters](https://docs.digitalocean.com/products/kubernetes/how-to/create-clusters/) covers the `doctl` CLI, API, and control panel. * A DigitalOcean account. * An ngrok account. * `kubectl` and Helm 3.0.0+ installed on your local workstation. * The [ngrok Kubernetes Operator](/docs/gateway/k8s/) installed on your cluster. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as `NGROK_DOMAIN`. ## Set up your cluster and demo app This guide assumes a DOKS cluster reachable with `kubectl` and the ngrok Kubernetes Operator installed, as covered in the prerequisites above. This guide uses the Bookinfo app from DigitalOcean's [sample Kubernetes apps repository](https://github.com/digitalocean/kubernetes-sample-apps). Clone the repository and deploy Bookinfo: ```bash theme={null} git clone https://github.com/digitalocean/kubernetes-sample-apps.git cd kubernetes-sample-apps kubectl apply -k bookinfo-example/kustomize ``` Confirm the workloads are running before continuing: ```bash theme={null} kubectl get all -n bookinfo ``` To expose a different app, deploy it now and adjust the service name and port in the ingress below. ## Add ngrok's Kubernetes ingress to your demo app Your Bookinfo app is running, but you have no way to reach it from outside the cluster. Deploy an `Ingress` resource that tells the ngrok Kubernetes Operator to route traffic arriving on your reserved domain to the `productpage` service on port `9080`, which serves the Bookinfo UI. Save the following manifest as `bookinfo-ingress.yaml`, replacing `NGROK_DOMAIN` with the domain you reserved: ```yaml showLineNumbers theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: bookinfo-ingress namespace: bookinfo spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: productpage port: number: 9080 ``` Apply it to your cluster: ```bash theme={null} kubectl apply -f bookinfo-ingress.yaml ``` Open `https://NGROK_DOMAIN` in your browser to see your Bookinfo app, then click **Normal user** to explore. ngrok routes requests to the ngrok Kubernetes Operator, which forwards them to the `productpage` service. ## Add OAuth authentication to your demo app Now that your demo app is publicly accessible through ngrok, you can add [capabilities](/docs/gateway/endpoints/http/) like authentication without deploying extra infrastructure. This section restricts access to Google accounts under a specific domain. With the [Traffic Policy system](/docs/gateway/traffic-policy/) and the [`oauth` action](/docs/gateway/traffic-policy/actions/oauth/), ngrok handles OAuth entirely on its network. You don't need to add services to your cluster or change any routes, because ngrok authenticates and authorizes requests before they reach your endpoint. To enable the `oauth` action, create an `NgrokTrafficPolicy` custom resource and apply it to your `Ingress` with an annotation. You can also apply the policy to a specific backend or as the default backend for an `Ingress`. See the doc on using the [Operator with Ingresses](/docs/gateway/k8s/guides/using-ingresses/#using-ngroktrafficpolicy-with-ingress) for details. Edit your `bookinfo-ingress.yaml` file with the following. Note the new `annotations` field and the `NgrokTrafficPolicy` CR, which must be in the same namespace as the `Ingress`. ```yaml showLineNumbers theme={null} --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: bookinfo-ingress namespace: bookinfo annotations: k8s.ngrok.com/traffic-policy: oauth spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: productpage port: number: 9080 --- # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: bookinfo spec: policy: on_http_request: - type: oauth config: provider: google ``` Re-apply your configuration: ```bash theme={null} kubectl apply -f bookinfo-ingress.yaml ``` When you open your demo app again, ngrok asks you to log in with Google. To limit access to just yourself or your colleagues, tighten the policy. Use [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) and [CEL interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation) to reject OAuth logins that aren't under `example.com`. Update the `NgrokTrafficPolicy` portion of your manifest after changing `example.com` to your domain. ```yaml theme={null} # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: bookinfo spec: policy: on_http_request: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@example.com')" actions: - type: custom-response config: body: You are not authorized, ${actions.ngrok.oauth.identity.name}. status_code: 400 ``` Check your deployed app again. If you log in with an email that isn't under your domain, ngrok rejects the request. ## What's next? You've used the open source ngrok Kubernetes Operator to add public ingress to an app on a cluster managed by DigitalOcean without managing complex Kubernetes networking. Because ngrok handles ingress and middleware execution, you can follow the same process for your production apps. To go further, explore the [Kubernetes docs](/docs/gateway/k8s/) for how the Operator works and how to integrate ngrok with an existing production cluster, or try [bindings](/docs/gateway/k8s/guides/bindings/) and [endpoint pooling](/docs/gateway/k8s/guides/pooling/). # Kubernetes ingress to services hosted on EKS Source: https://ngrok.com/docs/integrations/kubernetes-ingress/eks Learn how to get started running the ngrok Kubernetes Operator on AWS EKS. This guide explains how to run the ngrok Kubernetes Operator on AWS EKS to add secure ingress to your services. The [ngrok Kubernetes Operator](https://github.com/ngrok/ngrok-operator) is the official open-source controller for adding public and secure ingress traffic to your Kubernetes services. It works with an AWS EKS cluster as long as the cluster has outbound access to the ngrok service. ## What you'll need * An AWS EKS cluster reachable with `kubectl`. If you don't have one, follow AWS's [Get started with Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html) guide. * An ngrok account. * `kubectl` and Helm 3.0.0+ installed on your local workstation. * The [ngrok Kubernetes Operator](/docs/gateway/k8s/) installed on your cluster. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as `NGROK_DOMAIN`. On a free account, this must be a static subdomain, which you can claim from the banner in your dashboard. Paid accounts can use a custom domain or a subdomain of `ngrok.app` or `ngrok.dev`. ## Connect `kubectl` to your EKS cluster Amazon EKS authenticates `kubectl` through the `aws` CLI. Confirm your `kubectl` context points at the cluster before continuing: ```bash theme={null} kubectl config current-context ``` If it doesn't, follow AWS's guide to [connect `kubectl` to an EKS cluster](https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html), which walks through installing the `aws` CLI and running `aws eks update-kubeconfig`. ## Install a sample application and Kubernetes ingress Create a manifest file (for example `ngrok-manifest.yaml`) with the contents below. This deploys the tinyllama demo LLM application from [ngrok-samples/tinyllama](https://github.com/ngrok-samples/tinyllama) and an `Ingress` that tells the ngrok Kubernetes Operator to route traffic on your reserved domain to it. Replace `NGROK_DOMAIN` with the domain you reserved. This is the URL you'll use to access your service from anywhere. ```yaml showLineNumbers theme={null} apiVersion: v1 kind: Service metadata: name: tinyllama spec: ports: - name: http port: 80 targetPort: 8080 selector: app: tinyllama --- apiVersion: apps/v1 kind: Deployment metadata: name: tinyllama spec: replicas: 1 selector: matchLabels: app: tinyllama template: metadata: labels: app: tinyllama spec: containers: - name: tinyllama image: ghcr.io/ngrok-samples/tinyllama:main ports: - name: http containerPort: 8080 --- # Configuration for ngrok's Kubernetes Operator apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tinyllama-ingress namespace: default spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: tinyllama port: number: 80 ``` Apply the manifest file to your cluster: ```bash theme={null} kubectl apply -f ngrok-manifest.yaml ``` **Troubleshooting:** If you get an error when applying the manifest, double-check that you've updated the `NGROK_DOMAIN` value and try again. Open your reserved domain (for example, `https://NGROK_DOMAIN`) in a browser to confirm the tinyllama app is accessible from the internet. ## Add security to your app With the [Traffic Policy system](/docs/gateway/traffic-policy/) and the [`oauth` action](/docs/gateway/traffic-policy/actions/oauth/), ngrok manages OAuth protection entirely at ngrok. ngrok's network authenticates and authorizes all requests before allowing ingress to your endpoint, so you don't need to add any services to your cluster or alter any routes. To enable the `oauth` action, create a new `NgrokTrafficPolicy` custom resource and apply it to your entire `Ingress` with an annotation. You can also apply the policy to a specific backend or as the default backend for an `Ingress`. See the doc on using the [Operator with Ingresses](/docs/gateway/k8s/guides/using-ingresses/#using-ngroktrafficpolicy-with-ingress). Edit your `ngrok-manifest.yaml` file with the following, leaving the `Service` and `Deployment` as they were. Note the new `annotations` field and the `NgrokTrafficPolicy` CR. ```yaml theme={null} ... --- # Configuration for ngrok's Kubernetes Operator apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tinyllama-ingress namespace: default annotations: k8s.ngrok.com/traffic-policy: oauth spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: tinyllama port: number: 80 --- # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: default spec: policy: on_http_request: - type: oauth config: provider: google ``` Re-apply your `ngrok-manifest.yaml` configuration: ```bash theme={null} kubectl apply -f ngrok-manifest.yaml ``` When you open your demo app again, ngrok asks you to log in with Google. To limit access to just yourself or your colleagues, tighten the policy. Use [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) and [CEL interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation) to reject OAuth logins that aren't under `example.com`. Update the `NgrokTrafficPolicy` portion of your manifest after changing `example.com` to your domain. ```yaml theme={null} # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: default spec: policy: on_http_request: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@example.com')" actions: - type: custom-response config: body: You are not authorized, ${actions.ngrok.oauth.identity.name}. status_code: 400 ``` Check your deployed tinyllama app again. If you log in with an email that isn't under your domain, ngrok rejects the request. # ngrok External Secrets integration Source: https://ngrok.com/docs/integrations/kubernetes-ingress/external-secrets ngrok integrates with External Secrets Operator to enable secure management of secrets used in ngrok Traffic Policy. This guide explains how to use the External Secrets Operator with ngrok to push Kubernetes secrets to ngrok vaults, where they can be referenced in your Traffic Policies for secure configuration management. The [External Secrets Operator integration](https://external-secrets.io/latest/provider/ngrok/) allows you to push Kubernetes secrets to ngrok vaults for use in Traffic Policies. ## Use cases * **Traffic Policy configuration:** Store API keys, tokens, and credentials that you can use to authenticate visitors with Traffic Policy actions * **Multi-environment management:** Sync different secrets to different ngrok vaults for dev, staging, and production * **Secret rotation:** Automatically propagate rotated secrets from Kubernetes to ngrok * **Compliance:** Use sensitive configuration data in Traffic Policies securely, without leaking them ## How it works External Secrets Operator watches for [`PushSecret`](https://external-secrets.io/latest/api/pushsecret/) resources in your cluster. When a `PushSecret` is created or updated, it reads the specified Kubernetes secret and pushes the secret data to your ngrok vault using the ngrok API. The secret then becomes available in ngrok for use in Traffic Policies. The Operator continues to sync changes based on the configured refresh interval, ensuring your ngrok secrets stay up-to-date. ### Limitations * Currently supports push operations only (Kubernetes → ngrok) * Pull operations (ngrok → Kubernetes) are not yet supported ## What you'll need * An ngrok account. * Your ngrok [API key](https://dashboard.ngrok.com/api-keys). * An [ngrok vault](https://dashboard.ngrok.com/vaults) created for storing secrets. * The [External Secrets Operator](https://external-secrets.io/latest/introduction/getting-started/) installed in your Kubernetes cluster. ## Configuration ### 1. Create ngrok API credentials First, store your ngrok API key in a Kubernetes secret: ```bash theme={null} kubectl create secret generic ngrok-credentials \ --from-literal=api-key= ``` ### 2. Configure SecretStore Next, create a SecretStore that connects to ngrok's API: ```yaml theme={null} apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: ngrok spec: provider: ngrok: # apiURL: Default "https://api.ngrok.com", for enterprise ngrok instances uncomment and use your API URL. auth: apiKey: secretRef: name: ngrok-credentials key: api-key vault: name: my-vault # Name of the ngrok vault to use for storing secrets ``` **Configuration properties:** * `auth.apiKey`: Reference to your ngrok API key (required) * `vault.name`: Name of your ngrok vault where secrets will be stored (required) * `apiURL`: API endpoint (optional, defaults to `https://api.ngrok.com`) ## Pushing secrets to ngrok ### Basic push configuration To sync a Kubernetes secret with ngrok, create a PushSecret resource: ```yaml theme={null} apiVersion: external-secrets.io/v1alpha1 kind: PushSecret metadata: name: ngrok-push-secret-example spec: deletionPolicy: Delete refreshInterval: 10m # Refresh interval for which push secret will reconcile secretStoreRefs: # A list of secret stores to push secrets to - name: ngrok # Must match SecretStore on the cluster kind: SecretStore selector: secret: name: SECRET_NAME # Source Kubernetes secret to be pushed data: - match: # The key in the Kubernetes secret to push. Leave empty to push all keys, JSON encoded. # secretKey: "" secretKey: MY_K8S_SECRET_KEY remoteRef: remoteKey: MY_NGROK_SECRET_NAME # The name of the secret in the ngrok vault ``` ### Adding metadata to secrets You can enhance your ngrok secrets with descriptions and custom metadata: ```yaml theme={null} apiVersion: external-secrets.io/v1alpha1 kind: PushSecret metadata: name: ngrok-push-secret-example spec: deletionPolicy: Delete refreshInterval: 10m # Refresh interval for which push secret will reconcile secretStoreRefs: # A list of secret stores to push secrets to - name: ngrok # Must match SecretStore on the cluster kind: SecretStore selector: secret: name: SECRET_NAME # Source Kubernetes secret to be pushed data: - match: # The key in the Kubernetes secret to push. Leave empty to push all keys, JSON encoded. # secretKey: "" secretKey: MY_K8S_SECRET_KEY remoteRef: remoteKey: MY_NGROK_SECRET_NAME # The name of the secret in the ngrok vault metadata: apiVersion: kubernetes.external-secrets.io/v1alpha1 kind: PushSecretMetadata spec: # See https://ngrok.com/docs/api/resources/secrets/#parameters # We currently support customizing the description and metadata for the secret. description: "This is a secret for the API credentials" # Metadata for the secret in the ngrok vault. This will be merged with auto-generated metadata. metadata: environment: production team: devops ``` ## What's next? * Learn how to use [secrets in Traffic Policies](/docs/gateway/traffic-policy/secrets/) to reference your synced secrets dynamically in policy actions. * Explore the [Traffic Policy Actions](/docs/gateway/traffic-policy/actions/) available to secure and manage traffic to your endpoints. * Learn more about the [ngrok Kubernetes Operator](/docs/gateway/k8s/) and how to configure Traffic Policies directly in your Kubernetes deployments. # Kubernetes ingress to GKE services with ngrok Source: https://ngrok.com/docs/integrations/kubernetes-ingress/google-kubernetes-engine Learn how to use the ngrok Kubernetes Operator on Google Kubernetes Engine (GKE) to quickly add secure Kubernetes ingress to your apps or APIs. This guide shows you how to add public ingress to an app running on Google Kubernetes Engine (GKE). You'll use the ngrok Kubernetes Operator to route public traffic to the app through an encrypted tunnel, then secure it with OAuth. The [ngrok Kubernetes Operator](https://github.com/ngrok/ngrok-operator) is the official open source controller for adding public and secure ingress to your Kubernetes services. It works with a GKE cluster as long as the cluster has outbound access to the ngrok service. ## What you'll need * A GKE cluster. If you don't have one, Google's [cluster creation guide](https://docs.cloud.google.com/kubernetes-engine/docs/how-to/creating-a-zonal-cluster) covers provisioning one. * An ngrok account. * `kubectl` and Helm 3.0.0+ installed on your local workstation. * The [ngrok Kubernetes Operator](/docs/gateway/k8s/) installed on your cluster. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as ``. ## Connect kubectl to your GKE cluster GKE authenticates `kubectl` through the `gcloud` CLI and the `gke-gcloud-auth-plugin` credential helper. Google's [cluster access guide](https://docs.cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl) covers installing the plugin and configuring cluster access. Once you're authenticated, add the cluster to your `KUBECONFIG`: ```bash theme={null} gcloud container clusters get-credentials --region --project ``` Confirm `kubectl` can reach the cluster before continuing: ```bash theme={null} kubectl get nodes ``` ## Install the ngrok Kubernetes Operator If you haven't already, install the operator with Helm. See the [Kubernetes ingress quickstart](/docs/getting-started/kubernetes/ingress) for the steps and how to pass your ngrok credentials. ## Install a sample application Create a manifest file (for example, `ngrok-manifest.yaml`) with the following contents. This deploys the [tinyllama](https://github.com/ngrok-samples/tinyllama) demo LLM application. Replace `` in the highlighted section with your reserved domain. On a free account, use a static subdomain; on a paid account, you can use a custom domain or a subdomain of `ngrok.app` or `ngrok.dev`. The first section creates the tinyllama demo app Service and Deployment; the highlighted section configures the ngrok Kubernetes Operator Ingress. ```yaml showLineNumbers theme={null} apiVersion: v1 kind: Service metadata: name: tinyllama spec: ports: - name: http port: 80 targetPort: 8080 selector: app: tinyllama --- apiVersion: apps/v1 kind: Deployment metadata: name: tinyllama spec: replicas: 1 selector: matchLabels: app: tinyllama template: metadata: labels: app: tinyllama spec: containers: - name: tinyllama image: ghcr.io/ngrok-samples/tinyllama:main ports: - name: http containerPort: 8080 --- # highlight-start # ngrok Kubernetes Operator Configuration apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tinyllama-ingress spec: ingressClassName: ngrok rules: - host: http: paths: - path: / pathType: Prefix backend: service: name: tinyllama port: number: 80 # highlight-end ``` Apply the manifest file to your cluster: ```bash theme={null} kubectl apply -f ngrok-manifest.yaml ``` **Troubleshooting:** If you get an error when applying the manifest, double-check that you've updated the `` value and try again. Open your reserved domain (for example, `https://my-awesome-k8s-cluster.ngrok.app`) in a browser to confirm the tinyllama app is accessible from the internet. ## Add authentication to your app With the [Traffic Policy system](/docs/gateway/traffic-policy/) and the [`oauth` action](/docs/gateway/traffic-policy/actions/oauth), ngrok handles OAuth entirely on its network. You don't need to add services to your cluster or change any routes, because ngrok authenticates and authorizes requests before they reach your endpoint. To enable the `oauth` action, create an `NgrokTrafficPolicy` custom resource and apply it to your `Ingress` with an annotation. You can also apply the policy to a specific backend or as the default backend for an `Ingress`. See the doc on using the [Operator with Ingresses](/docs/gateway/k8s/guides/using-ingresses/#using-ngroktrafficpolicy-with-ingress) for details. Edit your existing `ngrok-manifest.yaml`, leaving the `Service` and `Deployment` as they were. Note the new `annotations` field and the `NgrokTrafficPolicy` CR. ```yaml theme={null} ... --- # Configuration for ngrok's Kubernetes Operator apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tinyllama-ingress namespace: default annotations: k8s.ngrok.com/traffic-policy: oauth spec: ingressClassName: ngrok rules: - host: http: paths: - path: / pathType: Prefix backend: service: name: tinyllama port: number: 80 --- # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: default spec: policy: on_http_request: - type: oauth config: provider: google ``` Re-apply your `ngrok-manifest.yaml` configuration: ```bash theme={null} kubectl apply -f ngrok-manifest.yaml ``` When you open your demo app again, ngrok asks you to log in with Google. To limit access to just yourself or your colleagues, tighten the policy. Use [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) and [CEL interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation) to reject OAuth logins that aren't under `example.com`. Update the `NgrokTrafficPolicy` portion of your manifest after changing `example.com` to your domain. ```yaml theme={null} # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: default spec: policy: on_http_request: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@example.com')" actions: - type: custom-response config: body: You are not authorized, ${actions.ngrok.oauth.identity.name}. status_code: 400 ``` Check your deployed tinyllama app again. If you log in with an email that isn't under your domain, ngrok rejects the request. ## What's next? You've used the open source ngrok Kubernetes Operator to add public ingress to an app on GKE without managing complex Kubernetes networking. Because ngrok handles ingress and middleware execution, you can follow the same process for your production apps. To go further, explore the [Kubernetes docs](/docs/gateway/k8s/) for how the Operator works and how to integrate ngrok with an existing production cluster, or try [bindings](/docs/gateway/k8s/guides/bindings/) and [endpoint pooling](/docs/gateway/k8s/guides/pooling/). # Use ngrok's Global Server Load Balancing with DigitalOcean Source: https://ngrok.com/docs/integrations/kubernetes-ingress/gslb Layer load balancing between three or more globally distributed, cloud-based virtual machines in DigitalOcean using ngrok's GSLB. This guide shows you how to layer ngrok's [Global Server Load Balancing](/docs/gateway/domains/global-load-balancer/) (GSLB) on top of a VM-based deployment on DigitalOcean. GSLB improves performance and resiliency by distributing traffic to the nearest Point of Presence (PoP) and upstream service. Unlike a traditional GSLB deployment, ngrok routes traffic automatically without new infrastructure, IP provisioning, or DNS changes. You configure your ngrok agents for high availability, horizontal scaling, A/B testing, and more. ngrok's GSLB works on top of DigitalOcean's internal load balancers for additional global resiliency and lower latency. ## What you'll need * An ngrok account. * A DigitalOcean account. * Three Ubuntu 24.04 virtual machines (VMs) in three globally distributed regions (for example, New York, Sydney, and Frankfurt). * Hostnames should be unique, ideally using the datacenter location (for example, `nyc`, `sydney`, and `frankfurt`). * Docker and Docker Compose installed on each VM. ## 1. Reserve your ngrok domain First, [generate a new API key](https://dashboard.ngrok.com/api-keys) in the [ngrok dashboard](https://dashboard.ngrok.com/). Save the API key before closing the modal; it won't be displayed again. To simplify authenticating your account with the ngrok API, export the API key on your local workstation. ```bash theme={null} export NGROK_API_KEY= ``` Next, create a domain that is a subdomain of an [ngrok-managed domain](/docs/gateway/domains/). For an example deployment like this, `YOUR_COMPANY-digitalocean-gslb.ngrok.app` would work great. You can reserve your domain in one of two ways: with the [ngrok API](/docs/api/), or in the [ngrok dashboard](https://dashboard.ngrok.com/domains). With the ngrok API, reserve your domain on the [`/reserved_domains` endpoint](/docs/api-reference/reserveddomains/list) using the `NGROK_API_KEY` and `NGROK_DOMAIN` variables you exported. Export a variable for your new domain, which will be used in following API calls. ```bash theme={null} export NGROK_DOMAIN= ``` ```bash theme={null} curl \ -X POST https://api.ngrok.com/reserved_domains \ -H "Authorization: Bearer ${NGROK_API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ -d '{"description":"DigitalOcean Load Balancing","domain":"'${NGROK_DOMAIN}'"}' ``` **Custom domains within ngrok:** Using CNAMEs, ngrok can host an endpoint on your domain while also managing the complete TLS certificate lifecycle on your behalf. If you'd prefer to use a custom domain rather than an ngrok-managed one, [follow the guide](/docs/gateway/domains/custom-domains/) to set up your DNS and replace your custom domain in the `export` command above. ## 2. Create a Cloud Endpoint Create a `policy.yaml` file on your local workstation with the Traffic Policy for your endpoint. This policy forwards all incoming requests to `https://gslb-demo.internal`, a shared internal domain that all three of your VMs' ngrok agents will connect to. ngrok automatically routes each request to the nearest connected agent, providing global load balancing without additional configuration. ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://gslb-demo.internal on_error: continue - type: custom-response config: status_code: 503 body: "No upstream tunnel available." ``` Use [`jq`](https://jqlang.org/download/) to safely embed the Traffic Policy YAML in the JSON payload and create your Cloud Endpoint: ```bash theme={null} curl \ -X POST https://api.ngrok.com/endpoints \ -H "Authorization: Bearer ${NGROK_API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ --data "$(jq -n \ --arg url "https://${NGROK_DOMAIN}" \ --rawfile policy policy.yaml \ '{type:"cloud",url:$url,description:"DigitalOcean GSLB",traffic_policy:$policy}' )" ``` Export the `id` from the response for use in optional step 5. ```bash theme={null} export ENDPOINT_ID=ep_... ``` If you open `https://` now, you'll see an error from ngrok: the domain and endpoint configuration exist, but there are no ngrok agents connected yet. You can go to the [Endpoints dashboard](https://dashboard.ngrok.com/endpoints) to see your endpoint. ## 3. Install the ngrok Agent and an example workload on each VM To get some agents online and quickly see how ngrok's GSLB works, you'll use an [example API deployment](https://github.com/joelhans/ngrok-vps-gslb-demo). This demo deployment has four parts: * A straightforward Go-based API with a single endpoint at `/api`, which returns a randomly generated UUID and the machine's `hostname`, which should reflect the regions of your VMs. * A `Dockerfile` for containerizing said Go-based API. * A `docker-compose.yml` file for starting the API container and a containerized edition of the ngrok agent on the same network. * A `ngrok.yml` [agent configuration file](/docs/gateway/agent/config/v3/) to connect the agent to your ngrok account and the shared internal domain. Repeat the following steps on each VM: * Clone the demo repository. ```bash theme={null} git clone https://github.com/joelhans/ngrok-vps-gslb-demo cd ngrok-vps-gslb-demo ``` * Edit the `ngrok.yml` [ngrok agent configuration file](/docs/gateway/agent/config/v3/) with your `` (find it in the [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken)); it's different from the API key you created in the first step. All three VMs use the same `url` value, and ngrok automatically routes each incoming request to the nearest connected agent. ```yaml theme={null} version: 3 authtoken: log_level: debug log: stdout endpoints: - name: vps-demo url: https://gslb-demo.internal upstream: url: http://localhost:5000 ``` * Build and start the containerized API deployment, passing the hostname of the VM to the hostname of the Docker container. ```bash theme={null} HOSTNAME=$(hostname -f) docker compose up -d ``` ## 4. Test out ngrok's Global Server Load Balancing ngrok is now load-balancing your single API endpoint across all three distributed VMs. You can now ping your demo API at `/api` to see which agent responds. ```bash theme={null} curl ${NGROK_DOMAIN}/api [{"id":"1cf26269-ce8b-4f16-91f1-fdf7bc6d9e80","dc":"nyc"}] ``` To see the distribution in action, try a batch of `curl` requests. ```bash theme={null} for i in `seq 1 20`; do \ curl \ -X GET "${NGROK_DOMAIN}/api" ; \ done ``` You should see each VM respond with similar frequency: ```json skip-validation theme={null} [{"id":"c46bdbd9-b588-4ffa-8018-d299d0918bbc","dc":"sydney"}] [{"id":"dce5a46e-15af-4479-a073-8fbc7f0097c5","dc":"nyc"}] [{"id":"ecc6451c-bc25-43bd-8cb6-a162f019adf3","dc":"frankfurt"}] [{"id":"ecc6451c-bc25-43bd-8cb6-a162f019adf3","dc":"frankfurt"}] [{"id":"ecc6451c-bc25-43bd-8cb6-a162f019adf3","dc":"frankfurt"}] [{"id":"c46bdbd9-b588-4ffa-8018-d299d0918bbc","dc":"sydney"}] [{"id":"c46bdbd9-b588-4ffa-8018-d299d0918bbc","dc":"sydney"}] [{"id":"ecc6451c-bc25-43bd-8cb6-a162f019adf3","dc":"frankfurt"}] [{"id":"ecc6451c-bc25-43bd-8cb6-a162f019adf3","dc":"frankfurt"}] [{"id":"dce5a46e-15af-4479-a073-8fbc7f0097c5","dc":"nyc"}] [{"id":"ecc6451c-bc25-43bd-8cb6-a162f019adf3","dc":"frankfurt"}] [{"id":"dce5a46e-15af-4479-a073-8fbc7f0097c5","dc":"nyc"}] [{"id":"dce5a46e-15af-4479-a073-8fbc7f0097c5","dc":"nyc"}] [{"id":"c46bdbd9-b588-4ffa-8018-d299d0918bbc","dc":"sydney"}] [{"id":"ecc6451c-bc25-43bd-8cb6-a162f019adf3","dc":"frankfurt"}] [{"id":"c46bdbd9-b588-4ffa-8018-d299d0918bbc","dc":"sydney"}] [{"id":"c46bdbd9-b588-4ffa-8018-d299d0918bbc","dc":"sydney"}] [{"id":"dce5a46e-15af-4479-a073-8fbc7f0097c5","dc":"nyc"}] [{"id":"ecc6451c-bc25-43bd-8cb6-a162f019adf3","dc":"frankfurt"}] [{"id":"dce5a46e-15af-4479-a073-8fbc7f0097c5","dc":"nyc"}] ``` To confirm global load balancing, go to the [Endpoints dashboard](https://dashboard.ngrok.com/endpoints) and confirm all three agents are connected and traffic is flowing. ## Step 5 (optional): Extend with Traffic Policy Your demo API is globally load-balanced with equal distribution across your three VMs. You can extend your endpoint's Traffic Policy in two ways: adding API gateway features like header injection, or configuring custom weights for fine-grained traffic control. ### Add API gateway features Update your `policy.yaml` to include an `on_http_response` phase that adds headers to every response: ```yaml theme={null} on_http_request: - actions: - type: forward-internal config: url: https://gslb-demo.internal on_error: continue - type: custom-response config: status_code: 503 body: "No upstream tunnel available." on_http_response: - name: Add ngrok headers actions: - type: add-headers config: headers: country: ${conn.client_ip.geo.location.country_code} is-ngrok: "1" ``` Apply the updated policy to your Cloud Endpoint: ```bash theme={null} curl \ -X PATCH "https://api.ngrok.com/endpoints/${ENDPOINT_ID}" \ -H "Authorization: Bearer ${NGROK_API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ --data "$(jq -n \ --rawfile policy policy.yaml \ '{traffic_policy:$policy}' )" ``` When you request your API again, the response includes the `country` (with the appropriate code for your request origin) and `is-ngrok` headers. ```bash theme={null} curl -s -I -X GET https:///api HTTP/2 200 content-type: application/json country: U.S. date: Wed, 22 May 2024 14:53:42 GMT is-ngrok: 1 content-length: 63 ``` ### Configure weighted distribution To route traffic by explicit weight across specific regions, such as for A/B tests or shifting capacity, update each VM's `ngrok.yml` to use a unique per-region internal domain instead of the shared one: * NYC VM: `url: https://nyc.internal` * Sydney VM: `url: https://sydney.internal` * Frankfurt VM: `url: https://frankfurt.internal` Then create a `policy-weighted.yaml` with a Traffic Policy that uses `set-vars` to select a backend by weight on each request. Adjust the `weight` values as needed; the `cumulative_weights` list must be recomputed to match (each entry is the sum of all weights up to and including that index). ```yaml theme={null} on_http_request: - actions: - type: set-vars config: vars: - urls: - weight: 33 url: https://nyc.internal - weight: 33 url: https://sydney.internal - weight: 34 url: https://frankfurt.internal - cumulative_weights: [33, 66, 100] - weighted_chance: ${rand.int(0, 99)} - weighted_index: ${[0, 1, 2].filter(i, vars.cumulative_weights[i] > vars.weighted_chance)[0]} - weighted_url: ${vars.urls[vars.weighted_index].url} - actions: - type: forward-internal config: url: ${vars.weighted_url} on_error: continue - type: custom-response config: status_code: 503 body: "No upstream tunnel available." ``` Apply the weighted policy to your Cloud Endpoint: ```bash theme={null} curl \ -X PATCH "https://api.ngrok.com/endpoints/${ENDPOINT_ID}" \ -H "Authorization: Bearer ${NGROK_API_KEY}" \ -H "Content-Type: application/json" \ -H "Ngrok-Version: 2" \ --data "$(jq -n \ --rawfile policy policy-weighted.yaml \ '{traffic_policy:$policy}' )" ``` ## What's next? You now have a globally load-balanced API deployment using three DigitalOcean VMs, three ngrok agents, three secure tunnels, and a single convenient endpoint for your users. From here, you have many options for extending your use of ngrok's GSLB: * Spread the load further by deploying additional VMs in more regions and connecting each agent to `https://gslb-demo.internal`. ngrok automatically includes them in load balancing. * Add more VMs in an existing region and connect them to the same internal domain; ngrok will equally distribute traffic among all connected agents for that domain. * Provision a Kubernetes cluster with the same workload and the ngrok [Kubernetes Operator](/docs/gateway/k8s/) to load-balance between VM- and Kubernetes-based deployments of the same API or application. * Use the [weighted distribution](#configure-weighted-distribution) Traffic Policy for A/B tests by adjusting weights between regions. To learn more about ngrok's GSLB, see: * [Introducing Always-On Global Server Load Balancing](https://ngrok.com/blog-post/gslb-global-server-load-balancing) * [What is global server load balancing (GSLB)?](https://ngrok.com/blog-post/what-is-gslb-global-server-load-balancing) # Kubernetes Ingress Integrations Source: https://ngrok.com/docs/integrations/kubernetes-ingress/index Browse integration guides for using the ngrok Kubernetes Operator with major cloud providers, platforms, and service meshes. This page lists integration guides for using the ngrok Kubernetes Operator with major cloud providers, platforms, and service meshes. The ngrok Kubernetes Operator provides secure public ingress and middleware execution to your Kubernetes applications with ngrok. Whether you're running on major cloud providers, managing clusters with platform tools, or integrating with service meshes, these guides help you get started with ngrok ingress for your Kubernetes workloads. ### Cloud providers Learn how to get started running the ngrok Kubernetes Operator on AWS EKS clusters. Add secure Kubernetes ingress to your apps or APIs running on Google Kubernetes Engine (GKE). Deploy a new Kubernetes cluster and demo app via AKS, then add ingress to applications with ngrok's Kubernetes Operator. Launch a new cluster on DigitalOcean and use the Marketplace to provision the ngrok Kubernetes Operator. Set up secure ingress for applications running on MicroK8s clusters. ### Platforms and management tools Set up a local installation of Rancher to deploy a new RKE2 cluster and add ingress to applications. Deploy your next production-ready Kubernetes app with Rafay and the ngrok Kubernetes Operator. Integrate the ngrok Kubernetes Operator with Spectro Cloud's Kubernetes management platform. Use the ngrok Kubernetes Operator with vCluster virtual Kubernetes clusters. ### Service mesh and integrations Set up ingress to a microservices-based application connected via Linkerd's service mesh. Set up a local Consul service mesh to use the ngrok Kubernetes Operator with Consul. Add ingress to apps running in Kubernetes, then restrict access to only users authorized through Microsoft Entra ID. ### Advanced use cases Deploy an API using an APIOps-based deployment and lifecycle management process with Argo CD and the ngrok Kubernetes Operator. Learn how to layer load balancing between three or more globally distributed, cloud-based virtual machines. # Ingress to a microservices-based application connected via Linkerd's service mesh Source: https://ngrok.com/docs/integrations/kubernetes-ingress/k8s Set up a local cluster to demonstrate how to use the ngrok Kubernetes Operator with Linkerd. This guide walks you through setting up a local cluster (or using an existing one) to mesh a microservices-based application with Linkerd and use the ngrok Kubernetes Operator to route public traffic through an encrypted tunnel to your cluster. The ngrok Kubernetes Operator is the official controller for adding secure public ingress and middleware execution to your Kubernetes applications with ngrok. Linkerd is an open source service mesh: a set of network proxies that handle communications between microservices and add observability, security, and reliability at the platform level. Linkerd enables mutual TLS (mTLS) between microservices for confidentiality and authenticity. When integrated, the ngrok Kubernetes Operator and Linkerd simplify your networking stack (internal and external traffic) and give you additional monitoring and resiliency. ## What you'll need * An existing remote or local Kubernetes cluster or the minikube CLI installed locally to create a new testing cluster. * The Linkerd 2.x CLI installed locally (helper script, Homebrew, or binary in your `$PATH`). * An ngrok account. * kubectl and Helm 3.0.0+ installed on your local workstation. * The [ngrok Kubernetes Operator](/docs/gateway/k8s/) installed on your cluster. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as ``. ## Set up a local development cluster * Create a local Kubernetes cluster with [minikube](https://minikube.sigs.k8s.io/docs/start/). Assign it a profile named `ngrok-linkerd` with `-p`, and for the best compatibility with Linkerd, use the `containerd` container runtime. ```bash theme={null} minikube start -p ngrok-linkerd --container-runtime=containerd ``` If your OS does not support containerd, you can run minikube without specifying the container runtime. ```bash theme={null} minikube start -p ngrok-linkerd ``` If minikube defaults to using the `docker` runtime, you will likely see an error related to root privileges when installing Linkerd to your cluster. The error includes a workaround to let you install Linkerd despite using the `docker` runtime. * Use `kubectl` to verify your local cluster is running properly. ```bash theme={null} kubectl get namespaces NAME STATUS AGE default Active 72s kube-node-lease Active 72s kube-public Active 72s kube-system Active 72s ``` ## Deploy Linkerd's service mesh to your cluster Install the Linkerd CLI and deploy the control plane by following Linkerd's [getting-started guide](https://linkerd.io/2/getting-started/), which covers `linkerd install --crds`, `linkerd install`, and the pre- and post-install validation with `linkerd check`. When `linkerd check` reports a healthy control plane, continue below to deploy a meshed application and route public traffic to it with ngrok. ## Deploy an example microservices-based application To demonstrate how Linkerd and the ngrok Kubernetes Operator integrate to add additional observability, security, and reliability into your cluster, you'll deploy the Emojivoto demo application, which was developed by Buoyant, the company that originally developed Linkerd. * Reserve an ngrok static subdomain for ingress from the [Domains](https://dashboard.ngrok.com/domains) page of the dashboard if you don't have one already. This subdomain will be your `NGROK_DOMAIN` for the remainder of this guide, and it provides a public route for HTTP, HTTPS, and TLS traffic. * Deploy Emojivoto to the `emojivoto` namespace. ```bash theme={null} curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/emojivoto.yml \ | kubectl apply -f - ``` * Add meshing by injecting Linkerd's data plane proxies into each pod with a rolling deploy. The following command retrieves all deployments from the previous step, injects the Linkerd proxy, and redeploys each pod. ```bash theme={null} kubectl get -n emojivoto deploy -o yaml \ | linkerd inject - \ | kubectl apply -f - ``` * Verify your data plane with `linkerd -n emojivoto check --proxy`; it should end with a healthy status check. ```bash theme={null} linkerd -n emojivoto check --proxy ... Status check results are √ ``` * Create a new `emojivoto-ingress.yaml` file and add the following YAML content. It tells the ngrok Kubernetes Operator to route traffic on your `NGROK_DOMAIN` to the `web-svc` service you created when deploying Emojivoto. Edit line `9` of the manifest below (the `NGROK_DOMAIN` variable) with your ngrok subdomain (for example, `one-two-three.ngrok.app`). ```yaml showLineNumbers theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: emojivoto-ingress namespace: emojivoto spec: ingressClassName: ngrok rules: - host: http: paths: - path: / pathType: Prefix backend: service: name: web-svc port: number: 80 ``` * Apply the `emojivoto-ingress.yaml` manifest you just created. ```bash theme={null} kubectl apply -f emojivoto-ingress.yaml ``` Give your cluster a few moments to launch the necessary resources and for ngrok to pick up the new tunnel. **Troubleshooting:** If you see an error when applying the manifest, double-check that you've updated the `NGROK_DOMAIN` value and try again. * Access your Emojivoto application by navigating to your ngrok domain (for example, `https://one-two-three.ngrok.app`) in your browser. ## Add Linkerd's dashboard to verify meshing and mTLS A service mesh adds observability and secures pod-to-pod traffic with mTLS, so confirm that your deployments and pods are properly meshed. * Install the Linkerd dashboard. ```bash theme={null} linkerd viz install | kubectl apply -f - ``` * To verify mTLS, restart all pods in the `emojivoto` namespace to enable tapping. ```bash theme={null} kubectl -n emojivoto rollout restart deploy ``` * Use Linkerd's tap feature: run `linkerd viz -n emojivoto tap deploy` to stream traffic from all pods in the `emojivoto` namespace to your terminal. The Emojivoto app generates traffic automatically, so you'll see a consistent stream of requests. ```bash theme={null} linkerd viz -n emojivoto tap deploy ... req id=0:11 proxy=out src=10.244.0.15:43706 dst=10.244.0.2:8080 tls=true :method=POST :authority=emoji-svc.emojivoto:8080 :path=/emojivoto.v1.EmojiService/ListAll ``` You should see `tls=true` in all of these requests between these pods. You can also use `kubectl get pods -o wide` to see the IP address of each pod, which lets you verify the source and destination of each request. For example, the request shown above is the `web-svc` service sending a POST request, with mTLS enabled, to the `emoji-svc` service, which maintains and stores the votes database. * Run `linkerd viz dashboard &` to open the Linkerd dashboard in your browser. The default dashboard shows golden metrics (success rates, traffic, latencies per namespace) and the number of meshed pods per namespace. Some of the auto-generated traffic is designed to fail, which shows how to use the dashboard to debug pod-to-pod issues. ## What's next? You've integrated a demo microservices application with Linkerd's service mesh and ngrok's Kubernetes Operator, adding security and reliability without configuring middleware, network interfaces, mTLS, or new monitoring services yourself. After deploying this local demo, you have a few options for moving forward. ### Clean up To clean up from the work you've done for this local demo, you can stop and delete your minikube cluster with the `minikube` CLI: ```bash theme={null} minikube stop -p ngrok-linkerd minikube delete -p ngrok-linkerd ``` Then remove the Linkerd CLI from your local workstation: ```bash theme={null} rm -rf $HOME/.linkerd2 ``` ### Extend your ngrok Kubernetes Operator and Linkerd integration For production and real-world applications, explore installing Linkerd with Helm and production runbooks. In more complex scenarios, you can follow the same steps to install the ngrok Kubernetes Operator and configure an Ingress so ngrok handles routing and middleware for simplicity, global load balancing, and automatic encryption. See the [ngrok Kubernetes Operator GitHub repository](https://github.com/ngrok/ngrok-operator) and [project documentation](https://github.com/ngrok/ngrok-operator/tree/main/docs) for more details. # Kubernetes ingress to apps/APIs on clusters managed by Canonical MicroK8s Source: https://ngrok.com/docs/integrations/kubernetes-ingress/microk8s-k8s Add ingress to any app running in a Kubernetes cluster managed by Canonical MicroK8s using the ngrok Kubernetes Operator. This guide shows you how to add public ingress to an app running on a Canonical MicroK8s cluster. You'll use the [ngrok Kubernetes Operator](/docs/gateway/k8s/) with the Kubernetes Gateway API to route public traffic to the app through an encrypted tunnel. ## What you'll need * A MicroK8s cluster reachable with `kubectl`. If you don't have one, Canonical's [MicroK8s get-started guide](https://canonical.com/microk8s/docs/getting-started) covers installing it. * An ngrok account. * `kubectl` and Helm 3.0.0+ installed on your local workstation. * The [ngrok Kubernetes Operator](/docs/gateway/k8s/) installed on your cluster. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as `NGROK_DOMAIN`. ## Set up your MicroK8s cluster This guide assumes a single-node MicroK8s cluster reachable with `kubectl`. MicroK8s runs on your local workstation, an IoT device, a virtual machine in the cloud, or any system running one of [the supported Linux flavors](https://snapcraft.io/microk8s). If you don't have a cluster yet, follow Canonical's [MicroK8s get-started guide](https://canonical.com/microk8s/docs/getting-started) to install the binary for your target system. MicroK8s deploys a single-node Kubernetes cluster automatically. Confirm your cluster is ready before continuing: ```bash theme={null} microk8s status --wait-ready ``` If you see `microk8s is running`, your cluster is ready. ## Install an example app Deploy the demo app you'll expose through ngrok. Create a new Kubernetes manifest (`tinyllama.yaml`) with the YAML below. This manifest defines the tinyllama demo LLM application from [ngrok-samples/tinyllama](https://github.com/ngrok-samples/tinyllama) as a service and deployment. ```yaml showLineNumbers theme={null} apiVersion: v1 kind: Service metadata: name: tinyllama namespace: default spec: ports: - name: http port: 80 targetPort: 8080 selector: app: tinyllama --- apiVersion: apps/v1 kind: Deployment metadata: name: tinyllama namespace: default spec: replicas: 1 selector: matchLabels: app: tinyllama template: metadata: labels: app: tinyllama spec: containers: - name: tinyllama image: ghcr.io/ngrok-samples/tinyllama:main ports: - name: http containerPort: 8080 ``` Apply the manifest to your MicroK8s cluster: ```bash theme={null} kubectl apply -f tinyllama.yaml ``` ## Configure the ngrok Kubernetes Operator With your example app running alongside the ngrok Kubernetes Operator, configure the [Kubernetes Gateway API](/docs/gateway/k8s/?k8s-install=gatewayAPI) to route traffic arriving on `NGROK_DOMAIN` to the `tinyllama` service. First you'll create a [GatewayClass](https://gateway-api.sigs.k8s.io/api-types/gatewayclass/), which represents a class of cluster-level [Gateways](https://gateway-api.sigs.k8s.io/api-types/gateway/). Then you'll configure the Gateway with a listener and an [HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) that specifies how the Gateway routes requests. * Create a new file called `gatewayclass.yaml` on your workstation with the following YAML: ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: ngrok spec: controllerName: ngrok.com/gateway-controller ``` * Install the GatewayClass: ```bash theme={null} kubectl apply -f gatewayclass.yaml ``` * Create a new file named `tinyllama-gateway.yaml` on your workstation with the following YAML, replacing `NGROK_DOMAIN` with the domain you reserved: ```yaml showLineNumbers theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: tinyllama-gateway namespace: default spec: gatewayClassName: ngrok listeners: - name: tinyllama-web protocol: HTTP port: 80 hostname: "NGROK_DOMAIN" allowedRoutes: namespaces: from: Same --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: tinyllama-route namespace: default spec: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: tinyllama-gateway namespace: default hostnames: - "NGROK_DOMAIN" rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: tinyllama kind: Service port: 80 ``` The `Gateway` allows north/south traffic to enter the cluster from external requests, and the `HTTPRoute` terminates the connection at the pod running the `tinyllama` service. * Apply the Gateway and HTTPRoute: ```bash theme={null} kubectl apply -f tinyllama-gateway.yaml ``` * Access your tinyllama demo app by navigating to your ngrok domain, for example, `https://NGROK_DOMAIN`. ngrok's network and the ngrok Kubernetes Operator route traffic to your app from any device or external network. ## What's next? You've used the open source ngrok Kubernetes Operator to add public ingress to an app on MicroK8s with the Kubernetes Gateway API. Because ngrok handles ingress and middleware execution, you can follow the same process for your production apps. To go further, explore the [Kubernetes docs](/docs/gateway/k8s/) for how the Operator works and how to integrate ngrok with an existing production cluster, or try [bindings](/docs/gateway/k8s/guides/bindings/) and [endpoint pooling](/docs/gateway/k8s/guides/pooling/). # Ingress to Kubernetes apps managed by Rafay Source: https://ngrok.com/docs/integrations/kubernetes-ingress/rafay-k8s Add ingress to any app running in a Kubernetes cluster managed by Rafay using the ngrok Kubernetes Operator. This guide shows you how to add public ingress to an app running on a Kubernetes cluster managed by Rafay. You'll install the [ngrok Kubernetes Operator](/docs/gateway/k8s/), optionally packaged as a Rafay blueprint, then use it to route public traffic to a demo app through a secure tunnel. Rafay is a SaaS-based cloud controller that helps platform and DevOps teams manage their Kubernetes clusters and cloud environments. Packaging the ngrok Kubernetes Operator as a blueprint lets you apply the same ingress setup to any number of Rafay-managed clusters. ## What you'll need * A Rafay account. * An account with the privileges to create a cluster on one of the following managed Kubernetes services: Amazon EKS, Azure AKS, Google GKE, or a data center/edge or private cloud where you can deploy an upstream Kubernetes cluster. * An ngrok account. * `kubectl` and Helm 3.0.0+ installed on your local workstation. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as `NGROK_DOMAIN`. ## Create a new cluster with Rafay This guide assumes a cluster provisioned and managed by Rafay. If you don't have one, follow the getting started guide for your Kubernetes service in [Rafay's documentation](https://docs.rafay.co/), completing the **Provision** steps for your provider. ## Install the ngrok Kubernetes Operator First, gather the ngrok credentials the Operator needs: * Log in to the [ngrok dashboard](https://dashboard.ngrok.com/) to get your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) and create an [API key](https://dashboard.ngrok.com/api-keys). * Create a file named `ngrok-values.yaml` with the values below, replacing the placeholders with your authtoken and API key: ```yaml theme={null} credentials: apiKey: authtoken: ``` The Operator installs from the official Helm chart in the `https://charts.ngrok.com` repository. For the standalone Helm install with your ngrok credentials, see the [Kubernetes ingress quickstart](/docs/getting-started/kubernetes/ingress); for chart configuration options, see the [Helm configuration reference](/docs/gateway/k8s/installation/helm). To install it through Rafay instead, add `https://charts.ngrok.com` as a repository, create a Helm 3 add-on for the `ngrok-operator` chart with your `ngrok-values.yaml` values file, then add that add-on to a blueprint and apply the blueprint to your cluster. See Rafay's [documentation](https://docs.rafay.co/) for creating repositories, add-ons, and blueprints. Rafay deploys the ngrok Kubernetes Operator when it applies the blueprint. ## Deploy an app with Rafay You'll deploy the AKS Store demo app and an ngrok ingress as a Rafay workload. * Download the AKS Store manifest to your local workstation: ```bash theme={null} curl https://raw.githubusercontent.com/Azure-Samples/aks-store-demo/main/aks-store-quickstart.yaml -o aks-store-workload.yaml ``` * Add the following ingress configuration to the bottom of your `aks-store-workload.yaml` file, replacing `NGROK_DOMAIN` with the domain you reserved. This tells the ngrok Kubernetes Operator to route traffic arriving on `NGROK_DOMAIN` to the `store-front` service on port `80`. ```yaml showLineNumbers theme={null} --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: store-ingress namespace: default spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: store-front port: number: 80 ``` * Create a Rafay workload from the `aks-store-workload.yaml` file using the `default` namespace, then place it on your cluster and publish it. See Rafay's [documentation](https://docs.rafay.co/) for creating and publishing workloads. Give Rafay a few minutes to provision the AKS Store resources. * Navigate to your ngrok domain (for example, `https://NGROK_DOMAIN`) in your browser to see the app deployed and publicly networked via Rafay, Kubernetes, and ngrok. ngrok routes requests through the ngrok Kubernetes Operator to the `store-front` service. ## What's next? You've used the open source ngrok Kubernetes Operator to add public ingress to a demo app on a Rafay-managed cluster. Because ngrok handles ingress and middleware execution, you can follow the same process for your other apps, and packaging the Operator as a Rafay blueprint lets you repeat it across clusters. Learn more about the ngrok Kubernetes Operator in the [Kubernetes docs](/docs/gateway/k8s/), or contribute to its development on [GitHub](https://github.com/ngrok/ngrok-operator). # Kubernetes ingress to applications and clusters managed by Rancher Source: https://ngrok.com/docs/integrations/kubernetes-ingress/rancher-k8s Deploy an RKE2 cluster with Rancher and add ingress to applications with ngrok's Kubernetes Operator. This guide shows you how to add public ingress to an app running on an RKE2 cluster managed by Rancher. You'll use the [ngrok Kubernetes Operator](/docs/gateway/k8s/) to route public traffic to the app through an encrypted tunnel. Rancher is an open source multi-cluster management platform from SUSE that DevOps teams use to run Kubernetes across on-prem and cloud environments. The ngrok Kubernetes Operator adds secure public ingress and middleware execution to the apps running on those clusters. ## What you'll need * One or more Linux hosts that meet Rancher's requirements for operating as Kubernetes nodes. These can be on-prem or cloud virtual machines or bare-metal servers. * An ngrok account. * `kubectl` and Helm 3.0.0+ installed on your local workstation. * The [ngrok Kubernetes Operator](/docs/gateway/k8s/) installed on your cluster. You can install it through Rancher, but this guide recommends the official Helm chart. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as `NGROK_DOMAIN`. ## Set up your Rancher cluster This guide assumes an RKE2 cluster managed by Rancher and reachable with `kubectl`. Disable Rancher's built-in NGINX ingress when you create the cluster, since ngrok provides ingress. If you don't have this yet: * Install the Rancher management server. Rancher's [single-node Docker install](https://ranchermanager.docs.rancher.com/getting-started/installation-and-upgrade/other-installation-methods/rancher-on-a-single-node-with-docker) is a quick option for test and demonstration environments. * Create an RKE2 cluster and register your Linux nodes, following Rancher's guide for [launching Kubernetes on existing nodes](https://ranchermanager.docs.rancher.com/reference-guides/cluster-configuration/rancher-server-configuration/use-existing-nodes/). * Set up `kubectl` with the cluster's kubeconfig, which you can copy from the Rancher dashboard. Confirm your cluster is reachable before continuing: ```bash theme={null} kubectl get namespaces ``` ## Install a sample application With the ngrok Kubernetes Operator running on your cluster, add a sample application. The Operator connects this application to ngrok, which routes external traffic to it through your Rancher-managed cluster. * Create a new Kubernetes manifest (`tinyllama.yaml`) with the contents below, replacing `NGROK_DOMAIN` with the domain you reserved. This manifest defines the [tinyllama](https://github.com/ngrok-samples/tinyllama) demo LLM application service and deployment, then adds an Ingress that tells the ngrok Kubernetes Operator to route traffic arriving on `NGROK_DOMAIN` to the `tinyllama` service. ```yaml showLineNumbers theme={null} apiVersion: v1 kind: Service metadata: name: tinyllama spec: ports: - name: http port: 80 targetPort: 8080 selector: app: tinyllama --- apiVersion: apps/v1 kind: Deployment metadata: name: tinyllama spec: replicas: 1 selector: matchLabels: app: tinyllama template: metadata: labels: app: tinyllama spec: containers: - name: tinyllama image: ghcr.io/ngrok-samples/tinyllama:main ports: - name: http containerPort: 8080 --- # ngrok Kubernetes Operator configuration apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tinyllama-ingress spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: tinyllama port: number: 80 ``` * Apply the `tinyllama.yaml` manifest to your RKE2 cluster. ```bash theme={null} kubectl apply -f tinyllama.yaml ``` * Access your tinyllama demo app by navigating to your ngrok domain (for example, `https://NGROK_DOMAIN`). ngrok's network and the Operator route traffic to your app from any device or external network. ## What's next? You've used the open source ngrok Kubernetes Operator to add public ingress to a Rancher-managed cluster and sample application without worrying about IPs, network interfaces, or VPC routing. Because ngrok offloads ingress and middleware execution to its global network, you can follow a similar process for Rancher-managed clusters in any on-prem or cloud Kubernetes environment, including EKS and GKE. To move this proof of concept toward production, back up your Rancher installation and migrate it to a high-availability cluster with Rancher's [backup, restore, and disaster recovery docs](https://ranchermanager.docs.rancher.com/how-to-guides/new-user-guides/backup-restore-and-disaster-recovery). Your ngrok ingress configuration carries over with no extra changes. Learn more about the ngrok Kubernetes Operator in the [Kubernetes docs](/docs/gateway/k8s/), or contribute to its development on the [GitHub repository](https://github.com/ngrok/ngrok-operator). # Ingress to Kubernetes apps deployed on Spectro Cloud Palette Source: https://ngrok.com/docs/integrations/kubernetes-ingress/spectro-cloud-k8s Add ingress to any app running in a Kubernetes cluster managed by Spectro Cloud's Palette platform using the ngrok Kubernetes Operator. This guide shows you how to add public ingress to an app running in a Kubernetes cluster managed by [Spectro Cloud's Palette](https://docs.spectrocloud.com/) platform. You'll use the ngrok Kubernetes Operator, which Palette ships as a ready-to-apply pack, to route public traffic to your app. Palette is a profile-based Kubernetes management platform that deploys clusters from reusable layers of Helm charts, manifests, and packs. You define the ngrok Kubernetes Operator and your app as add-on profiles, then attach them when you create a cluster. ## What you'll need * A Palette account with Spectro Cloud. * An ngrok account. * An account with privileges to create a cluster on one of the providers Palette supports, such as Amazon EKS, Azure AKS, or Google GKE. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as `NGROK_DOMAIN`. ## 1. Create a cluster profile in Palette Palette uses an infrastructure cluster profile to define the base layers of a Kubernetes cluster: the OS, Kubernetes version, network, and storage packs. If you already have a cluster profile, skip to [Create your cluster with Palette](#4-create-your-cluster-with-palette). If you already have a functioning cluster in Palette, skip to [Create an add-on cluster profile for an example app](#3-create-an-add-on-cluster-profile-for-an-example-app). Follow Palette's [Create Cluster Profiles](https://docs.spectrocloud.com/profiles/cluster-profiles/create-cluster-profiles/) documentation to create a `Full` infrastructure profile for your chosen cloud provider, leaving the pack defaults in place. ## 2. Create an add-on cluster profile for the ngrok Kubernetes Operator Your infrastructure profile defines the cluster's base layers, but not a way to handle ingress. Palette ships a ready-to-apply pack for the [ngrok Kubernetes Operator](/docs/gateway/k8s/), the official controller for adding public and secure ingress to your Kubernetes services. Create a new `Add-on` cluster profile and add the **ngrok Kubernetes Operator** pack from the **Ingress** section of the pack registry. See Palette's docs on [add-on profiles](https://docs.spectrocloud.com/profiles/cluster-profiles/create-cluster-profiles/create-addon-profile/) for the steps. In the pack's YAML, find the `credentials` block and set `apiKey` and `authtoken` to the values from your ngrok account: ```yaml theme={null} credentials: secret: name: "" apiKey: "" authtoken: "" ``` To create an API key, go to the [**API Keys**](https://dashboard.ngrok.com/api-keys) page of the ngrok dashboard, create a new key, and copy it into `apiKey`. Find your authtoken under [**Your Authtoken**](https://dashboard.ngrok.com/get-started/your-authtoken) and copy it into `authtoken`. Palette hides these values after you enter them. On a free ngrok account, you can run only one ngrok agent at a time. Set the pack's `replicaCount` parameter to `1` so the Operator works correctly. Save the pack to add the layer to your profile. ## 3. Create an add-on cluster profile for an example app This section deploys the `tinyllama` demo app and an `Ingress` that tells the ngrok Kubernetes Operator to route your reserved domain to it. Create another `Add-on` cluster profile named `tinyllama`. Add a manifest layer, then add the deployment and service manifests below. See Palette's docs on [adding a manifest to a profile](https://docs.spectrocloud.com/profiles/cluster-profiles/create-cluster-profiles/create-addon-profile/create-manifest-addon/) for the steps. Deployment manifest: ```yaml showLineNumbers theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: tinyllama spec: replicas: 1 selector: matchLabels: app: tinyllama template: metadata: labels: app: tinyllama spec: containers: - name: tinyllama image: ghcr.io/ngrok-samples/tinyllama:main ports: - name: http containerPort: 8080 ``` Service manifest: ```yaml showLineNumbers theme={null} apiVersion: v1 kind: Service metadata: name: tinyllama spec: ports: - name: http port: 80 targetPort: 8080 selector: app: tinyllama ``` Add an `Ingress` manifest, replacing `NGROK_DOMAIN` with the domain you reserved: ```yaml showLineNumbers theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tinyllama-ingress spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: tinyllama port: number: 80 ``` Save the layer to finish the profile. ## 4. Create your cluster with Palette You can now create the cluster and attach the profiles you built. Follow Palette's [Creating Clusters](https://docs.spectrocloud.com/clusters/) documentation for your provider (see [Public Cloud Clusters](https://docs.spectrocloud.com/clusters/public-cloud/) for EKS, AKS, and GKE). When Palette prompts you for cluster profiles, select the infrastructure profile from step 1, then attach the ngrok Kubernetes Operator and `tinyllama` add-on profiles. Deployment can take several minutes depending on your profile, node pool, and region. Once Palette reports the cluster as `Running`, open your `NGROK_DOMAIN` in a browser. ngrok routes requests to the ngrok Kubernetes Operator, which forwards them to the `tinyllama` service. ## What's next? You've used Spectro Cloud's Palette to build declarative cluster profiles and deploy an app with public ingress through the ngrok Kubernetes Operator. You can follow the same process to expose your own apps by swapping the manifests in step 3. ### Clean up Delete the cluster from its **Overview** in Palette. Palette sends the requests to your cloud provider to remove the cluster resources, and ngrok deletes the endpoint when the cluster goes away. ### Extend your ngrok Kubernetes Operator and Palette integration Read the [Kubernetes Operator docs](/docs/gateway/k8s/) for more detail, including [how it works](/docs/gateway/k8s/how-it-works/) and how to [apply an `NgrokTrafficPolicy` resource](/docs/gateway/k8s/guides/using-ingresses/#using-ngroktrafficpolicy-with-ingress) to your `Ingress` to start using the [Traffic Policy system](/docs/gateway/traffic-policy/). # Ingress to Kubernetes apps and services with vcluster Source: https://ngrok.com/docs/integrations/kubernetes-ingress/vcluster-k8s Set up a local virtual cluster to use the ngrok Kubernetes Operator with vcluster. This guide shows you how to add public ingress to an app running on a [vcluster](https://www.vcluster.com/) virtual cluster. You'll use the ngrok Kubernetes Operator to route public traffic to the app, then secure it with OAuth. vcluster is an open source project for creating virtual clusters that run inside the namespaces of a host cluster. The pods you deploy on a vcluster are scheduled on the underlying cluster, while other resources, such as deployments and CRDs, exist only inside the virtual cluster. This isolation makes vcluster useful for development environments, internal developer platforms, and experiments that still need to route external traffic. ## What you'll need * The [vcluster CLI](https://www.vcluster.com/docs/vcluster/deploy/basics) installed locally. * A Kubernetes cluster to host the virtual cluster. If you don't have one, [minikube](https://minikube.sigs.k8s.io/docs/start/) creates a local cluster for testing. * An ngrok account. * `kubectl` and Helm 3.0.0+ installed on your local workstation. * The [ngrok Kubernetes Operator](/docs/gateway/k8s/) installed on your virtual cluster. * A reserved domain from the ngrok [dashboard](https://dashboard.ngrok.com/domains) or [API](/docs/api-reference/reserveddomains/list); this guide refers to it as `NGROK_DOMAIN`. ## Set up a local virtual cluster with vcluster To follow this guide, you need a host Kubernetes cluster with a virtual cluster running on it. If you already have one, skip to [Install a sample application](#install-a-sample-application). If you don't have a host cluster, create one locally with minikube: ```bash theme={null} minikube start --profile dc1 --memory 4096 ``` Create a virtual cluster named `my-vcluster`. This creates a namespace on the host cluster and switches your active kube context to the new virtual cluster: ```bash theme={null} vcluster create my-vcluster --expose-local ``` Confirm the virtual cluster is running: ```bash theme={null} kubectl get namespaces ``` If your context is no longer pointed at the virtual cluster, run `vcluster connect my-vcluster` to reconnect. See vcluster's [deployment docs](https://www.vcluster.com/docs/vcluster/deploy/basics) for other ways to create and connect to a virtual cluster. Install the [ngrok Kubernetes Operator](/docs/getting-started/kubernetes/ingress) on the virtual cluster before continuing. ## Install a sample application With the ngrok Kubernetes Operator running on your virtual cluster, deploy a sample application to see how the Operator routes external traffic to it. Create a Kubernetes manifest named `tinyllama.yaml` with the contents below. It defines the `tinyllama` demo app from [ngrok-samples/tinyllama](https://github.com/ngrok-samples/tinyllama) (a `Service` and `Deployment`), then adds an `Ingress` that tells the ngrok Kubernetes Operator to route `NGROK_DOMAIN` to the `tinyllama` service. Replace `NGROK_DOMAIN` with the domain you reserved: ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: tinyllama namespace: default spec: ports: - name: http port: 80 targetPort: 8080 selector: app: tinyllama --- apiVersion: apps/v1 kind: Deployment metadata: name: tinyllama namespace: default spec: replicas: 1 selector: matchLabels: app: tinyllama template: metadata: labels: app: tinyllama spec: containers: - name: tinyllama image: ghcr.io/ngrok-samples/tinyllama:main ports: - name: http containerPort: 8080 --- # ngrok Kubernetes Operator configuration apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tinyllama-ingress namespace: default spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: tinyllama port: number: 80 ``` Apply the manifest to your virtual cluster: ```bash theme={null} kubectl apply -f tinyllama.yaml ``` **Troubleshooting:** If you get an error when applying the manifest, confirm that you've replaced `NGROK_DOMAIN` in `tinyllama.yaml` with your reserved domain and try again. Open your reserved domain (for example, `https://NGROK_DOMAIN`) in a browser to see the demo app. ngrok routes requests to the ngrok Kubernetes Operator, which forwards them to the `tinyllama` service as long as your virtual cluster is running. ## Add OAuth protection to your demo app Now that your demo app is publicly accessible through ngrok, you can add [capabilities](/docs/gateway/endpoints/http/) like authentication without deploying extra infrastructure. This section restricts access to Google accounts under a specific domain. With the [Traffic Policy system](/docs/gateway/traffic-policy/) and the [`oauth` action](/docs/gateway/traffic-policy/actions/oauth), ngrok handles OAuth entirely on its network. You don't need to add services to your cluster or change any routes, because ngrok authenticates and authorizes requests before they reach your endpoint. To enable the `oauth` action, create an `NgrokTrafficPolicy` custom resource and apply it to your `Ingress` with an annotation. You can also apply the policy to a specific backend or as the default backend for an `Ingress`. See the doc on using the [Operator with Ingresses](/docs/gateway/k8s/guides/using-ingresses/#using-ngroktrafficpolicy-with-ingress) for details. Edit your `tinyllama.yaml` manifest with the following, leaving the `Service` and `Deployment` as they were. Note the new `annotations` field and the `NgrokTrafficPolicy` CR. ```yaml theme={null} ... --- # Configuration for ngrok's Kubernetes Operator apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tinyllama-ingress namespace: default annotations: k8s.ngrok.com/traffic-policy: oauth spec: ingressClassName: ngrok rules: - host: NGROK_DOMAIN http: paths: - path: / pathType: Prefix backend: service: name: tinyllama port: number: 80 --- # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: default spec: policy: on_http_request: - type: oauth config: provider: google ``` Re-apply your manifest: ```bash theme={null} kubectl apply -f tinyllama.yaml ``` When you open your demo app again, ngrok asks you to log in with Google. To limit access to just yourself or your colleagues, tighten the policy. Use [expressions](/docs/gateway/traffic-policy/how-it-works#expressions) and [CEL interpolation](/docs/gateway/traffic-policy/how-it-works#cel-interpolation) to reject OAuth logins that aren't under `example.com`. Update the `NgrokTrafficPolicy` portion of your manifest after changing `example.com` to your domain: ```yaml theme={null} # Traffic Policy configuration for OAuth apiVersion: ngrok.k8s.ngrok.com/v1alpha1 kind: NgrokTrafficPolicy metadata: name: oauth namespace: default spec: policy: on_http_request: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@example.com')" actions: - type: custom-response config: body: You are not authorized, ${actions.ngrok.oauth.identity.name}. status_code: 400 ``` Check your deployed app again. If you log in with an email that isn't under your domain, ngrok rejects the request. ## What's next? You've used the open source ngrok Kubernetes Operator to add secure ingress to an app on a virtual cluster without managing IPs, network interfaces, or VPC routing. Because ngrok handles ingress and middleware execution, you can follow the same process for any Kubernetes environment, such as EKS, GKE, and OpenShift. To clean up, disconnect from your virtual cluster and delete it. This removes the namespace and all of its resources, returning the host cluster to its initial state: ```bash theme={null} vcluster disconnect vcluster delete my-vcluster ``` To go further, explore the [Kubernetes docs](/docs/gateway/k8s/) for how the Operator works and how to integrate ngrok with an existing production cluster, or try [bindings](/docs/gateway/k8s/guides/bindings/) and [endpoint pooling](/docs/gateway/k8s/guides/pooling/). # Facebook OAuth Source: https://ngrok.com/docs/integrations/oauth/facebook-oauth Configure ngrok to authenticate users with Facebook OAuth. Register an app and add the OAuth callback URL. This guide explains how to create a Facebook app and configure ngrok to use Facebook OAuth for user authentication. For more detail, see the [Facebook app registration](https://developers.facebook.com/docs/apps#register) documentation. ## What you'll need * A [Facebook Developer](https://developers.facebook.com/apps/) account (convert your account to a developer account if needed). * Your ngrok authtoken and an endpoint with the OAuth action in its Traffic Policy. ## Create a Facebook app 1. Follow [Facebook's documentation for creating an app](https://developers.facebook.com/docs/development/create-an-app/), and when prompted to select a use case, choose **Authenticate and request data from users with Facebook Login**. 2. Connecting a business portfolio is optional. You can skip it, since it isn't required to authenticate users to your own service. The **Authenticate and request data from users with Facebook Login** use case sets up standard [Facebook Login](https://developers.facebook.com/docs/facebook-login/), which lets visitors sign in with their personal Facebook account. This is the right fit for gating a service behind an ngrok endpoint. You don't need [Facebook Login for Business](https://developers.facebook.com/docs/facebook-login/facebook-login-for-business) or the [Business app type](https://developers.facebook.com/docs/development/create-an-app/app-dashboard/app-types/) unless your app needs ongoing access to business assets such as Pages or ad accounts. ## Configure your app for ngrok In your app dashboard, set the following: * **Valid OAuth Redirect URIs**: add `https://idp.ngrok.com/oauth2/callback` in your app's Facebook Login settings. * **App ID** and **App Secret**: note these from **App settings → Basic** for use in your Traffic Policy below. * **Mode**: switch your app from **Development** to **Live** using the toggle at the top of the dashboard. In Development mode, only app admins, developers, and testers can sign in. ngrok does not support Facebook's Server IP allowlisting. ## Update your ngrok endpoint Traffic Policy 1. Access the [ngrok Dashboard Endpoints page](https://dashboard.ngrok.com/endpoints?sortBy=createdAt\&orderBy=desc) and locate an existing endpoint you'd like to add this to or create a new one. 2. In your traffic policy, add the following configuration: You may add [any scopes](https://developers.facebook.com/docs/apps/review/login-permissions) that are required by your application with the following caveats. * Scopes which require a Facebook [app review](https://developers.facebook.com/docs/apps/review/#app-review) are unsupported. * ngrok will enforce that users [accept all permissions](https://developers.facebook.com/docs/facebook-login/handling-declined-permissions#reprompt) before completing authorization. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: facebook client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - email ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "facebook", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "email" ] } } ] } ] } ``` Click **Save** to validate and update your traffic policy. ### Configure access control Optionally, configure access control to your service by only allowing specific users or domains. ```yaml policy.yml theme={null} on_http_request: - expressions: - '!(actions.ngrok.oauth.identity.email in [''me@example.com''])' actions: - type: deny ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['me@example.com'])" ], "actions": [ { "type": "deny" } ] } ] } ``` ## Further resources * [Handling declined permissions](https://developers.facebook.com/docs/facebook-login/handling-declined-permissions) * [App review](https://developers.facebook.com/docs/apps/review) ## User permission revocation Facebook allows revocation of any permission as part of the authorization flow. ngrok enforces that users initially grant all configured permissions. After endpoint authorization, users may selectively revoke permissions. If your application requires more than the `default` or `email` scope, follow [Facebook's rules](https://developers.facebook.com/docs/facebook-login/handling-declined-permissions#reprompt) for handling revoked permissions without violating terms of use. # GitHub OAuth Source: https://ngrok.com/docs/integrations/oauth/github-oauth Configure ngrok to authenticate users with GitHub OAuth. Register an OAuth app and set the callback URL. This guide walks you through creating a GitHub OAuth app and configuring ngrok to use it for user authentication. ## What you'll need * A [GitHub](https://github.com/) account with permission to create OAuth apps. * Your ngrok authtoken and an endpoint with the OAuth action in its Traffic Policy. ## Create a GitHub OAuth application 1. Follow [GitHub's documentation for creating an OAuth app](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) until the final step of submitting the registration form. 2. Set the **Authorization callback URL** to `https://idp.ngrok.com/oauth2/callback`. 3. Submit the form. 4. From the application overview, copy your **Client ID**. Under **Client secrets**, select **Generate a new client secret**, then copy the secret immediately, since GitHub displays its full value only once. Save both for use in ngrok. ## Update your ngrok endpoint Traffic Policy 1. Access the [ngrok Dashboard Endpoints page](https://dashboard.ngrok.com/endpoints?sortBy=createdAt\&orderBy=desc) and locate an existing endpoint you'd like to add this to or create a new one. 2. In your traffic policy, add the following configuration: You may add [any scopes](https://developers.facebook.com/docs/apps/review/login-permissions) that are required by your application with the following caveats. * Scopes which require a Facebook [app review](https://developers.facebook.com/docs/apps/review/#app-review) are unsupported. * ngrok will enforce that users [accept all permissions](https://developers.facebook.com/docs/facebook-login/handling-declined-permissions#reprompt) before completing authorization. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: github client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - read:user - read:org ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "github", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "read:user", "read:org" ] } } ] } ] } ``` Click **Save** to validate and update your traffic policy. ### Configure access control Optionally, configure access control to your service by only allowing specific users or domains. ```yaml policy.yml theme={null} on_http_request: - expressions: - '!(actions.ngrok.oauth.identity.email in [''me@example.com''])' actions: - type: deny ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['me@example.com'])" ], "actions": [ { "type": "deny" } ] } ] } ``` ## Known limitations * Users who use GitHub's [private email setting](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address#:~:text=If%20you%27d%20like%20to%20keep%20your%20personal,to%20Keep%20my%20email%20address%20private) are not able to sign in. # GitLab OAuth Source: https://ngrok.com/docs/integrations/oauth/gitlab-oauth Configure ngrok to authenticate users with GitLab OAuth. Create an app and set the redirect URI and scopes. This guide shows you how to create a GitLab application and configure ngrok to use GitLab OAuth for user authentication. The steps below follow GitLab's [OAuth 2.0 setup documentation](https://docs.gitlab.com/integration/oauth_provider/) for web applications. ## What you'll need * A [GitLab](https://gitlab.com/) account. * Your ngrok authtoken and an endpoint with the OAuth action in its Traffic Policy. ## Create an application Follow [GitLab's OAuth application documentation](https://docs.gitlab.com/integration/oauth_provider/) to add a new application, and set the following: * **Redirect URI**: `https://idp.ngrok.com/oauth2/callback` * **Scopes**: select the scopes appropriate for your app, such as **openid**, **profile**, and **email**. After saving, note the **Application ID** and **Secret** for use in your Traffic Policy below. **Restricting sign-in:** To limit which users can sign in with GitLab, create the application under one of your groups or as an instance-wide application rather than as a user-owned application. See [Further resources](#further-resources) for more detail. ## Update your ngrok endpoint Traffic Policy 1. Access the [ngrok Dashboard Endpoints page](https://dashboard.ngrok.com/endpoints?sortBy=createdAt\&orderBy=desc) and locate an existing endpoint you'd like to add this to or create a new one. 2. In your traffic policy, add the following configuration: You may add [any scopes](https://developers.facebook.com/docs/apps/review/login-permissions) that are required by your application with the following caveats. * Scopes which require a Facebook [app review](https://developers.facebook.com/docs/apps/review/#app-review) are unsupported. * ngrok will enforce that users [accept all permissions](https://developers.facebook.com/docs/facebook-login/handling-declined-permissions#reprompt) before completing authorization. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: gitlab client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - openid - profile - email ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "gitlab", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "openid", "profile", "email" ] } } ] } ] } ``` Click **Save** to validate and update your traffic policy. ### Configure access control Optionally, configure access control to your service by only allowing specific users or domains. ```yaml policy.yml theme={null} on_http_request: - expressions: - '!(actions.ngrok.oauth.identity.email in [''me@example.com''])' actions: - type: deny ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['me@example.com'])" ], "actions": [ { "type": "deny" } ] } ] } ``` ## Further resources * [Configure GitLab as an OAuth 2.0 authentication identity provider](https://docs.gitlab.com/integration/oauth_provider/) * [OAuth 2.0 identity provider API](https://docs.gitlab.com/api/oauth2/) # Google OAuth Source: https://ngrok.com/docs/integrations/oauth/google-oauth Configure ngrok to authenticate users with Google OAuth 2.0. Set up a GCP project, consent screen, and OAuth client. By default, if you use `"google"` for OAuth in your Traffic Policies without specifying a Google OAuth application, visitors are authenticated using ngrok's managed Google OAuth instance. Setting up your own Google OAuth application lets you customize authentication in more detail. This guide walks you through creating a Google OAuth 2.0 application for your ngrok endpoints. ## What you'll need * A [Google Cloud Platform](https://console.cloud.google.com) account and project. * Your ngrok authtoken and an endpoint with the OAuth action in its Traffic Policy. ## 1. Configure the consent screen ngrok configures its OAuth settings in the [Google Auth Platform](https://console.cloud.google.com/auth/overview), which replaces the former OAuth consent screen. Follow [Google's OAuth 2.0 setup documentation](https://support.google.com/cloud/answer/6158849?hl=en) to configure the consent screen, and set the following for ngrok: * **Authorized domains**: add `ngrok.com` along with your application's homepage domain. * **Scopes**: keep the `email` and `profile` scopes selected, and add any additional scopes your application requires. Save the full scope URIs for later. See [possible scope URIs](https://developers.google.com/identity/protocols/oauth2/scopes) for options. * **Audience**: select whether your application is an [internal or external app](https://support.google.com/cloud/answer/6158849?hl=en#public-and-internal). Applications that require Google verification cannot complete setup and are not supported by ngrok. ## 2. Create credentials for ngrok Create an OAuth **Web application** client (see [Google's documentation](https://developers.google.com/identity/protocols/oauth2/web-server)), and set the following: * **Authorized redirect URIs**: `https://idp.ngrok.com/oauth2/callback` Securely store the **Client ID** and **Client secret** from the final screen for use in your Traffic Policy below. The client secret is shown only once. ## 3. Update your ngrok endpoint Traffic Policy 1. Access the [ngrok Dashboard Endpoints page](https://dashboard.ngrok.com/endpoints?sortBy=createdAt\&orderBy=desc) and locate an existing endpoint you'd like to add this to or create a new one. 2. In your traffic policy, add the following configuration: You may add [any scopes](https://developers.facebook.com/docs/apps/review/login-permissions) that are required by your application with the following caveats. * Scopes which require a Facebook [app review](https://developers.facebook.com/docs/apps/review/#app-review) are unsupported. * ngrok will enforce that users [accept all permissions](https://developers.facebook.com/docs/facebook-login/handling-declined-permissions#reprompt) before completing authorization. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: google client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - https://www.googleapis.com/auth/userinfo.profile - https://www.googleapis.com/auth/userinfo.email ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email" ] } } ] } ] } ``` Click **Save** to validate and update your traffic policy. ### Configure access control Optionally, configure access control to your service by only allowing specific users or domains. ```yaml policy.yml theme={null} on_http_request: - expressions: - '!(actions.ngrok.oauth.identity.email in [''me@example.com''])' actions: - type: deny ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['me@example.com'])" ], "actions": [ { "type": "deny" } ] } ] } ``` ## Further resources * [Google OAuth 2.0 Web Server](https://developers.google.com/identity/protocols/oauth2/web-server) (prerequisite steps) * [GCP Help: Setting up OAuth 2.0](https://support.google.com/cloud/answer/6158849?hl=en) * [Google OAuth 2.0 workflow](https://developers.google.com/identity/protocols/oauth2) # LinkedIn OAuth Source: https://ngrok.com/docs/integrations/oauth/linkedin-oauth Configure ngrok to authenticate users with LinkedIn OAuth. Create an app in the LinkedIn Developer Portal and set the callback URL. This guide explains how to create a LinkedIn app and configure ngrok to use LinkedIn OAuth for user authentication. The steps follow LinkedIn's [OAuth 2.0 setup documentation](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?tabs=HTTPS1#step-1-configure-your-application) for web applications. ## What you'll need * A [LinkedIn](https://www.linkedin.com/) account and (optionally) a LinkedIn Page for your app. * Your ngrok authtoken and an endpoint with the OAuth action in its Traffic Policy. ## Create credentials for ngrok Follow [LinkedIn's documentation for creating an app](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?tabs=HTTPS1#step-1-configure-your-application) in the [LinkedIn Developer Portal](https://developer.linkedin.com/), then set the following: * **Authorized redirect URLs**: add `https://idp.ngrok.com/oauth2/callback` in the app's authentication settings. * **Products**: request access to **Sign In with LinkedIn using OpenID Connect**, which is required for ngrok to authenticate users. Note the **Client ID** and **Client Secret** from the app's authentication settings for use in your Traffic Policy below. ## Update your ngrok endpoint Traffic Policy 1. Access the [ngrok Dashboard Endpoints page](https://dashboard.ngrok.com/endpoints?sortBy=createdAt\&orderBy=desc) and locate an existing endpoint you'd like to add this to or create a new one. 2. In your traffic policy, add the following configuration: You may add [any scopes](https://developers.facebook.com/docs/apps/review/login-permissions) that are required by your application with the following caveats. * Scopes which require a Facebook [app review](https://developers.facebook.com/docs/apps/review/#app-review) are unsupported. * ngrok will enforce that users [accept all permissions](https://developers.facebook.com/docs/facebook-login/handling-declined-permissions#reprompt) before completing authorization. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: linkedin client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - r_emailaddress - r_liteprofile ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "linkedin", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "r_emailaddress", "r_liteprofile" ] } } ] } ] } ``` Click **Save** to validate and update your traffic policy. ### Configure access control Optionally, configure access control to your service by only allowing specific users or domains. ```yaml policy.yml theme={null} on_http_request: - expressions: - '!(actions.ngrok.oauth.identity.email in [''me@example.com''])' actions: - type: deny ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['me@example.com'])" ], "actions": [ { "type": "deny" } ] } ] } ``` ## Further resources * [Authorization Code Flow](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow) # Microsoft OAuth Source: https://ngrok.com/docs/integrations/oauth/microsoft-oauth Configure ngrok to authenticate users with Microsoft Entra ID (Azure AD) OAuth. Register an app in the Azure portal. This guide walks you through registering a Microsoft Entra ID application and configuring ngrok to use it for user authentication. The steps below follow [Microsoft's app registration documentation](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) in the Azure portal. ## What you'll need * An [Azure](https://portal.azure.com/) account and a tenant (or [create a tenant](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-create-new-tenant)). * Your ngrok authtoken and an endpoint with the OAuth action in its Traffic Policy. ## Register an application Follow [Microsoft's app registration documentation](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) to register an application in Microsoft Entra ID, and set the following: * **Supported account types**: ngrok does not support [single-tenant applications](https://learn.microsoft.com/en-us/entra/identity-platform/single-and-multi-tenant-apps). Choose a multi-tenant option: either **Accounts in any organizational directory** or **Accounts in any organizational directory and personal Microsoft accounts** (for example, Skype, Xbox). * **Redirect URI**: choose the **Web** platform and enter `https://idp.ngrok.com/oauth2/callback`. ## Configure your application In your app's settings, configure the following: * **API permissions**: ensure `User.Read` (or a more permissive scope such as `User.Read.All`) is granted. Add any additional scopes your application requires and note them for later. * Scopes that require application review by Microsoft are unsupported. * Scopes that [require admin consent](https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview) prevent tenants' users from authorizing until consent is granted. * **Client secret**: create a new client secret and copy its **Value** immediately, since it is shown only once. Note the **Application (client) ID** and the client secret value for use in your Traffic Policy below. ## Update your ngrok endpoint Traffic Policy 1. Access the [ngrok Dashboard Endpoints page](https://dashboard.ngrok.com/endpoints?sortBy=createdAt\&orderBy=desc) and locate an existing endpoint you'd like to add this to or create a new one. 2. In your traffic policy, add the following configuration: You may add [any scopes](https://developers.facebook.com/docs/apps/review/login-permissions) that are required by your application with the following caveats. * Scopes which require a Facebook [app review](https://developers.facebook.com/docs/apps/review/#app-review) are unsupported. * ngrok will enforce that users [accept all permissions](https://developers.facebook.com/docs/facebook-login/handling-declined-permissions#reprompt) before completing authorization. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: microsoft client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - openid - email - profile ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "microsoft", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "openid", "email", "profile" ] } } ] } ] } ``` Click **Save** to validate and update your traffic policy. ### Configure access control Optionally, configure access control to your service by only allowing specific users or domains. ```yaml policy.yml theme={null} on_http_request: - expressions: - '!(actions.ngrok.oauth.identity.email in [''me@example.com''])' actions: - type: deny ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['me@example.com'])" ], "actions": [ { "type": "deny" } ] } ] } ``` ## Further resources * [Creating a Microsoft Entra ID tenant](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-create-new-tenant) * [Permissions and consent](https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview) (restricted permissions) * [Microsoft Graph User resource type](https://learn.microsoft.com/en-us/graph/api/resources/user) (id, displayName, mail, userPrincipalName) # Amazon OAuth Source: https://ngrok.com/docs/integrations/oauth/oauth Configure ngrok to authenticate users with Amazon OAuth (Login with Amazon) for websites and apps. This guide walks you through configuring ngrok to use Amazon OAuth (Login with Amazon) for user authentication. The steps below follow Amazon's [Register for Login with Amazon](https://developer.amazon.com/docs/login-with-amazon/register-web.html) documentation for websites. ## What you'll need * Your [Amazon Developer account](https://developer.amazon.com/loginwithamazon/console/site/lwa/overview.html) (sign up if you don't have one). * Your ngrok authtoken and an endpoint with the OAuth action in its Traffic Policy. ## Create a security profile for ngrok Follow [Amazon's documentation for registering with Login with Amazon](https://developer.amazon.com/docs/login-with-amazon/register-web.html) to create a security profile in the [Login with Amazon](https://developer.amazon.com/loginwithamazon/console/site/lwa/overview.html) portal, then set the following in the profile's web settings: * **Allowed Return URLs**: add `https://idp.ngrok.com/oauth2/callback`. Note the security profile's **Client ID** and **Client Secret** for use in your Traffic Policy below. ## Update your ngrok endpoint Traffic Policy 1. Access the [ngrok Dashboard Endpoints page](https://dashboard.ngrok.com/endpoints?sortBy=createdAt\&orderBy=desc) and locate an existing endpoint you'd like to add this to or create a new one. 2. In your traffic policy, add the following configuration: You may add [any scopes](https://developers.facebook.com/docs/apps/review/login-permissions) that are required by your application with the following caveats. * Scopes which require a Facebook [app review](https://developers.facebook.com/docs/apps/review/#app-review) are unsupported. * ngrok will enforce that users [accept all permissions](https://developers.facebook.com/docs/facebook-login/handling-declined-permissions#reprompt) before completing authorization. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: amazon client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - profile ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "amazon", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "profile" ] } } ] } ] } ``` Click **Save** to validate and update your traffic policy. ### Configure access control Optionally, configure access control to your service by only allowing specific users or domains. ```yaml policy.yml theme={null} on_http_request: - expressions: - '!(actions.ngrok.oauth.identity.email in [''me@example.com''])' actions: - type: deny ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['me@example.com'])" ], "actions": [ { "type": "deny" } ] } ] } ``` ## Further resources * [Login with Amazon Documentation](https://developer.amazon.com/docs/login-with-amazon/documentation-overview.html) * [Login with Amazon for Websites Overview](https://developer.amazon.com/docs/login-with-amazon/web-docs.html) # Twitch OAuth Source: https://ngrok.com/docs/integrations/oauth/twitch-oauth Configure ngrok to authenticate users with Twitch OAuth. Register an application in the Twitch developer console. This guide explains how to register a Twitch application and configure ngrok to use Twitch OAuth for user authentication. The steps follow Twitch's [OAuth 2.0 documentation](https://dev.twitch.tv/docs/authentication/). ## What you'll need * A [Twitch](https://www.twitch.tv/) account with two-factor authentication (2FA) enabled (required to register an application). * Your ngrok authtoken and an endpoint with the OAuth action in its Traffic Policy. ## Create credentials for ngrok Follow [Twitch's documentation for registering an app](https://dev.twitch.tv/docs/authentication/register-app/) in the [Twitch developer console](https://dev.twitch.tv/console), then set the following: * **OAuth Redirect URLs**: `https://idp.ngrok.com/oauth2/callback` * **Category**: select a category, for example **Website Integration**. After registering, generate a client secret and note the **Client ID** and **Client Secret** for use in your Traffic Policy below. **Required:** You must enable two-factor authentication (2FA) on your Twitch account before you can register an application. ## Update your ngrok endpoint Traffic Policy 1. Access the [ngrok Dashboard Endpoints page](https://dashboard.ngrok.com/endpoints?sortBy=createdAt\&orderBy=desc) and locate an existing endpoint you'd like to add this to or create a new one. 2. In your traffic policy, add the following configuration: You may add [any scopes](https://developers.facebook.com/docs/apps/review/login-permissions) that are required by your application with the following caveats. * Scopes which require a Facebook [app review](https://developers.facebook.com/docs/apps/review/#app-review) are unsupported. * ngrok will enforce that users [accept all permissions](https://developers.facebook.com/docs/facebook-login/handling-declined-permissions#reprompt) before completing authorization. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: twitch client_id: '{your app''s oauth client id}' client_secret: '{your app''s oauth client secret}' scopes: - user:read:email - openid ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "twitch", "client_id": "{your app's oauth client id}", "client_secret": "{your app's oauth client secret}", "scopes": [ "user:read:email", "openid" ] } } ] } ] } ``` Click **Save** to validate and update your traffic policy. ### Configure access control Optionally, configure access control to your service by only allowing specific users or domains. ```yaml policy.yml theme={null} on_http_request: - expressions: - '!(actions.ngrok.oauth.identity.email in [''me@example.com''])' actions: - type: deny ``` ```json policy.json theme={null} { "on_http_request": [ { "expressions": [ "!(actions.ngrok.oauth.identity.email in ['me@example.com'])" ], "actions": [ { "type": "deny" } ] } ] } ``` ## Further resources * [Authentication](https://dev.twitch.tv/docs/authentication/) * [Registering Your App](https://dev.twitch.tv/docs/authentication/register-app/) # AfterShip Webhooks Source: https://ngrok.com/docs/integrations/webhooks/aftership-webhooks Develop and test AfterShip webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive AfterShip webhooks on your localhost app. By integrating ngrok with AfterShip, you can: * Develop and test AfterShip webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from AfterShip in real time via the inspection UI and API. * Modify and replay AfterShip webhook requests with a single click instead of reproducing events manually in your AfterShip account. * Secure your app with AfterShip webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * An AfterShip account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with AfterShip. ## 3. Configure AfterShip to send webhooks Follow [AfterShip's documentation for creating a webhook](https://www.aftership.com/docs/tracking/webhook/webhook-overview), and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). ### Run webhooks with AfterShip and ngrok After you add the webhook, AfterShip sends POST requests to your app through ngrok as shipment statuses change. To trigger a webhook call, add a shipment with a tracking number in your AfterShip account. Confirm your app receives the notification and logs the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your AfterShip webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Create a Traffic Policy file named `aftership_policy.yml`. Replace `{your secret}` with the **Webhook secret** from your AfterShip webhook: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: aftership secret: "{your secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file aftership_policy.yml ``` * Create a new shipment in your AfterShip account to trigger a delivery. Your app should receive the request and log it in the terminal. # Airship Webhooks Source: https://ngrok.com/docs/integrations/webhooks/airship-webhooks Develop and test Airship webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Airship webhook notifications on your localhost app. By integrating ngrok with Airship, you can: * Develop and test Airship webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Airship in real time via the inspection UI and API. * Modify and replay Airship webhook requests with a single click instead of reproducing events manually in your Airship account. * Secure your app with Airship webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * An Airship account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Airship. ## 3. Configure Airship to send webhooks Airship sends webhooks through Open Channels. Follow [Airship's documentation for configuring an Open Channel](https://www.airship.com/docs/developer/api-integrations/open/getting-started/), and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Display Name** and **Name**: label the Open Channel (for example, `My Local Webhook`) so you can select it when sending a message and secure it later. ### Run webhooks with Airship and ngrok Airship sends different request bodies depending on the event that is triggered. To trigger a webhook call, send a message to your Open Channel from Airship, selecting it as the delivery channel. Confirm your app receives the notification and logs the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Airship webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In your Airship app, go to **Settings**, **Open Channels** under **Channels**, and click **Edit** on the **My Local Webhook** tile. * Select **Signature** in **Authentication**, enter a value in **Secret Key** (for example, `12345`), and click **Update**. * Create a Traffic Policy file named `airship_policy.yml`. Replace `{your secret}` with the value from **Secret Key**: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: airship secret: "{your secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file airship_policy.yml ``` * Create a new message in Airship to trigger a webhook. Your app should receive the request and log it in the terminal. # Alchemy Webhooks Source: https://ngrok.com/docs/integrations/webhooks/alchemy-webhooks Develop and test Alchemy webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Alchemy webhooks on your localhost app. By integrating ngrok with Alchemy, you can: * Develop and test Alchemy webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Alchemy in real time via the inspection UI and API. * Modify and replay Alchemy webhook requests with a single click instead of reproducing events manually in your Alchemy account. * Secure your app with Alchemy webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * An Alchemy account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Alchemy. ## 3. Configure Alchemy to send webhooks Follow [Alchemy's documentation for creating an Address Activity webhook](https://www.alchemy.com/docs/reference/address-activity-webhook), and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Webhook type**: choose which events trigger the webhook (for this example, **Address Activity**, along with the chain, network, and address you want to monitor). ### Run webhooks with Alchemy and ngrok Alchemy sends different request bodies depending on the webhook type. Because this example uses **Address Activity**, sending or receiving tokens at the monitored address triggers a webhook. To test without an on-chain transaction, send a test notification for your webhook from Alchemy. Confirm your app receives the event and logs the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Alchemy webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [Alchemy dashboard](https://dashboard.alchemy.com/), go to **Data** and **Webhooks**. * In the **Address Activity** section, click the 3 dots for your webhook and click **Signing Key**. * In the popup, copy the **Signing key** value and click **Close**. * Create a Traffic Policy file named `alchemy_policy.yml`. Replace `{your signing key}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: alchemy secret: "{your signing key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file alchemy_policy.yml ``` After restarting ngrok, the tunnel URL changes. Alchemy does not allow editing webhook URLs, so it's recommended to create a [reserved domain](/docs/gateway/domains/) on your ngrok account and run the agent with `--domain your-domain.ngrok.app`. * Test the webhook from the Alchemy dashboard or trigger an event with the Alchemy SDK. Your app should receive the request and log it in the terminal. # Amazon SNS Webhooks Source: https://ngrok.com/docs/integrations/webhooks/amazon-sns-webhooks Develop and test Amazon SNS webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Amazon SNS topic notifications on your localhost app. By integrating ngrok with Amazon SNS, you can: * Develop and test Amazon SNS webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Amazon SNS in real time via the inspection UI and API. * Modify and replay Amazon SNS webhook requests with a single click instead of reproducing notifications manually in your SNS topic. * Secure your app with Amazon SNS webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * An [AWS](https://aws.amazon.com/) account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Amazon SNS. ## 3. Configure Amazon SNS to send webhooks Amazon SNS sends notifications to an HTTP/S endpoint that you subscribe to a topic. Using a standard SNS topic, follow [AWS's documentation for subscribing an HTTPS endpoint to an SNS topic](https://docs.aws.amazon.com/sns/latest/dg/sns-subscribe-https-s-endpoints-to-topic.html), and set the following: * **Protocol**: `HTTPS`. * **Endpoint**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). After you create the subscription, SNS sends a **SubscriptionConfirmation** request to your app. Copy the **SubscribeURL** value from the request body your app logs, then confirm the subscription in SNS using that URL. The subscription status changes to confirmed once you do. ### Run webhooks with Amazon SNS and ngrok Publishing a message to your topic triggers your HTTPS subscription. To trigger a webhook call, publish a message to the topic from SNS. Confirm your app receives the notification and logs the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Amazon SNS topic so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [AWS](https://aws.amazon.com/) console, open the Amazon SNS **Dashboard** and click **Topics**. * Open your topic and copy the value of **Topic owner**. * Create a Traffic Policy file named `amazon_sns_policy.yml`. Replace `{Topic owner}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: sns secret: "{Topic owner}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file amazon_sns_policy.yml ``` * Publish a new message to your topic from the AWS console. Your app should receive the request and log it in the terminal. # Autodesk Platform Services Webhooks Source: https://ngrok.com/docs/integrations/webhooks/autodesk-webhooks Develop and test Autodesk webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Autodesk Platform Services webhooks on your localhost app. By integrating ngrok with Autodesk, you can: * Develop and test Autodesk webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Autodesk in real time via the inspection UI and API. * Modify and replay Autodesk webhook requests with a single click instead of reproducing events manually in your Autodesk account. * Secure your app with Autodesk webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * An Autodesk Platform Services account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Autodesk. ## 3. Configure Autodesk to send webhooks Autodesk Platform Services delivers events to a callback URL that you register with the Webhooks API. Follow [Autodesk's Webhooks API documentation](https://aps.autodesk.com/en/docs/webhooks/v1/developers_guide/overview/) to register a webhook, setting its callback URL to your ngrok URL. First, create an APS application to obtain a **Client ID** and **Client Secret**, set its callback URL to your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`), and grant it access to the **Data Management API** and **Webhooks API**. This example uses Data Management; you can choose other APIs for different Autodesk services. Next, obtain an access token: ```bash theme={null} curl -v 'https://developer.api.autodesk.com/authentication/v1/authenticate' \ -X 'POST' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=client_credentials&scope=data:read' ``` Replace `CLIENT_ID` and `CLIENT_SECRET` with your application's values, and copy the `access_token` from the response. Then register a webhook with the Data Management API: ```bash theme={null} curl -X 'POST' \ -v 'https://developer.api.autodesk.com/webhooks/v1/systems/data/events/dm.*/hooks' \ -H 'Content-Type: application/json' \ -H 'authorization: Bearer ACCESS_TOKEN' \ -d '{ "callbackUrl": "CALLBACK_URL", "scope": { "folder": "FOLDER_URN" }, "hookAttribute": { "projectId": "PROJECT_ID" }, "filter": "$[?(@.ext=='\''txt'\'')]" }' ``` Replace `ACCESS_TOKEN` with your access token, `CALLBACK_URL` with your ngrok URL, `FOLDER_URN` with your Data Management folder URN, and `PROJECT_ID` with your project ID. A successful response returns `HTTP/1.1 201`. ### Run webhooks with Autodesk and ngrok Autodesk sends different requests depending on the event. For this example, adding a file to the folder assigned to your webhook triggers a call. To trigger a webhook call, upload a text file to that folder. Confirm your app receives the notification and logs the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Autodesk webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Register a webhook secret with the Autodesk API: ```bash theme={null} curl -X 'POST' \ -v 'https://developer.api.autodesk.com/webhooks/v1/tokens' \ -H 'Content-Type: application/json' \ -H 'authorization: Bearer ACCESS_TOKEN' \ -d '{ "token": "YOUR_WEBHOOK_SECRET" }' ``` Replace `ACCESS_TOKEN` with your access token and `YOUR_WEBHOOK_SECRET` with a value you choose to sign each webhook request. The response should be `HTTP/1.1 200`. * Create a Traffic Policy file named `autodesk_policy.yml`. Replace `{your client secret}` with your Autodesk **Client Secret**: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: autodesk secret: "{your client secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file autodesk_policy.yml ``` * Upload a text file to the folder assigned to your webhook to trigger a request. Your app should receive the request and log it in the terminal. # Bitbucket Repository Webhooks Source: https://ngrok.com/docs/integrations/webhooks/bitbucket-webhooks Develop and test Bitbucket webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Bitbucket repository webhooks on your localhost app. By integrating ngrok with Bitbucket, you can: * Develop and test Bitbucket webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Bitbucket in real time via the inspection UI and API. * Modify and replay Bitbucket webhook requests with a single click instead of reproducing events manually in your Bitbucket account. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Bitbucket account and a repository. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Bitbucket. ## 3. Configure Bitbucket to send webhooks Follow [Bitbucket's documentation for managing webhooks](https://support.atlassian.com/bitbucket-cloud/docs/manage-webhooks/), and set the following: * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Triggers**: choose which events trigger the webhook (for this example, **Push**). ### Run webhooks with Bitbucket and ngrok With **Push** as the trigger, Bitbucket sends a POST request to your app through ngrok when you push to the repository. The payload your app receives depends on the triggers you chose. To trigger a push event, commit a new file to the repository, either through Bitbucket or by pushing from a local clone: ```bash theme={null} git add . git commit -m "my first commit" git push ``` Confirm your app receives the push event and logs the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests You can restrict or verify traffic in two ways depending on your Bitbucket setup. **Bitbucket Cloud:** Use ngrok IP restrictions to allow ingress only from Bitbucket Cloud IP addresses. IP restrictions require an ngrok Pay-as-you-go plan. You can find Bitbucket Cloud IP addresses in [Bitbucket Support](https://support.atlassian.com/bitbucket-cloud/docs/what-are-the-bitbucket-cloud-ip-addresses-i-should-use-to-configure-my-corporate-firewall/). * In the [ngrok dashboard](https://dashboard.ngrok.com), go to **Identity & Access** and **IP Restrictions**. * In **Agent**, click **Attach IP Policies**, **New IP Policy**, and **Add Rule**. * In **Add Rule**, click **Allow** and enter a CIDR that covers the Bitbucket IP (for example, `123.456.789.1/32`) in **CIDR**. * Enter a **Description** for the rule and policy, click **Save**, then **Attach IP Policy**. * On **IP Restrictions**, click **Save**. **Bitbucket Server:** Use ngrok webhook verification so only requests from your Bitbucket webhook reach your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. * In your Bitbucket repository, open the webhook page and edit the webhook. * In **Authentication**, select **Secret token** and enter a secret value. * Create a Traffic Policy file named `bitbucket_policy.yml`. Replace `{your secret token}` with that value: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: bitbucket secret: "{your secret token}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file bitbucket_policy.yml ``` * Add and commit a new file in the repository to trigger the webhook. Your app should receive the request and log it in the terminal. # Box Webhooks Source: https://ngrok.com/docs/integrations/webhooks/box-webhooks Develop and test Box webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Box webhooks on your localhost app. By integrating ngrok with Box, you can: * Develop and test Box webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Box in real time via the inspection UI and API. * Modify and replay Box webhook requests with a single click instead of reproducing events manually in your Box account. * Secure your app with Box webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Box account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Box. ## 3. Configure Box to send webhooks Box V2 webhooks require a custom app that has the **Manage Webhooks** scope enabled and is authorized for your account. With that app in place, follow [Box's documentation for creating a V2 webhook](https://developer.box.com/guides/webhooks/v2/create-v2/), and set the following: * **URL Address**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Content Type**: a folder in your Box account for the webhook to monitor. * **Triggers**: choose which events trigger the webhook (for this example, **File Uploaded**). ### Run webhooks with Box and ngrok With the **File Uploaded** trigger, Box notifies your app when a file is uploaded to the folder you selected. To trigger a webhook call, upload a file to that folder in Box. Confirm your app receives the **FILE.UPLOADED** event and logs the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Box webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [Box Developer Console](https://app.box.com/developers/console), open your app, go to the **Webhook** tab, and click **Manage Signature Keys**. * On **Manage Signature Keys**, click **Generate Key** in **Primary Key** and click **COPY** to copy the primary key value. * Create a Traffic Policy file named `box_policy.yml`. Replace `{your primary key}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: box secret: "{your primary key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file box_policy.yml ``` * Upload a file to the folder you selected for the webhook to trigger a request. Your app should receive the request and log it in the terminal. # Brex Webhooks Source: https://ngrok.com/docs/integrations/webhooks/brex-webhooks Develop and test Brex webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Brex webhooks on your localhost app. By integrating ngrok with Brex, you can: * Develop and test Brex webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Brex in real time via the inspection UI and API. * Modify and replay Brex webhook requests with a single click instead of reproducing events manually in your Brex account. * Secure your app with Brex webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Brex account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Brex. ## 3. Configure Brex to send webhooks Brex registers webhooks through its API. Follow [Brex's Webhooks API documentation](https://developer.brex.com/openapi/webhooks_api/) to create a webhook, using an API token with write access to the resources you want to receive events for (this example uses **Users**). Create the webhook, setting its URL to your ngrok URL: ```bash theme={null} curl -i -X POST https://platform.brexapis.com/v1/webhooks \ -H 'Authorization: Bearer YOUR_TOKEN_HERE' \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: string' \ -d '{"url": "YOUR_NGROK_URL", "event_types": ["USER_UPDATED"]}' ``` Replace `YOUR_TOKEN_HERE` with your API token and `YOUR_NGROK_URL` with your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). A successful response returns HTTP 200 with `"status": "ACTIVE"`. ### Run webhooks with Brex and ngrok The `USER_UPDATED` event fires when a user in your account changes. To trigger a webhook call, invite a new user to your Brex account. Confirm your app receives the event and logs the headers and body in the terminal. If Brex doesn't send the notification, check the invitee's email and accept the invitation to complete the user creation. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Brex webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Get your webhook secret from the Brex API: ```bash theme={null} curl -i -X GET https://platform.brexapis.com/v1/webhooks/secrets \ -H 'Authorization: Bearer YOUR_TOKEN_HERE' ``` Replace `YOUR_TOKEN_HERE` with the token you copied when configuring the webhook. The response includes JSON with a `"secret"` value; copy it. * Create a Traffic Policy file named `brex_policy.yml`. Replace `{your webhook secret}` with the secret value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: brex secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file brex_policy.yml ``` * Create a new user in the Brex Dashboard (and have the invite accepted) to trigger a webhook. Your app should receive the request and log it in the terminal. # Buildkite Webhooks Source: https://ngrok.com/docs/integrations/webhooks/buildkite-webhooks Develop and test Buildkite webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Buildkite webhooks on your localhost app. By integrating ngrok with Buildkite, you can: * Develop and test Buildkite webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Buildkite in real time via the inspection UI and API. * Modify and replay Buildkite webhook requests with a single click instead of reproducing events manually in your Buildkite account. * Secure your app with Buildkite webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Buildkite account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Buildkite. ## 3. Configure Buildkite to send webhooks Follow [Buildkite's documentation for adding a webhook notification](https://buildkite.com/docs/apis/webhooks/pipelines), and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: choose which events trigger the webhook (for this example, subscribe to all events across all pipelines). ### Run webhooks with Buildkite and ngrok Buildkite sends a different request body for each event type. Create a new build on one of your pipelines to trigger new calls to your app. Confirm your app receives the event and logs its headers and body in the terminal. You can also review the delivery in your webhook's request log. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Buildkite webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Buildkite](https://buildkite.com/), go to **Settings** and **Notification Services**. * On **Notification Services**, click your webhook tile, copy the value of **Token**, select **Send an HMAC signature**, and click **Save Webhook Settings**. * Create a Traffic Policy file named `buildkite_policy.yml`. Replace `{your token}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: buildkite secret: "{your token}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file buildkite_policy.yml ``` * Create a new build on one of your pipelines to trigger the webhook. Your app should receive the request and log it in the terminal. # Calendly Webhooks Source: https://ngrok.com/docs/integrations/webhooks/calendly-webhooks Develop and test Calendly webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Calendly webhooks on your localhost app. By integrating ngrok with Calendly, you can: * Develop and test Calendly webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Calendly in real time via the inspection UI and API. * Modify and replay Calendly webhook requests with a single click instead of reproducing events manually in your Calendly account. * Secure your app with Calendly webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Calendly account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Calendly. ## 3. Configure Calendly to send webhooks Calendly registers webhooks through its API rather than a dashboard form. Follow [Calendly's guide for creating a webhook subscription](https://developer.calendly.com/receive-data-from-scheduled-events-in-real-time-with-webhook-subscriptions), which covers creating a personal access token and sending the registration request with your organization and user URIs. Set the following in your request: * **url**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **events**: choose which events trigger the webhook (for this example, `invitee.created` and `invitee.canceled`). * **scope**: the level the webhook applies to (for this example, `user`). Your registration request looks like this: ```bash theme={null} curl --request POST --url https://api.calendly.com/webhook_subscriptions \ --header 'Authorization: Bearer TOKEN' \ --header 'Content-Type: application/json' --data '{ "url": "URL", "events": [ "invitee.created", "invitee.canceled" ], "organization": "ORGANIZATION_URL", "user": "USER_URL", "scope": "user", "signing_key": "KEY" }' ``` Replace `URL` with your ngrok URL, `TOKEN` with your personal access token, `ORGANIZATION_URL` and `USER_URL` with your organization and user URIs, and `KEY` with your signing key. ### Run webhooks with Calendly and ngrok Calendly sends a different request body for each event type. Book a meeting through your Calendly scheduling link to trigger a new event. Confirm your app receives the `invitee.created` event and logs its headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Calendly webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Calendly](https://calendly.com/), go to **Integrations**, **API and webhooks**, and copy the **API Key** value (the same value you used to register your webhook). * Create a Traffic Policy file named `calendly_policy.yml`. Replace `{your key}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: calendly secret: "{your key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file calendly_policy.yml ``` * Schedule a meeting from your Calendly link to trigger the webhook. Your app should receive the request and log it in the terminal. # Castle Webhooks Source: https://ngrok.com/docs/integrations/webhooks/castle-webhooks Develop and test Castle webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Castle webhooks on your localhost app. By integrating ngrok with Castle, you can: * Develop and test Castle webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Castle in real time via the inspection UI and API. * Modify and replay Castle webhook requests with a single click instead of reproducing events manually in your Castle account. * Secure your app with Castle webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Castle account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Castle. ## 3. Configure Castle to send webhooks Follow [Castle's documentation for setting up a webhook](https://docs.castle.io/docs/webhooks), and set the following: * **Endpoint URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: choose which events trigger the webhook (for this example, subscribe to all events). After you save the webhook, activate it if it isn't already so Castle starts sending requests. ### Run webhooks with Castle and ngrok Castle sends a different request body for each event type. To trigger a call, send a test webhook from your endpoint and choose an event to deliver. You can also generate an event using the Castle SDK. Confirm your app receives the event and logs its headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Castle webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [Castle dashboard](https://dashboard.castle.io/), go to **Configuration** and copy the value of the **API SECRET** field on the **Application** page. * Create a Traffic Policy file named `castle_policy.yml`. Replace `{your api secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: castle secret: "{your api secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file castle_policy.yml ``` * Test the webhook endpoint or create an event using the Castle SDK to trigger a request. Your app should receive the request and log it in the terminal. # Chargify Webhooks Source: https://ngrok.com/docs/integrations/webhooks/chargify-webhooks Develop and test Chargify webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Chargify webhooks on your localhost app. By integrating ngrok with Chargify, you can: * Develop and test Chargify webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Chargify in real time via the inspection UI and API. * Modify and replay Chargify webhook requests with a single click instead of reproducing events manually in your Chargify account. * Secure your app with Chargify webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Chargify account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Chargify. ## 3. Configure Chargify to send webhooks Follow [Chargify's documentation for adding a webhook endpoint](https://docs.maxio.com/hc/en-us/articles/24266136649869-Webhooks-Configuration-and-Testing), and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: choose which events trigger the webhook (for this example, subscribe to all events). ### Run webhooks with Chargify and ngrok Chargify sends a different request body for each event type. To trigger a call, send a test notification to your webhook from Chargify's webhook testing tools. Confirm your app receives the event and logs its headers and body in the terminal. You can also review the delivery in Chargify's webhook history. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Chargify webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * On your Chargify site, click **Edit current Site** from the **Site** dropdown at the top. * On the **Site** page, copy the value of the **Site Shared Key**. * Create a Traffic Policy file named `chargify_policy.yml`. Replace `{your site shared key}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: chargify secret: "{your site shared key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file chargify_policy.yml ``` * Go to **Webhook Testing** and send a new test notification to your webhook. Your app should receive the request and log it in the terminal. # CircleCI Webhooks Source: https://ngrok.com/docs/integrations/webhooks/circleci-webhooks Develop and test CircleCI webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive CircleCI webhooks on your localhost app. By integrating ngrok with CircleCI, you can: * Develop and test CircleCI webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from CircleCI in real time via the inspection UI and API. * Modify and replay CircleCI webhook requests with a single click instead of reproducing events manually in your CircleCI account. * Secure your app with CircleCI webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A CircleCI account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with CircleCI. ## 3. Configure CircleCI to send webhooks Follow [CircleCI's documentation for setting up outbound webhooks](https://circleci.com/docs/guides/integration/outbound-webhooks/), and set the following: * **Receiver URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: choose which events trigger the webhook (for this example, **Workflow Completed** and **Job Completed**). * **Secret token**: set a token and note it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with CircleCI and ngrok CircleCI sends a different request body for each event type. Rerun a workflow from the start to trigger new calls to your app. Confirm your app receives the workflow events and logs their headers and body in the terminal. You can also send a test ping from the webhook's settings to check delivery. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your CircleCI webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Create a Traffic Policy file named `circleci_policy.yml`. Replace `{your secret token}` with the value you entered in **Secret token** when you registered the webhook: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: circleci secret: "{your secret token}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file circleci_policy.yml ``` * Rerun your workflow from start in CircleCI to trigger the webhook. Your app should receive the request and log it in the terminal. # Clerk Webhooks Source: https://ngrok.com/docs/integrations/webhooks/clerk-webhooks Develop and test Clerk webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Clerk webhooks on your localhost app. By integrating ngrok with Clerk, you can: * Develop and test Clerk webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Clerk in real time via the inspection UI and API. * Modify and replay Clerk webhook requests with a single click instead of reproducing events manually in your Clerk account. * Secure your app with Clerk webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Clerk account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Clerk. ## 3. Configure Clerk to send webhooks Follow [Clerk's documentation for syncing data with webhooks](https://clerk.com/docs/guides/development/webhooks/syncing), and set the following: * **Endpoint URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: choose which events trigger the webhook (for this example, `user.created`, `user.updated`, and `user.deleted`). ### Run webhooks with Clerk and ngrok Clerk sends a different request body for each event type. Create a new user in Clerk to trigger a `user.created` event. Confirm your app receives the event and logs its headers and body in the terminal. You can also send a sample event from the endpoint's testing options or review deliveries in the endpoint's logs. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Clerk webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [Clerk Dashboard](https://dashboard.clerk.com/sign-in), go to **Webhooks** and click your endpoint. * On the **Endpoint** page, click the eye icon under **Signing Secret** and copy the value. * Create a Traffic Policy file named `clerk_policy.yml`. Replace `{endpoint signing secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: clerk secret: "{endpoint signing secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file clerk_policy.yml ``` * Create a new user in Clerk to trigger the webhook. Your app should receive the request and log it in the terminal. # Coinbase Webhooks Source: https://ngrok.com/docs/integrations/webhooks/coinbase-webhooks Develop and test Coinbase webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Coinbase webhooks on your localhost app. By integrating ngrok with Coinbase, you can: * Develop and test Coinbase webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Coinbase in real time via the inspection UI and API. * Modify and replay Coinbase webhook requests with a single click instead of reproducing events manually in your Coinbase account. * Secure your app with Coinbase webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Coinbase account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Coinbase. ## 3. Configure Coinbase to send webhooks Follow [Coinbase's documentation for setting up notifications](https://docs.cdp.coinbase.com/sign-in-with-coinbase/docs/notifications), which delivers webhooks to a notification endpoint on your API key. Set the following: * **Notification URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). Grant the API key access to the accounts and permissions you want notifications for, then note its API Secret for the [verification step](#secure-webhook-requests) below. ### Run webhooks with Coinbase and ngrok Coinbase sends a different request body for each event type. Perform an account activity such as buying an asset to trigger a notification. Confirm your app receives the event and logs its headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Coinbase webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Create a Traffic Policy file named `coinbase_policy.yml`. Replace `{your api secret}` with the API Secret you copied when configuring the webhook: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: coinbase secret: "{your api secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file coinbase_policy.yml ``` * Buy an asset in Coinbase to trigger the webhook. Your app should receive the request and log it in the terminal. # Contentful Webhooks Source: https://ngrok.com/docs/integrations/webhooks/contentful-webhooks Develop and test Contentful webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Contentful webhooks on your localhost app. By integrating ngrok with Contentful, you can: * Develop and test Contentful webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Contentful in real time via the inspection UI and API. * Modify and replay Contentful webhook requests with a single click instead of reproducing events manually in your Contentful account. * Secure your app with Contentful webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Contentful account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Contentful. ## 3. Configure Contentful to send webhooks Follow [Contentful's documentation for configuring a webhook](https://www.contentful.com/developers/docs/webhooks/configure-webhook/), and set the following: * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Content type**: `application/json`. * **Triggers**: choose which events trigger the webhook (for this example, trigger for all events). Make sure the webhook is active before you save it. ### Run webhooks with Contentful and ngrok After you save the webhook, Contentful sends POST requests to your app when the events you chose occur. The request body varies by event. To trigger a call, publish an asset or make another change that matches your triggers, then confirm your localhost app logs the request headers and body in the terminal. You can also review the webhook's activity log in Contentful to compare what Contentful sent with what your app received. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Contentful webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * When you created the webhook in Contentful, you set a signing secret; copy that value (or create a new webhook and note the signing secret). * Create a Traffic Policy file named `contentful_policy.yml`. Replace `{your webhook signing secret}` with the value from Contentful: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: contentful secret: "{your webhook signing secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file contentful_policy.yml ``` * Publish an asset or make another change that triggers your webhook. Your app should receive the request and log it in the terminal. # DocuSign Webhooks Source: https://ngrok.com/docs/integrations/webhooks/docusign-webhooks Develop and test DocuSign webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive DocuSign webhooks on your localhost app. By integrating ngrok with DocuSign, you can: * Develop and test DocuSign webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from DocuSign in real time via the inspection UI and API. * Modify and replay DocuSign webhook requests with a single click instead of reproducing events manually in your DocuSign account. * Secure your app with DocuSign webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A DocuSign account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with DocuSign. ## 3. Configure DocuSign to send webhooks Follow [DocuSign's documentation for creating a Connect configuration](https://developers.docusign.com/platform/webhooks/connect/create-configuration-trigger-event/), and set the following: * **URL to Publish**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Trigger Events**: choose which events trigger the webhook (for this example, **Envelope Sent**). ### Run webhooks with DocuSign and ngrok After you save the configuration, DocuSign sends POST requests to your app when the events you chose occur. The request body varies by event. To trigger a call, send an envelope in DocuSign, then confirm your localhost app logs the request headers and body in the terminal. You can also review the Connect logs in DocuSign to compare what DocuSign sent with what your app received. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your DocuSign webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In DocuSign, go to **Settings** and **Connect**, open the **CONNECT KEYS** tab, click **ADD SECRET KEY**, and copy the key value. * In the **APPLICATIONS** tab, click your webhook and enable **Include HMAC Signature** in **Integration and Security Settings**. * Create a Traffic Policy file named `docusign_policy.yml`. Replace `{your connect key}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: docusign secret: "{your connect key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file docusign_policy.yml ``` * Create a new envelope in [DocuSign](https://app.docusign.com/home) to trigger the webhook. Your app should receive the request and log it in the terminal. # Dropbox Webhooks Source: https://ngrok.com/docs/integrations/webhooks/dropbox-webhooks Develop and test Dropbox webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Dropbox webhooks on your localhost app. By integrating ngrok with Dropbox, you can: * Develop and test Dropbox webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Dropbox in real time via the inspection UI and API. * Modify and replay Dropbox webhook requests with a single click instead of reproducing events manually in your Dropbox account. * Secure your app with Dropbox webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Dropbox account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Dropbox. ## 3. Configure Dropbox to send webhooks Dropbox delivers webhooks through a Dropbox app. Follow [Dropbox's webhooks documentation](https://www.dropbox.com/developers/reference/webhooks) to create an app and add your webhook, and set the following: * **Webhook URI**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). When you add the URI, Dropbox sends a one-time verification request with a challenge parameter. Confirm your localhost app receives that request and logs the challenge in the terminal, and that the webhook shows as enabled. ### Run webhooks with Dropbox and ngrok Dropbox sends a POST request to your app when files change in the folder your app can access. To trigger a call, upload a file to that folder, then confirm your localhost app logs the request headers and body in the terminal. The payload does not include file or folder changes; it only indicates which users have changes. Your app must call the Dropbox API to get the latest changes for each user. You can grant your app full Dropbox access to receive notifications for any folder. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Dropbox webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * On the [Dropbox developer site](https://www.dropbox.com/developers/apps), click your app, open the **Settings** tab, click **Show** for **App secret**, and copy the value. * Create a Traffic Policy file named `dropbox_policy.yml`. Replace `{your app secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: dropbox secret: "{your app secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file dropbox_policy.yml ``` * Upload a new file to your app folder in Dropbox to trigger the webhook. Your app should receive the request and log it in the terminal. # Facebook Messenger Webhooks Source: https://ngrok.com/docs/integrations/webhooks/facebook-messenger-webhooks Develop and test Facebook Messenger webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Facebook Messenger webhooks on your localhost app. By integrating ngrok with Facebook Messenger, you can: * Develop and test Facebook Messenger webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Facebook Messenger in real time via the inspection UI and API. * Modify and replay Facebook Messenger webhook requests with a single click instead of reproducing events manually in your account. * Secure your app with Facebook Messenger webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Facebook account. This integration requires an ngrok Pay-as-you-go plan because Facebook validates your ngrok domain. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm run startFacebook ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * In the dashboard, expand **Gateway** and click **Domains**. If you don't have an ngrok Pay-as-you-go plan, subscribe by clicking **Update Subscription** and following the procedure. * On the **Domains** page, click **+ Create Domain** or **+ New Domain**. * In the **Domain** pane, enter a value in the **Domain** field (for example, `myexample.ngrok.app`) and click **Continue**. Make sure your domain is available. * Close the **Start a Tunnel** pane and then close the **Domain** pane. * Start ngrok with your domain: ```bash theme={null} ngrok http 3000 --url myexample.ngrok.app ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Facebook Messenger. ## 3. Configure Facebook Messenger to send webhooks Facebook Messenger delivers webhooks through a Meta app with a Messenger product and a linked Facebook page. Follow [Meta's Messenger Platform webhooks documentation](https://developers.facebook.com/docs/messenger-platform/webhooks) to create the app, add the Messenger product, and subscribe your page, and set the following: * **Callback URL**: your ngrok URL with `/webhook` at the end (for example, `https://myexample.ngrok.app/webhook`). * **Verify token**: a string of your choice (for this example, `12345`). Meta sends this value in the verification request so your app can confirm the request. When you save the callback URL, Meta sends a verification request to your app. Confirm your localhost app receives it in the terminal before you continue subscribing your page and fields. ### Run webhooks with Facebook Messenger and ngrok After your page is subscribed, Meta sends POST requests to your app when the fields you subscribed to change. The request body varies by field. To trigger a test call, use Meta's webhook testing tool to send a sample event for a field you subscribed to, then confirm your localhost app logs the request headers and body in the terminal. To test with a real message, send a message to the Facebook page you linked to your webhook from another account. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Facebook Messenger webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Meta for Developers](https://developers.facebook.com/), go to **My Apps**, then **Settings** and **Basic**. * On the **Basic Settings** page, click **Show** next to **App secret** and copy the value. * Create a Traffic Policy file named `messenger_policy.yml`. Replace `{your app secret}` with the value you copied: ```yaml theme={null} on_http_request: - name: "Facebook Messenger Webhooks" actions: - type: "webhook-validation" config: provider: "facebook_messenger" secret: "{your app secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file messenger_policy.yml ``` * Send a message from the Facebook page linked to your webhook to trigger a request. Your app should receive the request and log it in the terminal. # Facebook Webhooks Source: https://ngrok.com/docs/integrations/webhooks/facebook-webhooks Develop and test Facebook webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Facebook webhooks on your localhost app. By integrating ngrok with Facebook, you can: * Develop and test Facebook webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Facebook in real time via the inspection UI and API. * Modify and replay Facebook webhook requests with a single click instead of reproducing events manually in your Facebook account. * Secure your app with Facebook webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Facebook account. This integration requires an ngrok Pay-as-you-go plan because Facebook validates your ngrok domain and certificate. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm run startFacebook ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * In the dashboard, expand **Gateway** and click **Domains**. If you don't have an ngrok Pay-as-you-go plan, sign up for one by clicking **Update Subscription** and following the subscribe procedure. * On the **Domains** page, click **+ Create Domain** or **+ New Domain**. * In the **Domain** pane, enter a value in the **Domain** field (for example, `myexample.ngrok.app`) and click **Continue**. Make sure your domain is available. * Close the **Start a Tunnel** pane and then close the **Domain** pane. * Start ngrok with your domain: ```bash theme={null} ngrok http 3000 --url myexample.ngrok.app ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Facebook. ## 3. Configure Facebook to send webhooks Facebook delivers webhooks through a Meta app linked to a Facebook page. Follow [Meta's Graph API webhooks documentation](https://developers.facebook.com/docs/graph-api/webhooks/getting-started) to add the Webhooks product to your app and subscribe to the **Page** object, and set the following: * **Callback URL**: your ngrok URL with `/webhooks` at the end (for example, `https://myexample.ngrok.app/webhooks`). * **Verify token**: a string of your choice (for this example, `12345`). * **Fields**: choose which fields trigger the webhook (for this example, **feed**). When you save the callback URL, Meta sends a verification request to your app. Confirm your localhost app logs `WEBHOOK_VERIFIED` in the terminal, then set your app to live mode so it can send events. You can subscribe to multiple fields on the **Page** object and other objects using the same URL. ### Run webhooks with Facebook and ngrok After you subscribe to a field, Meta sends POST requests to your app when that field changes. The request body varies by object and field. With the **feed** field, you can trigger a call by creating a post on your page. Confirm your localhost app logs the request headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Facebook webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Meta for Developers](https://developers.facebook.com/), go to **My Apps**, then **Settings** and **Basic**. * On the **Basic Settings** page, click **Show** next to **App secret** and copy the value. * Create a Traffic Policy file named `facebook_policy.yml`. Replace `{your app secret}` with the value you copied: ```yaml theme={null} on_http_request: - name: "Facebook Webhooks" actions: - type: "webhook-validation" config: provider: "facebook_graph_api" secret: "{your app secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file facebook_policy.yml ``` * Send a message from the Facebook page you use for your webhook to trigger a request. Your app should receive the request and log it in the terminal. # Frame.io Webhooks Source: https://ngrok.com/docs/integrations/webhooks/frameio-webhooks Develop and test Frame.io webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Frame.io webhooks on your localhost app. By integrating ngrok with Frame.io, you can: * Develop and test Frame.io webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Frame.io in real time via the inspection UI and API. * Modify and replay Frame.io webhook requests with a single click instead of reproducing events manually in your Frame.io account. * Secure your app with Frame.io webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Frame.io account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Frame.io. ## 3. Configure Frame.io to send webhooks Follow [Frame.io's webhooks documentation](https://next.developer.frame.io/platform/docs/guides/webhooks) to create a webhook, and set the following: * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: choose which events trigger the webhook (for this example, the project created event). ### Run webhooks with Frame.io and ngrok After you create the webhook, Frame.io sends POST requests to your app when the events you chose occur. The request body varies by event. To trigger a call, create a new project in Frame.io, then confirm your localhost app logs the request headers and body in the terminal. You can also review the webhook logs in Frame.io to compare what Frame.io sent with what your app received. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Frame.io webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Copy the **Secret** for your webhook from Frame.io (see [Frame.io's webhooks documentation](https://next.developer.frame.io/platform/docs/guides/webhooks)). * Create a Traffic Policy file named `frameio_policy.yml`. Replace `{your webhook secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: frameio secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file frameio_policy.yml ``` * Create a new project in Frame.io to trigger the webhook. Your app should receive the request and log it in the terminal. # GitHub Repository Webhooks Source: https://ngrok.com/docs/integrations/webhooks/github-webhooks Develop and test GitHub webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive GitHub repository webhooks on your localhost app. By integrating ngrok with GitHub, you can: * Develop and test GitHub webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from GitHub in real time via the inspection UI and API. * Modify and replay GitHub webhook requests with a single click instead of reproducing events manually in GitHub. * Secure your app with GitHub webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * Your GitHub account and a repository. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok/ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok With your app running locally, expose it with ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with GitHub. ## 3. Configure GitHub to send webhooks Follow [GitHub's documentation for creating a repository webhook](https://docs.github.com/en/webhooks/using-webhooks/creating-webhooks#creating-a-repository-webhook), and set the following: * **Payload URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Content type**: `application/json`. * **Events**: choose which events trigger the webhook (for this example, **Just the push event**). * **Secret**: set a secret and note it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with GitHub and ngrok After you add the webhook, GitHub sends POST requests to your app through ngrok. To compare what GitHub sends with what your app receives, open the webhook's recent deliveries, select a delivery, and compare its headers and body with what your application received; they should match. The payload your app receives depends on the events you chose. Because this example uses **Just the push event**, push changes to the repository to trigger new webhook calls. You can also redeliver a request from the webhook's recent deliveries. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your GitHub webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Create a Traffic Policy file named `github_policy.yml`. Replace `{your secret}` with the **Secret** from your GitHub webhook: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: github secret: "{your secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file github_policy.yml ``` * Resend a delivery from your GitHub webhook. Your app should receive the request and log it in the terminal. # GitLab Repository Webhooks Source: https://ngrok.com/docs/integrations/webhooks/gitlab-webhooks Develop and test GitLab webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive GitLab webhooks on your localhost app. By integrating ngrok with GitLab, you can: * Develop and test GitLab webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from GitLab in real time via the inspection UI and API. * Modify and replay GitLab webhook requests with a single click instead of reproducing events manually in your GitLab account. * Secure your app with GitLab webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A GitLab account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with GitLab. ## 3. Configure GitLab to send webhooks Follow [GitLab's documentation for creating a webhook](https://docs.gitlab.com/user/project/integrations/webhooks/), and set the following: * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Trigger**: choose which events trigger the webhook (for this example, **Push events**). * **Secret token**: set a token and note it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with GitLab and ngrok After you add the webhook, GitLab sends POST requests to your app when the events you chose occur. The request body varies by event. To trigger a call, push a commit to the repository, or use the webhook's test option to send a sample push event. Then confirm your localhost app logs the request headers and body in the terminal. You can also review the webhook's recent events in GitLab to compare what GitLab sent with what your app received. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your GitLab webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In your repository, go to **Settings** and **Webhooks**, scroll to **Project Hooks**, and click **Edit** for your webhook. * Enter a value in the **Secret token** field and click **Save changes**. * Create a Traffic Policy file named `gitlab_policy.yml`. Replace `{your secret token}` with the value you entered: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: gitlab secret: "{your secret token}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file gitlab_policy.yml ``` * In **Project Hooks**, click **Test** and then **Push events**, or push content to your repository. Your app should receive the request and log it in the terminal. # Heroku Webhooks Source: https://ngrok.com/docs/integrations/webhooks/heroku-webhooks Develop and test Heroku webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Heroku webhooks on your localhost app. By integrating ngrok with Heroku, you can: * Develop and test Heroku webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Heroku in real time via the inspection UI and API. * Modify and replay Heroku webhook requests with a single click instead of reproducing events manually in your Heroku account. * Secure your app with Heroku webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Heroku account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Heroku. ## 3. Configure Heroku to send webhooks Follow [Heroku's app webhooks documentation](https://devcenter.heroku.com/articles/app-webhooks), and set the following: * **Payload URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Event Types**: choose which events trigger the webhook (for this example, **api:app** and **api:build**). * **Secret**: set a secret and note it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with Heroku and ngrok After you create the webhook, Heroku sends POST requests to your app when the events you chose occur. The request body varies by event. To trigger a call, deploy your app or run a new build, then confirm your localhost app logs the request headers and body in the terminal. You can also review the webhook's activity in Heroku to compare what Heroku sent with what your app received. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Heroku webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Create a Traffic Policy file named `heroku_policy.yml`. Replace `{your webhook secret}` with the value you entered in the **Secret** field when you registered the webhook: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: heroku secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file heroku_policy.yml ``` * Run a new build for your app in Heroku to trigger the webhook. Your app should receive the request and log it in the terminal. # HostedHooks Webhooks Source: https://ngrok.com/docs/integrations/webhooks/hostedhooks-webhooks Develop and test HostedHooks webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive HostedHooks webhooks on your localhost app. By integrating ngrok with HostedHooks, you can: * Develop and test HostedHooks webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from HostedHooks in real time via the inspection UI and API. * Modify and replay HostedHooks webhook requests with a single click instead of reproducing events manually in your HostedHooks account. * Secure your app with HostedHooks webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A HostedHooks account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with HostedHooks. ## 3. Configure HostedHooks to send webhooks Follow [HostedHooks' documentation for creating a webhook endpoint](https://docs.hostedhooks.com/getting-started/webhooks), and set the following: * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Version**: `1.0`. * **Event**: subscribe the endpoint to the event type you want to receive (for this example, `myapp.event`). ### Run webhooks with HostedHooks and ngrok When you send a message to the HostedHooks message endpoint with an event type in the body, HostedHooks routes it to your app through your ngrok URL. To trigger a call, post a message to your app's message endpoint (replace `APP_ID` with your app ID and `API_KEY` with your API key): ```bash theme={null} curl --request POST --url https://hostedhooks.com/api/v1/apps/APP_ID/messages \ --header 'Authorization: Bearer API_KEY' \ --header 'Content-Type: application/json' --data '{ "data": {"foo": "bar"}, "event_type": "myapp.event", "version": "1.0", "event_id": "00001" }' ``` Confirm your localhost app receives the event and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your HostedHooks webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [HostedHooks Dashboard](https://www.hostedhooks.com/), go to **Apps** and click your app. * In **Subscribers**, click **View**, then **View** in **Endpoints**. * On the **Endpoint** page, click **Reveal** next to **Signing Secret** and copy the value. * Create a Traffic Policy file named `hostedhooks_policy.yml`. Replace `{app sign secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: hostedhooks secret: "{app sign secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file hostedhooks_policy.yml ``` * Post a new message to the HostedHooks message endpoint (use a unique **event\_id** in the JSON body). Your app should receive the request and log it in the terminal. # HubSpot Webhooks Source: https://ngrok.com/docs/integrations/webhooks/hubspot-webhooks Develop and test HubSpot webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive HubSpot webhooks on your localhost app. By integrating ngrok with HubSpot, you can: * Develop and test HubSpot webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from HubSpot in real time via the inspection UI and API. * Modify and replay HubSpot webhook requests with a single click instead of reproducing events manually in your HubSpot account. * Secure your app with HubSpot webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A HubSpot account (Pro or developer for automation workflows). ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with HubSpot. ## 3. Configure HubSpot to send webhooks HubSpot can send webhook requests to your app from an automation workflow (HubSpot Pro or developer account) or from an app in [HubSpot Developers](https://developers.hubspot.com/). The following uses an automation workflow. Follow [HubSpot's documentation for using webhooks in workflows](https://knowledge.hubspot.com/workflows/how-do-i-use-webhooks-with-hubspot-workflows), add a **Send a webhook** action, and set the following: * **Method**: `POST`. * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Authentication type**: `None`. Set the workflow trigger to the change you want to test. For this example, use a new contact with a specific contact owner. ### Run webhooks with HubSpot and ngrok To trigger calls from HubSpot to your app, create a record that matches your workflow trigger. For this example, create a contact and set the same **Contact owner** you used in the trigger. **Contact owner** must match the user you selected in the workflow trigger. Confirm your localhost app receives the contact-created event and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your HubSpot webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Create a custom app in your HubSpot developer account and note its ID. * In the workflow, click **Actions** for the **Send a webhook** step and **Edit**, then enter the app ID and save. * In the developer account, go to **Manage apps**, open your app, **Auth** tab, and copy the **Client secret**. * Create a Traffic Policy file named `hubspot_policy.yml`. Replace `{your client secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: hubspot secret: "{your client secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file hubspot_policy.yml ``` * Create a new contact in HubSpot (with the same **Contact owner**) to trigger the webhook. Your app should receive the request and log it in the terminal. # Hygraph Webhooks Source: https://ngrok.com/docs/integrations/webhooks/hygraph-webhooks Develop and test Hygraph webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Hygraph webhooks on your localhost app. By integrating ngrok with Hygraph, you can: * Develop and test Hygraph webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Hygraph in real time via the inspection UI and API. * Modify and replay Hygraph webhook requests with a single click instead of reproducing events manually in your Hygraph account. * Secure your app with Hygraph webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Hygraph account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Hygraph. ## 3. Configure Hygraph to send webhooks Follow [Hygraph's documentation for creating a webhook](https://hygraph.com/docs/api-reference/basics/webhooks), and set the following: * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Content Model**: choose the model whose changes trigger the webhook (for this example, **Asset**). ### Run webhooks with Hygraph and ngrok Hygraph sends different request body contents depending on the event. To trigger new calls from Hygraph to your app, update content in the model you selected. For this example, upload a new asset and publish it. Confirm your localhost app receives event notifications and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Hygraph webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Hygraph](https://app.hygraph.com/), go to **Webhooks** and click the pencil icon on your webhook tile. * In the popup, enter a secret at least 32 characters long in **Secret key** and click **Update**. * Create a Traffic Policy file named `hygraph_policy.yml`. Replace `{your secret key}` with the value you entered: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: graphcms secret: "{your secret key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file hygraph_policy.yml ``` * Create a new asset in Hygraph to trigger the webhook. Your app should receive the request and log it in the terminal. # Instagram Webhooks Source: https://ngrok.com/docs/integrations/webhooks/instagram-webhooks Develop and test Instagram webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Instagram webhooks on your localhost app. By integrating ngrok with Instagram, you can: * Develop and test Instagram webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Instagram in real time via the inspection UI and API. * Modify and replay Instagram webhook requests with a single click instead of reproducing events manually in your Instagram account. * Secure your app with Instagram webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Facebook page and a Facebook app linked to that page; connect your Instagram account to the page. This integration requires an ngrok Pay-as-you-go plan because Facebook validates your ngrok domain. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm run startFacebook ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * In the dashboard, expand **Gateway** and click **Domains**. If you don't have an ngrok Pay-as-you-go plan, sign up by clicking **Update Subscription** and following the procedure. * On the **Domains** page, click **+ Create Domain** or **+ New Domain**. * In the **Domain** pane, enter a domain (for example, `myexample.ngrok.app`) and click **Continue**. Make sure your domain is available. * Close the **Start a Tunnel** and **Domain** panes. * Start ngrok with your domain: ```bash theme={null} ngrok http 3000 --url myexample.ngrok.app ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Instagram. ## 3. Configure Instagram to send webhooks Instagram sends webhook events through a Facebook app. First, connect your Instagram account to your Facebook page so Instagram activity is available to the app. Follow [Meta's documentation for setting up webhooks](https://developers.facebook.com/docs/graph-api/webhooks/getting-started), add the **Instagram** object, and set the following: * **Callback URL**: your ngrok URL with `/webhooks` at the end (for example, `https://myexample.ngrok.app/webhooks`). * **Verify token**: a value of your choice that your app checks during verification (for this example, `12345`). * **Include values**: `Yes`. After you save, confirm your localhost app receives the validation request and logs `WEBHOOK_VERIFIED` in the terminal. Then subscribe to the **comments** field and set your app mode to live. You can subscribe to more fields and objects using the same URL. ### Run webhooks with Instagram and ngrok Instagram sends different request body contents depending on the object and field. With the **comments** field, comment on one of your posts to trigger a call. Confirm your localhost app receives the message and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Instagram webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Meta for Developers](https://developers.facebook.com/), go to **My Apps**, **Settings**, **Basic**. * Click **Show** next to **App secret** and copy the value. * Create a Traffic Policy file named `instagram_policy.yml`. Replace `{your app secret}` with the value you copied: ```yaml theme={null} on_http_request: - name: "Instagram Webhooks" actions: - type: "webhook-validation" config: provider: facebook_graph_api secret: "{your app secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file instagram_policy.yml ``` * Trigger an Instagram event (for example, comment on a post) to send a request. Your app should receive the request and log it in the terminal. # Intercom Webhooks Source: https://ngrok.com/docs/integrations/webhooks/intercom-webhooks Develop and test Intercom webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Intercom webhooks on your localhost app. By integrating ngrok with Intercom, you can: * Develop and test Intercom webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Intercom in real time via the inspection UI and API. * Modify and replay Intercom webhook requests with a single click instead of reproducing events manually in your Intercom account. * Secure your app with Intercom webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * An Intercom account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Intercom. ## 3. Configure Intercom to send webhooks Webhook subscriptions in Intercom belong to an app, so create an internal integration app in your [Developer Hub](https://developers.intercom.com/) first. Follow [Intercom's documentation for setting up webhooks](https://developers.intercom.com/docs/webhooks/setting-up-webhooks), and set the following: * **Your request endpoint URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Webhook topics**: choose the topics that trigger the webhook (for this example, **contact.user.created**). After you save, Intercom sends a test request to your app. Confirm your localhost app receives it in the terminal. You can send another test request from Intercom's webhook settings. ### Run webhooks with Intercom and ngrok With **contact.user.created** selected, create a user in Intercom to trigger a call. Confirm your localhost app receives the event and logs both headers and body in the terminal. Intercom sends different request body contents depending on the topics you selected when registering the webhook. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Intercom webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the Intercom **Developer Hub**, click your app and then **Basic information**. * Copy the **Client secret** value. * Create a Traffic Policy file named `intercom_policy.yml`. Replace `{your client secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: intercom secret: "{your client secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file intercom_policy.yml ``` * Create a new contact in Intercom to trigger the webhook. Your app should receive the request and log it in the terminal. # Jira Webhooks Source: https://ngrok.com/docs/integrations/webhooks/jira-webhooks Develop and test Jira webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Jira webhooks on your localhost app. By integrating ngrok with Jira, you can: * Develop and test Jira webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Jira in real time via the inspection UI and API. * Modify and replay Jira webhook requests with a single click instead of reproducing events manually in your Jira instance. * Secure your app with Jira webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Jira instance and an account with **Jira Administrators** permission. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Jira. ## 3. Configure Jira to send webhooks Sign in to Jira with an account that has **Jira Administrators** permission, then follow [Atlassian's documentation for managing webhooks](https://support.atlassian.com/jira-cloud-administration/docs/manage-webhooks/), and set the following: * **Name**: a description for the webhook (for example, `ngrok Integration Webhook`). * **URL**: your ngrok URL (for example, `https://1234-abcd-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: select the events that trigger the webhook. * **JQL**: optionally, a query to limit scope (for example, `project in (ABC, XYZ)`). * **Secret**: set a secret and note it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with Jira and ngrok After you create the webhook, Jira sends a POST request to your app through ngrok when the selected events occur. To trigger an event, create a new issue or update an existing issue in a project that matches your webhook scope. Confirm your localhost app receives the webhook and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Jira webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In your Jira webhook settings, find the **Secret** field, generate or set a secret, and copy the value. * Create a Traffic Policy file named `jira_policy.yml`. Replace `{your secret}` with the secret from Jira: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: jira secret: "{your secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file jira_policy.yml ``` * Create or update an issue in Jira to trigger the webhook. Your app should receive the request and log it in the terminal. # LaunchDarkly Webhooks Source: https://ngrok.com/docs/integrations/webhooks/launchdarkly-webhooks Develop and test LaunchDarkly webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive LaunchDarkly webhooks on your localhost app. By integrating ngrok with LaunchDarkly, you can: * Develop and test LaunchDarkly webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from LaunchDarkly in real time via the inspection UI and API. * Modify and replay LaunchDarkly webhook requests with a single click instead of reproducing events manually in your LaunchDarkly account. * Secure your app with LaunchDarkly webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A LaunchDarkly account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with LaunchDarkly. ## 3. Configure LaunchDarkly to send webhooks Follow [LaunchDarkly's documentation for creating a webhook](https://launchdarkly.com/docs/home/infrastructure/webhooks), and set the following: * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Signing**: enable signing so ngrok can verify requests, and note the generated secret for the [verification step](#secure-webhook-requests) below. After you save, LaunchDarkly sends a request to your app. Confirm your localhost app receives the POST and logs both headers and body in the terminal. ### Run webhooks with LaunchDarkly and ngrok LaunchDarkly sends different request body contents depending on the event. To trigger new calls from LaunchDarkly to your app, make a change in your account. For this example, create a segment. Confirm your localhost app receives the event and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your LaunchDarkly webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Create a Traffic Policy file named `launchdarkly_policy.yml`. Replace `{your webhook secret}` with the **Secret** you copied when creating the webhook: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: launch_darkly secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file launchdarkly_policy.yml ``` * Create a new segment in LaunchDarkly to trigger the webhook. Your app should receive the request and log it in the terminal. # Linear Webhooks Source: https://ngrok.com/docs/integrations/webhooks/linear-webhooks Develop and test Linear webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Linear webhooks on your localhost app. By integrating ngrok with Linear, you can: * Develop and test Linear webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Linear in real time via the inspection UI and API. * Modify and replay Linear webhook requests with a single click instead of reproducing events manually in your Linear workspace. * Secure your app with Linear webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Linear account with administrative privileges. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Linear. ## 3. Configure Linear to send webhooks Sign in to Linear with an account that has administrative privileges, then follow [Linear's documentation for creating a webhook](https://linear.app/developers/webhooks), and set the following: * **Name**: a name for the webhook (for example, `ngrok Integration Webhook`). * **URL**: your ngrok URL (for example, `https://1234-abcd-5e6f-7g8h-9i0j.ngrok.app`). * **Data change events**: choose the events that trigger the webhook. Linear generates a signing secret when you create the webhook. Copy and store it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with Linear and ngrok Linear sends a POST request to your app through ngrok when the selected events occur. To trigger a webhook event, create a new issue or update an existing issue in a project. Confirm your localhost app receives the webhook and logs both headers and body in the terminal. Different messages are sent depending on the trigger events you chose. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Linear webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In your Linear webhook settings, copy the **Secret** value (Linear generates it when you create the webhook). * Create a Traffic Policy file named `linear_policy.yml`. Replace `{your secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: linear secret: "{your secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file linear_policy.yml ``` * Create or update an issue in Linear to trigger the webhook. Your app should receive the request and log it in the terminal. # Mailchimp Webhooks Source: https://ngrok.com/docs/integrations/webhooks/mailchimp-webhooks Develop and test Mailchimp webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Mailchimp webhooks on your localhost app. By integrating ngrok with Mailchimp, you can: * Develop and test Mailchimp webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Mailchimp in real time via the inspection UI and API. * Modify and replay Mailchimp webhook requests with a single click instead of reproducing events manually in your Mailchimp account. * Secure your app with Mailchimp webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Mailchimp account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Mailchimp. ## 3. Configure Mailchimp to send webhooks Follow [Mailchimp's documentation for creating an audience webhook](https://mailchimp.com/help/about-webhooks/), and set the following: * **Callback URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Actions**: choose which audience updates trigger the webhook. When you save the webhook, Mailchimp sends a verification request to your callback URL. Confirm your localhost app receives it and logs both headers and body in the terminal. ### Run webhooks with Mailchimp and ngrok Mailchimp sends different request body contents depending on the audience and the actions you selected. To trigger a call, cause one of those updates in your audience, such as adding a new subscriber. If the email is not on your contact list, you may need to import the contact first. Confirm your localhost app receives the event and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. # Mailgun Webhooks Source: https://ngrok.com/docs/integrations/webhooks/mailgun-webhooks Develop and test Mailgun webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Mailgun webhooks on your localhost app. By integrating ngrok with Mailgun, you can: * Develop and test Mailgun webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Mailgun in real time via the inspection UI and API. * Modify and replay Mailgun webhook requests with a single click instead of reproducing events manually in your Mailgun account. * Secure your app with Mailgun webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Mailgun account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Mailgun. ## 3. Configure Mailgun to send webhooks Follow [Mailgun's documentation for configuring webhooks](https://documentation.mailgun.com/docs/mailgun/user-manual/webhooks/configuring-webhooks), and set the following: * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Event type**: choose which events trigger the webhook (for this example, **Delivered Messages**). Mailgun can send a test request to your webhook URL. Use it to confirm your localhost app receives the event and logs both headers and body in the terminal. ### Run webhooks with Mailgun and ngrok Mailgun sends different request body contents depending on the event. To trigger a real call, authorize a recipient, have them confirm by email, and then send an email to that recipient (for example, with the Mailgun API). Replace the placeholders with values from your account: ```bash theme={null} curl --location --request POST 'API_BASE_URL/messages' \ --header 'Authorization: Basic api:API_KEY' \ --form 'from="mailgun@YOUR_DOMAIN"' \ --form 'to="AUTHORIZED_RECIPIENT"' \ --form 'subject="Hello from Mailgun"' \ --form 'text="Testing some Mailgun email!"' ``` Confirm your localhost app receives the event and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Mailgun webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Mailgun](https://app.mailgun.com/), go to **Sending** and **Webhooks**. * Click the eye icon next to **HTTP webhook signing key** and copy the value. * Create a Traffic Policy file named `mailgun_policy.yml`. Replace `{webhook signing key}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: mailgun secret: "{webhook signing key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file mailgun_policy.yml ``` * Send a new email to an authorized recipient to trigger the webhook. Your app should receive the request and log it in the terminal. # Modern Treasury Webhooks Source: https://ngrok.com/docs/integrations/webhooks/modern-treasury-webhooks Develop and test Modern Treasury webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Modern Treasury webhooks on your localhost app. By integrating ngrok with Modern Treasury, you can: * Develop and test Modern Treasury webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Modern Treasury in real time via the inspection UI and API. * Modify and replay Modern Treasury webhook requests with a single click instead of reproducing events manually in your Modern Treasury account. * Secure your app with Modern Treasury webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Modern Treasury account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Modern Treasury. ## 3. Configure Modern Treasury to send webhooks Follow [Modern Treasury's documentation for adding a webhook endpoint](https://docs.moderntreasury.com/platform/docs/set-a-webhook-url), and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events to send**: choose which events trigger the webhook (for this example, **Receive all events**). You can switch to Sandbox mode to use example accounts. If you have an active Modern Treasury account, you can skip Sandbox. ### Run webhooks with Modern Treasury and ngrok Modern Treasury sends different request body contents depending on the event. To trigger a call, create an event in your account, such as a new payment order. Confirm your localhost app receives event notifications as the payment updates and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Modern Treasury webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Modern Treasury](https://app.moderntreasury.com/), go to **Developers**, **Webhooks** tab, and click your webhook URL. * On the webhook page, copy the **Webhook Key** value. * Create a Traffic Policy file named `modern_treasury_policy.yml`. Replace `{your webhook key}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: modern_treasury secret: "{your webhook key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file modern_treasury_policy.yml ``` * Create a new payment order in Modern Treasury to trigger the webhook. Your app should receive the request and log it in the terminal. # MongoDB Webhooks Source: https://ngrok.com/docs/integrations/webhooks/mongodb-webhooks Develop and test MongoDB webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive MongoDB webhooks on your localhost app. By integrating ngrok with MongoDB Atlas, you can: * Develop and test MongoDB webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from MongoDB in real time via the inspection UI and API. * Modify and replay MongoDB webhook requests with a single click instead of reproducing events manually in your MongoDB account. * Secure your app with MongoDB webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A MongoDB Atlas account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with MongoDB. ## 3. Configure MongoDB to send webhooks Follow [MongoDB Atlas's documentation for configuring alert settings](https://www.mongodb.com/docs/atlas/configure-alerts/), add a webhook notification, and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Webhook Secret**: enter a value (for example, `12345`) and note it for the [verification step](#secure-webhook-requests) below. * **Alert condition**: choose which event triggers the webhook (for this example, when a user joins the project). If you don't have a project, create one first. ### Run webhooks with MongoDB and ngrok MongoDB sends different request body contents depending on the event. To trigger a call, cause the event you selected, such as inviting a new user to your project. After the user registers with MongoDB Atlas, confirm your localhost app receives the event and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your MongoDB webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Create a Traffic Policy file named `mongodb_policy.yml`. Replace `{your webhook secret}` with the value you entered in **Webhook Secret** when registering the webhook: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: mongodb secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file mongodb_policy.yml ``` * Invite a new user to your project to trigger the webhook. Your app should receive the request and log it in the terminal. # Mux Webhooks Source: https://ngrok.com/docs/integrations/webhooks/mux-webhooks Develop and test Mux webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Mux webhooks on your localhost app. By integrating ngrok with Mux, you can: * Develop and test Mux webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Mux in real time via the inspection UI and API. * Modify and replay Mux webhook requests with a single click instead of reproducing events manually in your Mux account. * Secure your app with Mux webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Mux account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Mux. ## 3. Configure Mux to send webhooks Follow [Mux's documentation for listening for webhooks](https://www.mux.com/docs/guides/listen-for-webhooks), select your environment, and set the following: * **URL to notify**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). ### Run webhooks with Mux and ngrok Mux sends different request body contents depending on the event. To trigger a call, create an event in your environment, such as a new asset. Confirm your localhost app receives event notifications and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Mux webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Mux](https://www.mux.com/), go to **Settings** and **Webhooks**. * On the **Webhooks** tab, click **Show Signing Secret** for your webhook and copy the value. * Create a Traffic Policy file named `mux_policy.yml`. Replace `{your signing secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: mux secret: "{your signing secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file mux_policy.yml ``` * Create a new asset in Mux to trigger the webhook. Your app should receive the request and log it in the terminal. # Okta Webhooks Source: https://ngrok.com/docs/integrations/webhooks/okta-webhooks Develop and test Okta webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Okta webhooks on your localhost app. By integrating ngrok with Okta, you can: * Develop and test Okta webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Okta in real time via the inspection UI and API. * Modify and replay Okta webhook requests with a single click instead of reproducing events manually in your Okta account. * Secure your app with Okta webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * An Okta account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm run startOkta ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Okta. ## 3. Configure Okta to send webhooks Follow [Okta's documentation for creating an event hook](https://help.okta.com/en-us/content/topics/automation-hooks/add-event-hooks.htm), and set the following: * **Name**: a name for the event hook (for example, `My Webhook`). * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: choose which events trigger the hook (for this example, **User sign in attempt**). Okta sends a one-time verification request to your URL before it can deliver events. Complete the verification so Okta can reach your localhost through ngrok. ### Run webhooks with Okta and ngrok Okta sends different request body contents depending on the event. You can send a test delivery from the event hook, or trigger a real event by signing out of the Okta console and signing in again. Confirm your localhost app receives the event and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. # PagerDuty Webhooks Source: https://ngrok.com/docs/integrations/webhooks/pagerduty-webhooks Develop and test PagerDuty webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive PagerDuty webhooks on your localhost app. By integrating ngrok with PagerDuty, you can: * Develop and test PagerDuty webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from PagerDuty in real time via the inspection UI and API. * Modify and replay PagerDuty webhook requests with a single click instead of reproducing events manually in your PagerDuty account. * Secure your app with PagerDuty webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A PagerDuty account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with PagerDuty. ## 3. Configure PagerDuty to send webhooks Follow [PagerDuty's documentation for creating a webhook](https://support.pagerduty.com/main/docs/webhooks), and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Scope type**: **Service**, then select a service to scope the webhook to. * **Event subscription**: choose which events trigger the webhook (for this example, select all events). When you create the webhook, PagerDuty shows a signing secret once. Copy it and note it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with PagerDuty and ngrok PagerDuty sends different request body contents depending on the event. You can send a test event from the webhook, or trigger a real event by creating a new incident on a service. Confirm your localhost app receives the event and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your PagerDuty webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Create a Traffic Policy file named `pagerduty_policy.yml`. Replace `{your webhook payload signing}` with the value you copied when creating the webhook: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: pagerduty secret: "{your webhook payload signing}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file pagerduty_policy.yml ``` * Create a new incident in PagerDuty to trigger the webhook. Your app should receive the request and log it in the terminal. # Pinwheel Webhooks Source: https://ngrok.com/docs/integrations/webhooks/pinwheel-webhooks Develop and test Pinwheel webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Pinwheel webhooks on your localhost app. By integrating ngrok with Pinwheel, you can: * Develop and test Pinwheel webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Pinwheel in real time via the inspection UI and API. * Modify and replay Pinwheel webhook requests with a single click instead of reproducing events manually in your Pinwheel account. * Secure your app with Pinwheel webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Pinwheel account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Pinwheel. ## 3. Configure Pinwheel to send webhooks Pinwheel registers webhook endpoints through its API rather than a dashboard form. Follow [Pinwheel's documentation for creating a webhook](https://docs.pinwheelapi.com/public/docs/webhooks-1) using your API secret and server URL from the Pinwheel Developer Portal, and set the following: * **url**: your ngrok URL. * **enabled\_events**: the events you want to subscribe to (for this example, `account.added`, `employment.added`, `identity.added`, and `income.added`). * **status**: `active`. The request looks like this, with `SERVER_URL`, `API_SECRET`, and `NGROK_URL` replaced by your values: ```bash theme={null} curl --request POST --url SERVER_URL/v1/webhooks \ --header 'x-api-secret: API_SECRET' \ --header 'Content-Type: application/json' --data '{ "url": "NGROK_URL", "status": "active", "enabled_events": [ "account.added", "employment.added", "identity.added", "income.added" ] }' ``` Confirm the response JSON has a `status` attribute set to `active`. ### Run webhooks with Pinwheel and ngrok After you create the webhook, Pinwheel sends requests to your app when the events you subscribed to occur. Because this example subscribes to `account.added`, trigger calls by having users log into their payroll accounts through the Pinwheel API. Confirm your localhost app receives the event and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Pinwheel webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Create a Traffic Policy file named `pinwheel_policy.yml`. Replace `{your api secret}` with the API Secret you noted when creating the webhook: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: pinwheel secret: "{your api secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file pinwheel_policy.yml ``` * Trigger an event (for example, via the Pinwheel API) to send a request. Your app should receive the request and log it in the terminal. # Plivo Webhooks Source: https://ngrok.com/docs/integrations/webhooks/plivo-webhooks Develop and test Plivo webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Plivo webhooks on your localhost app. By integrating ngrok with Plivo, you can: * Develop and test Plivo webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Plivo in real time via the inspection UI and API. * Modify and replay Plivo webhook requests with a single click instead of reproducing events manually in your Plivo account. * Secure your app with Plivo webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Plivo account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Plivo. ## 3. Configure Plivo to send webhooks Plivo sends webhook requests (also called callbacks) to your app when messages arrive on your Plivo numbers. Follow [Plivo's documentation for receiving SMS messages](https://www.plivo.com/docs/messaging/use-cases/receive-sms/receive-sms), and set the following: * **Message URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`), with the method set to **POST**. * **Trigger**: an incoming message on an SMS-enabled Plivo number, which you assign to the application you create. ### Run webhooks with Plivo and ngrok After you assign the application to your number, Plivo sends POST requests to your app whenever that number receives a message. To trigger a request, send an SMS to your Plivo number. Confirm your localhost app receives the notification and logs both the headers and body in the terminal. You can also review the message logs in Plivo to check the callback status. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Plivo webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [Plivo Console](https://console.plivo.com/), click the eye icon next to **Auth Token** and copy the value. * Create a Traffic Policy file named `plivo_policy.yml`. Replace `{your auth token}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: plivo secret: "{your auth token}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file plivo_policy.yml ``` * Send an SMS to your Plivo number to trigger the webhook. Your app should receive the request and log it in the terminal. # Pusher Webhooks Source: https://ngrok.com/docs/integrations/webhooks/pusher-webhooks Develop and test Pusher webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Pusher webhooks on your localhost app. By integrating ngrok with Pusher, you can: * Develop and test Pusher webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Pusher in real time via the inspection UI and API. * Modify and replay Pusher webhook requests with a single click instead of reproducing events manually in your Pusher account. * Secure your app with Pusher webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Pusher account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Pusher. ## 3. Configure Pusher to send webhooks Follow [Pusher's documentation for configuring Channels webhooks](https://pusher.com/docs/channels/server_api/webhooks/), and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Event type**: the events you want to receive (for this example, **Client events**). ### Run webhooks with Pusher and ngrok After you add the webhook, Pusher sends requests to your app when the events you subscribed to occur. To trigger a request, send a client event to your channel from the Pusher debug console. Confirm your localhost app receives the event and logs both the headers and body in the terminal. Pusher also records undelivered messages in its webhook error logs if a delivery fails. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Pusher webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [Pusher dashboard](https://dashboard.pusher.com/), open your channel, **Webhooks**, and copy the webhook secret (or the value shown for signing). * Create a Traffic Policy file named `pusher_policy.yml`. Replace `{your webhook secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: pusher secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file pusher_policy.yml ``` * Send a client event from the Debug console to trigger the webhook. Your app should receive the request and log it in the terminal. # SendGrid Webhooks Source: https://ngrok.com/docs/integrations/webhooks/sendgrid-webhooks Develop and test SendGrid webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive SendGrid webhooks on your localhost app. By integrating ngrok with SendGrid, you can: * Develop and test SendGrid webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from SendGrid in real time via the inspection UI and API. * Modify and replay SendGrid webhook requests with a single click instead of reproducing events manually in your SendGrid account. * Secure your app with SendGrid webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A SendGrid account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with SendGrid. ## 3. Configure SendGrid to send webhooks SendGrid sends webhook requests to your app with information about events as it processes your email. Follow [SendGrid's documentation for setting up the Event Webhook](https://www.twilio.com/docs/sendgrid/for-developers/tracking-events/getting-started-event-webhook), and set the following: * **HTTP Post URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: the events you want to receive (for this example, all deliverability events). Enable the Event Webhook so SendGrid starts sending requests. ### Run webhooks with SendGrid and ngrok To trigger webhooks, send an email through SendGrid using the Web API or SMTP. SendGrid then notifies your app as it processes and delivers the message. Confirm your localhost app receives the notifications and logs both the headers and body in the terminal. SendGrid sends different request body contents depending on the events you selected. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your SendGrid webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [SendGrid Dashboard](https://app.sendgrid.com/), go to **Settings** and **Mail Settings**. * Click **Signed Event Webhook Requests**. * In the popup, click **Generate Verification Key**, copy the key value, and click **Close**. Ensure **Signed Event Webhook Request Status** is **ENABLED**. * Create a Traffic Policy file named `sendgrid_policy.yml`. Replace `{your verification key}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: sendgrid secret: "{your verification key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file sendgrid_policy.yml ``` * Send an email through SendGrid to trigger the webhook. Your app should receive the request and log it in the terminal. # Sentry Webhooks Source: https://ngrok.com/docs/integrations/webhooks/sentry-webhooks Develop and test Sentry webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Sentry webhooks on your localhost app. By integrating ngrok with Sentry, you can: * Develop and test Sentry webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Sentry in real time via the inspection UI and API. * Modify and replay Sentry webhook requests with a single click instead of reproducing events manually in your Sentry account. * Secure your app with Sentry webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Sentry account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Sentry. ## 3. Configure Sentry to send webhooks Sentry sends webhook requests to your app through an internal integration. Follow [Sentry's documentation for the integration platform](https://docs.sentry.io/organization/integrations/integration-platform/webhooks/), create an internal integration, and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Permissions**: grant read and write access to **Issue & Event** so the integration can send issue events. * **Webhooks**: subscribe to the events you want to receive (for this example, **issue**). ### Run webhooks with Sentry and ngrok After you save the integration, Sentry sends requests to your app when the events you subscribed to occur. Because this example subscribes to issue events, resolve an issue in Sentry to trigger a request. If you have no issues yet, create a sample event first. Confirm your localhost app receives the notification and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Sentry webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Sentry](https://sentry.io/), go to **Settings**, **Developer Settings**, and your integration. * Under **CREDENTIALS**, copy the **Client Secret** value. * Create a Traffic Policy file named `sentry_policy.yml`. Replace `{your client secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: sentry secret: "{your client secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file sentry_policy.yml ``` * Resolve an issue or trigger another event in Sentry to send a request. Your app should receive the request and log it in the terminal. # Shopify Webhooks Source: https://ngrok.com/docs/integrations/webhooks/shopify-webhooks Develop and test Shopify webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Shopify webhooks on your localhost app. By integrating ngrok with Shopify, you can: * Develop and test Shopify webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Shopify in real time via the inspection UI and API. * Modify and replay Shopify webhook requests with a single click instead of reproducing events manually in your Shopify store. * Secure your app with Shopify webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Shopify store. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Shopify. ## 3. Configure Shopify to send webhooks Follow [Shopify's documentation for creating webhooks](https://help.shopify.com/en/manual/fulfillment/setup/notifications/webhooks), and set the following: * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Format**: `JSON`. * **Event**: the event that triggers the webhook (for this example, **Product creation**). * **Webhook API version**: choose the version marked `(Latest)`. ### Run webhooks with Shopify and ngrok After you save the webhook, Shopify sends requests to your app when the event you selected occurs. You can send a test notification from the same webhooks settings to confirm delivery. Because this example uses **Product creation**, add a product in your store to trigger a request. Confirm your localhost app receives the notification and logs both the headers and body in the terminal. Shopify sends different request body contents depending on the event you selected. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Shopify webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In **Settings**, **Notifications**, **Webhooks**, copy the value shown after **All your webhooks will be signed with**. * Create a Traffic Policy file named `shopify_policy.yml`. Replace `{your signing secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: shopify secret: "{your signing secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file shopify_policy.yml ``` * Create a product in your Shopify store to trigger the webhook. Your app should receive the request and log it in the terminal. # Signal Sciences Webhooks Source: https://ngrok.com/docs/integrations/webhooks/signalsciences-webhooks Develop and test Signal Sciences webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Signal Sciences webhooks on your localhost app. By integrating ngrok with Signal Sciences, you can: * Develop and test Signal Sciences webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Signal Sciences in real time via the inspection UI and API. * Modify and replay Signal Sciences webhook requests with a single click instead of reproducing events manually in your Signal Sciences account. * Secure your app with Signal Sciences webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Signal Sciences account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Signal Sciences. ## 3. Configure Signal Sciences to send webhooks Signal Sciences sends webhook requests to your app when specific events occur in your account. Follow [Signal Sciences' documentation for the generic webhook integration](https://www.fastly.com/documentation/guides/next-gen-waf/using-ngwaf/integrations/generic-webhooks), and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Activity**: the activity you want to receive (for this example, **All activity**). ### Run webhooks with Signal Sciences and ngrok After you create the site integration, Signal Sciences sends requests to your app when the activity you selected occurs. To trigger a request, change your site display name in Signal Sciences. Confirm your localhost app receives an event notification and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Signal Sciences webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [Signal Sciences Dashboard](https://dashboard.signalsciences.net/), go to **Site Integrations** in the **Manage** menu and click your webhook. * On the **Webhook** page, click the eye icon to reveal the **secret key** and copy the value. * Create a Traffic Policy file named `signalsciences_policy.yml`. Replace `{your webhook secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: signalsciences secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file signalsciences_policy.yml ``` * In the Signal Sciences dashboard, change your site display name to trigger an event. Your app should receive the request and log it in the terminal. # Slack Webhooks Source: https://ngrok.com/docs/integrations/webhooks/slack-webhooks Develop and test Slack webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Slack webhooks on your localhost app. Slack requires your application to be available at an HTTPS endpoint. By integrating ngrok with Slack, you can: * Develop and test Slack webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Slack in real time via the inspection UI and API. * Modify and replay Slack webhook requests with a single click instead of reproducing events manually in your Slack account. * Secure your app with Slack webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Slack account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app (Slack sample): ```bash theme={null} npm run startSlack ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Slack. ## 3. Configure Slack to send webhooks Slack sends webhook requests to your app through the Events API. Follow [Slack's documentation for the Events API](https://docs.slack.dev/apis/events-api/), create a Slack app, enable event subscriptions, and set the following: * **Request URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: subscribe to the events you want to receive (for this example, `message.im`). After you subscribe to events, install the app to your workspace. Slack makes a one-time call to your app with a challenge parameter and expects the app to respond with that value. See the [Slack Events API handshake](https://api.slack.com/apis/events-api#handshake) for details. ### Run webhooks with Slack and ngrok Because you subscribed to `message.im` and installed the app, you can trigger webhooks by sending a direct message. Message Slackbot with something like `Hello Slack bot` to send a request. Confirm your localhost app receives the notification. Slack sends different request body contents and headers depending on the event. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Slack webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * On **Basic Information** for your Slack app, click **Show** for **Signing Secret** and copy the value. * Create a Traffic Policy file named `slack_policy.yml`. Replace `{your signing secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: slack secret: "{your signing secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file slack_policy.yml ``` * Send a message to **Slackbot** (for example, `Hello Slack bot!`) to trigger the webhook. Your app should receive the request and log it in the terminal. # Sonatype Nexus Webhooks Source: https://ngrok.com/docs/integrations/webhooks/sonatype-nexus-webhooks Develop and test Sonatype Nexus webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Sonatype Nexus webhooks on your localhost app. By integrating ngrok with Sonatype Nexus, you can: * Develop and test Sonatype Nexus webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Sonatype Nexus in real time via the inspection UI and API. * Modify and replay Sonatype Nexus webhook requests with a single click instead of reproducing events manually in your Sonatype Nexus account. * Secure your app with Sonatype Nexus webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Sonatype Nexus Repository Manager instance. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Sonatype Nexus. ## 3. Configure Sonatype Nexus to send webhooks Follow [Sonatype's documentation for configuring a webhook capability](https://help.sonatype.com/en/configure-webhook.html), and set the following: * **Capability type**: choose **Webhook: Global** or **Webhook: Repository** (for this example, **Webhook: Global**). * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: select the event categories you want to receive (for this example, **repository** events). * **Secret Key**: set a secret and note it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with Sonatype Nexus and ngrok Sonatype Nexus sends a different request body depending on the event. To trigger a call to your app, create a repository or perform another action that matches the events you selected. Confirm your localhost app receives an event notification and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Sonatype Nexus webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In your **Sonatype Nexus Repository Manager**, go to **System**, **Capabilities**, and the webhook capability you created (**Webhook: Global** or **Webhook: Repository**). * On the **Webhook** page, open the **Settings** tab and enter a value in **Secret Key** (for example, `12345`). * Create a Traffic Policy file named `sonatype_nexus_policy.yml`. Replace `{your webhook secret}` with the **Secret Key** value: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: sonatype_nexus secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file sonatype_nexus_policy.yml ``` * Trigger an event (for example, create a repository as above) to send a request. Your app should receive the request and log it in the terminal. # Square Webhooks Source: https://ngrok.com/docs/integrations/webhooks/square-webhooks Develop and test Square webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Square webhooks on your localhost app. By integrating ngrok with Square, you can: * Develop and test Square webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Square in real time via the inspection UI and API. * Modify and replay Square webhook requests with a single click instead of reproducing events manually in your Square account. * Secure your app with Square webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Square account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Square. ## 3. Configure Square to send webhooks Follow [Square's documentation for subscribing to webhook event notifications](https://developer.squareup.com/docs/webhooks/step2subscribe), and set the following: * **URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: select the events you want (for this example, **catalog.version.updated**). ### Run webhooks with Square and ngrok Because this example uses **catalog.version.updated**, creating or updating a catalog item triggers a webhook. Create or update an item in your Square account to send a request to your app. Confirm your localhost app receives the notification and logs both headers and body in the terminal. Square sends a different request body depending on the event you selected. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Square webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Square Developer](https://developer.squareup.com/apps), open your application. * Scroll to **Access token**, click **Show**, and copy the value (**Sandbox Access token** or **Production Access token** depending on your environment). * Create a Traffic Policy file named `square_policy.yml`. Replace `{your access token}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: square secret: "{your access token}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file square_policy.yml ``` * Create a new item in the [Square Dashboard](https://squareup.com/dashboard/) to trigger the webhook. Your app should receive the request and log it in the terminal. # Stripe Webhooks Source: https://ngrok.com/docs/integrations/webhooks/stripe-webhooks Develop and test Stripe webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Stripe webhooks on your localhost app. Stripe requires your application to be available at an HTTPS endpoint. By integrating ngrok with Stripe, you can: * Develop and test Stripe webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Stripe in real time via the inspection UI and API. * Modify and replay Stripe webhook requests with a single click instead of reproducing events manually in your Stripe account. * Secure your app with Stripe webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Stripe account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Stripe. ## 3. Configure Stripe to send webhooks Follow [Stripe's documentation for creating a webhook endpoint](https://docs.stripe.com/webhooks), and set the following: * **Endpoint URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: choose the events you want (for this tutorial, all **Product** events). Stripe may show sample code for running an app. For this tutorial, use the sample app from [Start your app](#1-start-your-app) instead. ### Run webhooks with Stripe and ngrok Create a product in your Stripe account so Stripe notifies your app. Confirm your localhost app receives the product creation notification. Stripe sends different request body contents and headers depending on the event. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Stripe webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * On the webhook page for your endpoint, click **Reveal** under **Signing secret** and copy the value. * Create a Traffic Policy file named `stripe_policy.yml`. Replace `{your signing secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: stripe secret: "{your signing secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file stripe_policy.yml ``` * Create a new product in the Stripe dashboard to trigger the webhook. Your app should receive the request and log it in the terminal. # Svix Webhooks Source: https://ngrok.com/docs/integrations/webhooks/svix-webhooks Develop and test Svix webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Svix incoming webhooks on your localhost app. By integrating ngrok with Svix, you can: * Develop and test Svix webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Svix in real time via the inspection UI and API. * Modify and replay Svix webhook requests with a single click instead of reproducing events manually in your Svix account. * Secure your app with Svix webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Svix account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Svix. ## 3. Configure Svix to send webhooks Follow [Svix's documentation for adding an endpoint](https://docs.svix.com/receiving/using-app-portal/adding-endpoints), and set the following: * **Endpoint URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: select the events you want (for this example, **endpoint** and **message**). ### Run webhooks with Svix and ngrok Svix sends a different request body depending on the event. Send an example message from Svix, or trigger one of the events you selected, to send a request to your app. Confirm your localhost app receives the request and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Svix webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [Svix Dashboard](https://dashboard.svix.com/), go to **Incoming Webhooks**, click your webhook URL, and under **Signing Secret** click the eye icon and copy the value. * Create a Traffic Policy file named `svix_policy.yml`. Replace `{your webhook secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: svix secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file svix_policy.yml ``` * Create a new endpoint or send a test from the Svix dashboard to trigger the webhook. Your app should receive the request and log it in the terminal. # Microsoft Teams Webhooks Source: https://ngrok.com/docs/integrations/webhooks/teams-webhooks Develop and test Microsoft Teams webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Microsoft Teams webhooks on your localhost app. By integrating ngrok with Microsoft Teams, you can: * Develop and test Microsoft Teams webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Microsoft Teams in real time via the inspection UI and API. * Modify and replay Microsoft Teams webhook requests with a single click instead of reproducing events manually in your Microsoft Teams account. * Secure your app with Microsoft Teams webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Microsoft account and a Microsoft Teams page with an app. This integration requires an ngrok Pay-as-you-go plan because Microsoft Teams validates your ngrok domain and certificate. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. You need a reserved domain: in the dashboard, expand **Gateway**, click **Domains**, then **+ Create Domain** or **+ New Domain**, and reserve a domain (for example, `myexample.ngrok.app`). * Start ngrok with your reserved domain: ```bash theme={null} ngrok http 3000 --url myexample.ngrok.app ``` * Copy the URL ngrok displays (for example, `https://myexample.ngrok.app`). Your app is now exposed at that URL for use with Microsoft Teams. ## 3. Configure Microsoft Teams to send webhooks Microsoft Teams uses an outgoing webhook that acts as a bot: when you @mention it in a channel, it sends the message to your app. Follow [Microsoft's documentation for creating an outgoing webhook](https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-outgoing-webhook), and set the following: * **Name**: a name for the webhook (for example, `My local app`), which is also the @mention used to invoke it. * **Callback URL**: your ngrok URL (for example, `https://myexample.ngrok.app`). Microsoft Teams generates a **Security token** when you create the webhook. Note it for the [verification step](#secure-webhook-requests) below, because Teams shows it only once. ### Run webhooks with Microsoft Teams and ngrok In a channel, @mention the webhook by name (for example, `@My local app`) to send a message to your app. Your localhost app receives the notification and logs both headers and body in the terminal. The bot may reply with a message such as `{ message: "Thank you for the message" }`. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Microsoft Teams webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Use the **Security token** you copied when creating the outgoing webhook (see [Configure Microsoft Teams to send webhooks](#3-configure-microsoft-teams-to-send-webhooks)). * Create a Traffic Policy file named `teams_policy.yml`. Replace `{your app secret}` with that Security token: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: microsoft_teams secret: "{your app secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file teams_policy.yml ``` * In Teams, send another message using the @mention for your webhook. Your app should receive the request and log it in the terminal. # Terraform Cloud Webhooks Source: https://ngrok.com/docs/integrations/webhooks/terraform-webhooks Develop and test Terraform webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Terraform Cloud webhooks on your localhost app. By integrating ngrok with Terraform Cloud, you can: * Develop and test Terraform Cloud webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Terraform Cloud in real time via the inspection UI and API. * Modify and replay Terraform Cloud webhook requests with a single click instead of reproducing events manually in your Terraform Cloud account. * Secure your app with Terraform Cloud webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Terraform Cloud account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Terraform Cloud. ## 3. Configure Terraform Cloud to send webhooks Follow [HashiCorp's documentation for creating a workspace notification](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/notifications), and set the following: * **Destination**: choose the **Webhook** type. * **Name**: a name for the notification (for example, `ngrok`). * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Triggers**: choose which events send a notification (for this example, **All events**). * **Token**: set a token and note it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with Terraform Cloud and ngrok Terraform Cloud sends a different request body depending on the event. Start a new run in your workspace, or send a test notification, to trigger a request to your app. Confirm your localhost app receives event notifications and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Terraform Cloud webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Terraform Cloud](https://app.terraform.io/), open your workspace, then **Settings**, **Notifications**, and the notification you created (for example, **ngrok**). * Click **Edit Notification**, enter a value in the **Token** field (for example, `12345`), and click **Update notification**. * Create a Traffic Policy file named `terraform_policy.yml`. Replace `{your webhook token}` with that token value: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: terraform secret: "{your webhook token}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file terraform_policy.yml ``` * Start a new run (for example, **Actions** > **Start new run** with **Plan and apply**) to trigger the webhook. Your app should receive the request and log it in the terminal. # TikTok Webhooks Source: https://ngrok.com/docs/integrations/webhooks/tiktok-webhooks Develop and test TikTok webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive TikTok webhooks on your localhost app. By integrating ngrok with TikTok, you can: * Develop and test TikTok webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from TikTok in real time via the inspection UI and API. * Modify and replay TikTok webhook requests with a single click instead of reproducing events manually in your TikTok app. * Secure your app with TikTok webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A TikTok Developer account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with TikTok. ## 3. Configure TikTok to send webhooks Follow [TikTok's webhooks documentation](https://developers.tiktok.com/doc/webhooks-overview), and set the following: * **Callback URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). When you add the callback URL, TikTok subscribes your app to all events by default. TikTok may require you to configure Login Kit and submit your app for review before it delivers webhooks. ### Run webhooks with TikTok and ngrok TikTok sends a different request body depending on the event. After your app is approved and a user is associated with it, TikTok sends notifications for events such as account deauthorization, video upload failure, or video published. Confirm your localhost app receives these event notifications and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your TikTok webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the [TikTok Developer Portal](https://developers.tiktok.com/), open **Manage apps**, click your app, click the eye icon to reveal **Client secret**, and copy the value. * Create a Traffic Policy file named `tiktok_policy.yml`. Replace `{your webhook secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: tiktok secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file tiktok_policy.yml ``` * Use your TikTok app (or trigger an event as in [Run webhooks with TikTok and ngrok](#run-webhooks-with-tiktok-and-ngrok)) to send a request. Your app should receive the request and log it in the terminal. # Trend Micro Webhooks Source: https://ngrok.com/docs/integrations/webhooks/trendmicro-webhooks Develop and test Trend Micro webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Trend Micro Cloud One Conformity webhooks on your localhost app. By integrating ngrok with Trend Micro, you can: * Develop and test Trend Micro webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Trend Micro in real time via the inspection UI and API. * Modify and replay Trend Micro webhook requests with a single click instead of reproducing events manually in your Trend Micro account. * Secure your app with Trend Micro webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Trend Micro Cloud One account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Trend Micro. ## 3. Configure Trend Micro to send webhooks Trend Micro Cloud One Conformity sends webhook requests to your app when the Conformity bot runs. Follow [Trend Micro's documentation for webhook communication](https://docs.trendmicro.com/en-us/documentation/article/trend-micro-cloud-one-conformity-webhook-communication), and set the following: * **Webhook URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Automatic notifications**: turn on so Conformity notifies your app after the bot runs. * **Webhook Security Token**: set a token and note it for the [verification step](#secure-webhook-requests) below. If no cloud provider is associated with Conformity, add one first. ### Run webhooks with Trend Micro and ngrok Conformity notifies your webhook after the bot runs and reviews your cloud account for compliance. Run the Conformity bot to trigger a request to your app. After the bot finishes, confirm your localhost app receives a notification and logs both headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Trend Micro webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Trend Micro Cloud One](https://cloudone.trendmicro.com/), go to **Conformity** > **Accounts** > **Settings** > **Update communication settings** > **Configure** (Webhooks) > **Configure now**. * In the **Send notification to** popup, enter a value in **Webhook Security Token** (for example, `12345`) and click **Save**. * Create a Traffic Policy file named `trendmicro_policy.yml`. Replace `{your webhook token}` with that value: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: trendmicro_conformity secret: "{your webhook token}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file trendmicro_policy.yml ``` * Run the Conformity bot again to trigger the webhook. Your app should receive the request and log it in the terminal. # Twilio SMS Webhooks Source: https://ngrok.com/docs/integrations/webhooks/twilio-webhooks Develop and test Twilio webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive [Twilio SMS webhooks](https://www.twilio.com/docs/usage/webhooks/sms-webhooks) on your localhost app. By integrating ngrok with Twilio, you can: * Develop and test Twilio webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Twilio in real time via the inspection UI and API. * Modify and replay Twilio webhook requests with a single click instead of reproducing events manually in Twilio. * Secure your app with Twilio webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * An app that handles POST requests (for example, the [Twilio SMS webhook sample on GitHub](https://github.com/thomas-ngrok/ngrok-example-twilio-sms-webhook) or your own), running on port 3000 with a route such as `/sms`. * A Twilio account. ## 1. Start your app Use an Express app that responds to Twilio webhooks, such as the [Twilio SMS webhook sample on GitHub](https://github.com/thomas-ngrok/ngrok-example-twilio-sms-webhook). The handler should respond with TwiML; for more on parsing incoming SMS webhooks in Node.js, see the [Twilio blog](https://www.twilio.com/blog/parsing-an-incoming-twilio-sms-webhook-with-node-js). Start your app on port 3000. You can confirm it's running by visiting `http://localhost:3000`. The endpoint Twilio will call is typically `http://localhost:3000/sms` (or the path you configured). ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Twilio (use the full path to your webhook, for example `https://xxxx.ngrok.app/sms`). ## 3. Configure Twilio to send webhooks Follow [Twilio's documentation for configuring a phone number's messaging webhook](https://www.twilio.com/docs/usage/webhooks/messaging-webhooks), and set the following for the number you want to use: * **A message comes in**: set the webhook to your ngrok URL plus your app's path (for example, `https://xxxx.ngrok.app/sms`). * **HTTP method**: `HTTP POST`. ### Run webhooks with Twilio and ngrok Send an SMS to your Twilio phone number to trigger the webhook. Your app receives the request and can respond with TwiML. Confirm the request appears in your app logs and in the ngrok inspection UI. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Twilio account so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. ngrok verifies the signature of each request using your Twilio Auth Token, so only traffic from your Twilio account is allowed through. To add verification: * In the [Twilio Console](https://console.twilio.com/), copy your **Auth Token** (Primary). * Create a Traffic Policy file named `twilio_policy.yml`. Replace `{your auth token}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: twilio secret: "{your auth token}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file twilio_policy.yml ``` * Send another SMS to your Twilio number to trigger the webhook. Your app should receive the request and log it in the terminal. # X (formerly Twitter) Webhooks Source: https://ngrok.com/docs/integrations/webhooks/twitter-webhooks Develop and test X (formerly Twitter) webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive X (formerly Twitter) webhooks on your localhost app. By integrating ngrok with X, you can: * Develop and test X webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from X in real time via the inspection UI and API. * Modify and replay X webhook requests with a single click instead of reproducing events manually in your X account. * Secure your app with X webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * An approved X Developer account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with X. ## 3. Configure X to send webhooks X sends webhooks for Account Activity API events, and you register your webhook URL through the API rather than a dashboard form. Follow [X's Account Activity API documentation](https://docs.x.com/x-api/account-activity/introduction) to create an app, generate your access tokens, set up a dev environment, and register the webhook, using the following: * **Webhook URL**: your ngrok URL, URL-encoded when you pass it to the API. After you register the webhook, subscribe a user to it so X delivers that user's account activity to your app. ### Run webhooks with X and ngrok X sends different request body contents depending on the event. To trigger a notification, post from the subscribed X account. Confirm your localhost app receives the notification and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your X webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In the X Developer Portal, click **DEVELOPER TOOLS** and **Webhooks**. * On the **Webhooks** page, click **Copy** to copy the **Secret** value. * Create a Traffic Policy file named `twitter_policy.yml`. Replace `{your webhook secret}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: twitter secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file twitter_policy.yml ``` * Post a new tweet on [X](https://www.twitter.com/) to trigger the webhook. Your app should receive the request and log it in the terminal. # Typeform Webhooks Source: https://ngrok.com/docs/integrations/webhooks/typeform-webhooks Develop and test Typeform webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Typeform webhooks on your localhost app. By integrating ngrok with Typeform, you can: * Develop and test Typeform webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Typeform in real time via the inspection UI and API. * Modify and replay Typeform webhook requests with a single click instead of reproducing events manually in your Typeform account. * Secure your app with Typeform webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Typeform account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Typeform. ## 3. Configure Typeform to send webhooks Follow [Typeform's documentation for creating a webhook](https://www.typeform.com/developers/webhooks/), and set the following: * **Endpoint**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Secret**: set a secret (for example, `12345`) and note it for the [verification step](#secure-webhook-requests) below. Make sure the webhook is turned on. ### Run webhooks with Typeform and ngrok Typeform sends a POST request to your app when someone submits your form. Open your form's public link, complete it, and submit it. Confirm your localhost app receives the request and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Typeform webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Use the **Secret** value you set when configuring the webhook (see [Configure Typeform to send webhooks](#3-configure-typeform-to-send-webhooks)). * Create a Traffic Policy file named `typeform_policy.yml`. Replace `{your webhook secret}` with that value: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: typeform secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file typeform_policy.yml ``` * Submit the form again to trigger the webhook. Your app should receive the request and log it in the terminal. # VMware Workspace ONE Webhooks Source: https://ngrok.com/docs/integrations/webhooks/vmware-webhooks Develop and test VMware webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive VMware Workspace ONE webhooks on your localhost app. By integrating ngrok with VMware Workspace ONE, you can: * Develop and test VMware Workspace ONE webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from VMware Workspace ONE in real time via the inspection UI and API. * Modify and replay VMware Workspace ONE webhook requests with a single click instead of reproducing events manually in your VMware Workspace ONE account. * Secure your app with VMware Workspace ONE webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A VMware Workspace ONE account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with VMware Workspace ONE. ## 3. Configure VMware Workspace ONE to send webhooks Follow [VMware's documentation for configuring Event Notifications in Workspace ONE UEM](https://docs.vmware.com/en/VMware-Workspace-ONE-UEM/2206/System_Settings_On_Prem/GUID-AWT-SYSTEM-ADVANCED-API-NOTIF.html), and set the following when you add a notification rule: * **Target URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Format**: `JSON`. * **Events**: enable the events you want to send to your app. ### Run webhooks with VMware Workspace ONE and ngrok Trigger one of the events you enabled, such as enrolling a device. Confirm your localhost app receives the notification and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your VMware Workspace ONE webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [VMware Workspace ONE](https://console.cloud.vmware.com/), go to **Event Notifications** (as above), click your webhook, and **EDIT**. * On **Edit Event Notification**, enter **Username** and **Password** (and **Confirm Password**), then click **SAVE**. * Create a Traffic Policy file named `vmware_policy.yml`. Replace `{username}` and `{password}` with the values you set: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: vmware_workspace secret: "{username}::{password}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file vmware_policy.yml ``` * Trigger an event again (for example, device enrollment) to send a request. Your app should receive the request and log it in the terminal. # Cisco Webex Webhooks Source: https://ngrok.com/docs/integrations/webhooks/webex-webhooks Develop and test Webex webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Cisco Webex webhooks on your localhost app. By integrating ngrok with Cisco Webex, you can: * Develop and test Cisco Webex webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Cisco Webex in real time via the inspection UI and API. * Modify and replay Cisco Webex webhook requests with a single click instead of reproducing events manually in your Cisco Webex account. * Secure your app with Cisco Webex webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Cisco Webex account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Cisco Webex. ## 3. Configure Cisco Webex to send webhooks Follow [Webex's documentation for creating a webhook](https://developer.webex.com/messaging/docs/api/guides/webhooks), which registers the webhook through the Webex API, and set the following in the request body: * **targetUrl**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **resource** and **event**: the activity you want to receive (for this example, the `meetings` resource and the `started` event). * **secret**: a value used to sign requests; note it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with Cisco Webex and ngrok You subscribed to the `meetings` resource and `started` event, so start a meeting in your Webex account to trigger the webhook. Confirm your localhost app receives the notification and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Cisco Webex webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Use the **SECRET\_KEY** value you set when registering the webhook (see [Configure Cisco Webex to send webhooks](#3-configure-cisco-webex-to-send-webhooks)). * Create a Traffic Policy file named `webex_policy.yml`. Replace `{your webhook secret}` with that value: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: webex secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file webex_policy.yml ``` * Start a meeting in [Cisco Webex](https://www.webex.com) to trigger the webhook. Your app should receive the request and log it in the terminal. # WhatsApp Webhooks Source: https://ngrok.com/docs/integrations/webhooks/whatsapp-webhooks Develop and test WhatsApp webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive WhatsApp webhooks on your localhost app. By integrating ngrok with WhatsApp, you can: * Develop and test WhatsApp webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from WhatsApp in real time via the inspection UI and API. * Modify and replay WhatsApp webhook requests with a single click instead of reproducing events manually in your WhatsApp account. * Secure your app with WhatsApp webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Facebook app and a business phone number for WhatsApp. This integration requires an ngrok Pay-as-you-go plan because Meta validates your ngrok domain and certificate. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app (Facebook/WhatsApp sample): ```bash theme={null} npm run startFacebook ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. You need a reserved domain: in the dashboard, expand **Gateway**, click **Domains**, then **+ Create Domain** or **+ New Domain**, and reserve a domain (for example, `myexample.ngrok.app`). * Start ngrok with your reserved domain: ```bash theme={null} ngrok http 3000 --url myexample.ngrok.app ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with WhatsApp (use `https://myexample.ngrok.app/webhooks` as the callback URL). ## 3. Configure WhatsApp to send webhooks Follow [Meta's documentation for setting up WhatsApp Cloud API webhooks](https://developers.facebook.com/docs/whatsapp/cloud-api/guides/set-up-webhooks), and set the following: * **Callback URL**: your ngrok URL with your app's path (for example, `https://myexample.ngrok.app/webhooks`). * **Verify token**: a value of your choosing (for example, `12345`) that your app checks during the verification handshake. * **Webhook fields**: subscribe to **messages** and any other fields you need. After you save, confirm your app receives the verification request and logs `WEBHOOK_VERIFIED`. ### Run webhooks with WhatsApp and ngrok Send a message to your WhatsApp business number to trigger the webhook. Confirm your localhost app receives the message and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your WhatsApp webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Meta for Developers](https://developers.facebook.com/), go to **Settings** > **Basic**, click **Show** for **App secret**, and copy the value. * Create a Traffic Policy file named `whatsapp_policy.yml`. Replace `{your app secret}` with the value you copied: ```yaml theme={null} on_http_request: - name: "WhatsApp Webhooks" actions: - type: "webhook-validation" config: provider: facebook_graph_api secret: "{your app secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file whatsapp_policy.yml ``` * Send another message to the WhatsApp number to trigger the webhook. Your app should receive the request and log it in the terminal. # Worldline Webhooks Source: https://ngrok.com/docs/integrations/webhooks/worldline-webhooks Develop and test Worldline webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Worldline webhooks on your localhost app. By integrating ngrok with Worldline, you can: * Develop and test Worldline webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Worldline in real time via the inspection UI and API. * Modify and replay Worldline webhook requests with a single click instead of reproducing events manually in your Worldline account. * Secure your app with Worldline webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Worldline account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Worldline. ## 3. Configure Worldline to send webhooks Follow [Worldline's documentation for creating a webhook endpoint](https://docs.direct.worldline-solutions.com/en/integration/api-developer-guide/webhooks), and set the following: * **Endpoint URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). When you create the webhook, Worldline generates a **Secret Webhook Key**. Note it for the [verification step](#secure-webhook-requests) below. ### Run webhooks with Worldline and ngrok Worldline sends different request body contents depending on the event. To trigger a test, generate a payment link, open it, and start or cancel a payment. Confirm your localhost app receives the POST and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Worldline webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Use the **Secret Webhook Key** you noted when creating the webhook (see [Configure Worldline to send webhooks](#3-configure-worldline-to-send-webhooks)). * Create a Traffic Policy file named `worldline_policy.yml`. Replace `{your webhook secret}` with that value: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: worldline secret: "{your webhook secret}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file worldline_policy.yml ``` * Create a new payment link, open it, and complete or cancel a payment to trigger the webhook. Your app should receive the request and log it in the terminal. # Xero Webhooks Source: https://ngrok.com/docs/integrations/webhooks/xero-webhooks Develop and test Xero webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide walks you through using ngrok to receive Xero webhooks on your localhost app. By integrating ngrok with Xero, you can: * Develop and test Xero webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Xero in real time via the inspection UI and API. * Modify and replay Xero webhook requests with a single click instead of reproducing events manually in your Xero account. * Secure your app with Xero webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Xero account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Xero. ## 3. Configure Xero to send webhooks Follow [Xero's documentation for creating a webhook](https://developer.xero.com/documentation/guides/webhooks/overview/), and set the following: * **Delivery URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Event types**: select the events you want (for example, **Contacts** or **Invoices**). Xero sends an "Intent to Receive" validation request before it activates the webhook, so confirm your app receives it, then save. Xero also generates a **Webhooks key** that you note for the [verification step](#secure-webhook-requests) below. ### Run webhooks with Xero and ngrok Xero sends different request body contents depending on the event. To trigger a notification, create a record for one of your subscribed event types, such as a new contact. Confirm your localhost app receives the event and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Xero webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Xero Developer](https://developer.xero.com/myapps/webhooks/), open your app and **Webhooks**. * Copy the **Webhooks key** value. * Create a Traffic Policy file named `xero_policy.yml`. Replace `{your webhook key}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: xero secret: "{your webhook key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file xero_policy.yml ``` * Create a new contact in the [Xero dashboard](https://go.xero.com/) to trigger the webhook. Your app should receive the request and log it in the terminal. # Zendesk Webhooks Source: https://ngrok.com/docs/integrations/webhooks/zendesk-webhooks Develop and test Zendesk webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide explains how to use ngrok to receive Zendesk webhooks on your localhost app. By integrating ngrok with Zendesk, you can: * Develop and test Zendesk webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Zendesk in real time via the inspection UI and API. * Modify and replay Zendesk webhook requests with a single click instead of reproducing events manually in your Zendesk account. * Secure your app with Zendesk webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Zendesk account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Zendesk. ## 3. Configure Zendesk to send webhooks Follow [Zendesk's documentation for creating a webhook](https://support.zendesk.com/hc/en-us/articles/4408839108378-Creating-webhooks-to-interact-with-third-party-systems), and set the following: * **Endpoint URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Request method**: `POST`. * **Request format**: `JSON`. * **Authentication**: `None` for this tutorial. To send events, connect the webhook to a trigger or automation. For this example, create an automation that fires on a high-priority incident ticket and add a **Notify webhook** action that calls your webhook. ### Run webhooks with Zendesk and ngrok Create a ticket that matches your automation's conditions, such as a high-priority incident. Zendesk evaluates automations on a schedule, so the webhook may take some time to fire. Confirm your localhost app receives the request and logs both the headers and body in the terminal. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Zendesk webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * In [Zendesk](https://www.zendesk.com), go to **Admin Center** > **Apps and integrations** > **Webhooks** and click your webhook. * Click **Reveal secret** and **Copy** to copy the **Secret key**. * Create a Traffic Policy file named `zendesk_policy.yml`. Replace `{your secret key}` with the value you copied: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: zendesk secret: "{your secret key}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file zendesk_policy.yml ``` * Create another high-priority incident ticket (as in [Run webhooks with Zendesk and ngrok](#run-webhooks-with-zendesk-and-ngrok)) to trigger the webhook. Your app should receive the request and log it in the terminal. # Zoom Webhooks Source: https://ngrok.com/docs/integrations/webhooks/zoom-webhooks Develop and test Zoom webhooks from localhost. If you don't see your preferred webhook provider, you can [request support for it](https://dashboard.ngrok.com/early-access#webhook-providers). This guide shows you how to use ngrok to receive Zoom webhooks on your localhost app. By integrating ngrok with Zoom, you can: * Develop and test Zoom webhooks locally without deploying to a public environment or setting up HTTPS. * Inspect and troubleshoot requests from Zoom in real time via the inspection UI and API. * Modify and replay Zoom webhook requests with a single click instead of reproducing events manually in your Zoom account. * Secure your app with Zoom webhook validation provided by ngrok. Invalid requests are blocked by ngrok before reaching your app. ## What you'll need * An [ngrok account](https://ngrok.com/signup) and your [authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). * The [ngrok agent](https://ngrok.com/download) installed. * [Node.js](https://nodejs.org/) installed (for the sample app, or use your own app). * A Zoom account. ## 1. Start your app For this tutorial, you can use the [sample Node.js app on GitHub](https://github.com/ngrok/ngrok-webhook-nodejs-sample). To install the sample, run the following in a terminal: ```bash theme={null} git clone https://github.com/ngrok/ngrok-webhook-nodejs-sample.git cd ngrok-webhook-nodejs-sample npm install ``` Then start the app: ```bash theme={null} npm start ``` The app runs on port 3000 by default. You can confirm it's running by visiting `http://localhost:3000`. The app logs request headers and body in the terminal and shows a message in the browser. ## 2. Expose your app with ngrok Once your app is running locally, you're ready to put it online securely using ngrok. * Copy [your ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) from the dashboard. The ngrok agent uses your authtoken to authenticate when you start a tunnel. * Start ngrok: ```bash theme={null} ngrok http 3000 ``` * Copy the URL ngrok displays. Your app is now exposed at that URL for use with Zoom. ## 3. Configure Zoom to send webhooks This guide uses the **Webhook Only** app type, which works with a free Zoom account. Follow [Zoom's documentation for setting up webhooks](https://developers.zoom.us/docs/api/webhooks/), and set the following: * **Event notification endpoint URL**: your ngrok URL (for example, `https://1a2b-3c4d-5e6f-7g8h-9i0j.ngrok.app`). * **Events**: select the events you want (for this example, **Start Meeting** and **End Meeting**). Zoom generates a **Secret Token** that you note for the [verification step](#secure-webhook-requests) below. Before Zoom saves the webhook, it validates the endpoint URL by sending an `endpoint.url_validation` event that your app must answer within a few seconds. The [sample app](#1-start-your-app) does not handle this, so use an app that responds to Zoom's validation request. See [Zoom's webhook documentation](https://developers.zoom.us/docs/api/webhooks/) for how to build the response. ### Run webhooks with Zoom and ngrok Start a meeting in your Zoom account and end it after a few seconds to trigger both events. Confirm your localhost app receives the notifications and logs both the headers and body in the terminal. Zoom sends different request body contents and headers depending on the event. Each request includes an authorization header with the Secret Token, which you can validate in your app or with ngrok's webhook verification below. ### Inspecting requests ngrok's [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) captures all requests made through your ngrok endpoint to your localhost app. Select any request to view detailed information about both the request and response. To avoid exposing secrets, accounts only collect traffic metadata by default. You must enable full capture in the **Traffic Inspector** section of [your account settings](https://dashboard.ngrok.com/settings) to capture complete request and response data. Use the traffic inspector to: * Validate webhook payloads and response data * Debug request headers, methods, and status codes * Troubleshoot integration issues without adding logging to your app ### Replaying requests Test your webhook handling code without triggering new events from your service using the Traffic Inspector's replay feature: 1. Send a test webhook from your service to generate traffic in your Traffic Inspector. 2. Select the request you want to replay in the traffic inspector. 3. Choose your replay option: * Click **Replay** to send the exact same request again * Select **Replay with modifications** to edit the request before sending 4. (Optional) Modify the request: Edit any part of the original request, such as changing field values in the request body. 5. Send the request by clicking **Replay**. Your local application will receive the replayed request and log the data to the terminal. ## Secure webhook requests ngrok can verify that incoming requests are from your Zoom webhook so only that traffic reaches your app. Webhook verification is limited to 500 validations per month on free accounts. If you need more, you can upgrade to Hobbyist or Pay-as-you-go. See [TPU Pricing](/docs/pricing-limits/traffic-policy-unit-pricing/) for details. To add verification: * Use the **Secret Token** you copied when configuring the webhook (see [Configure Zoom to send webhooks](#3-configure-zoom-to-send-webhooks)). * Create a Traffic Policy file named `zoom_policy.yml`. Replace `{your secret token}` with that value: ```yaml theme={null} on_http_request: - actions: - type: verify-webhook config: provider: zoom secret: "{your secret token}" ``` * Restart ngrok with the policy file: ```bash theme={null} ngrok http 3000 --traffic-policy-file zoom_policy.yml ``` * Start and then end a meeting in Zoom to trigger the webhook. Your app should receive the request and log it in the terminal. # Events Source: https://ngrok.com/docs/obs/events/index Learn about ngrok's event logging system for exporting traffic logs, audit logs, and configuration changes to external services. ## Overview Whenever changes occur in your ngrok account or when traffic transits through your endpoints, a log is generated. You may export these logs and in some cases, filter them to those relevant to you and publish them to any number of destinations. ngrok's logging system was designed for three primary use cases: * Sending logs of your ngrok traffic to external services such as Datadog and Amazon CloudWatch Logs * Sending audit logs of ngrok configuration changes to your SIEM * Enabling you to programmatically respond to events on your ngrok account There are [over 40 log sources published](/docs/obs/events/reference) via ngrok at the time of writing. They break down into two important classes of logs: * **[Traffic Logs](/docs/obs/events/reference/#traffic-events)**: When traffic transits through your endpoints like processing an HTTP request or TCP connection * **[Audit Logs](/docs/obs/events/reference/#audit-events)**: Changes to your account like create/update/delete of Domains, API Keys, IP Policies, etc. ## Concepts ngrok's logging system is composed of three simple primitives. You create a **Log Export** to *subscribe* to a set of **Log Sources** and *publish* those logs to one or more **Log Destinations**. * **[Log Exporting](#log-exporting)**: Define which Log Sources to capture and which Log Destinations to publish to. * **[Log Sources](#sources)**: The ngrok log types to capture and optionally a set of fields to record and a boolean filter expression. * **[Log Destinations](#destinations)**: Where logs are sent, for example, Datadog, CloudWatch, Kinesis. ## Quickstart You can create your first Log Export on the [Log Exporting page of your ngrok Dashboard](https://dashboard.ngrok.com/log-exporting) to begin capturing and publishing logs. Guides are also published to get started with each of ngrok's Log Destinations: * **[AWS CloudWatch Logs](/docs/integrations/event-destinations/amazon-cloudwatch-event-destination)** * **[AWS Firehose](/docs/integrations/event-destinations/amazon-firehose-event-destination)** * **[AWS Kinesis](/docs/integrations/event-destinations/amazon-kinesis-event-destination)** * **[Azure Logs Ingestion](/docs/integrations/event-destinations/azure-logs-event-destination)** * **[Datadog Logs](/docs/integrations/event-destinations/datadog-event-destination)** ## Log exporting A Log Export defines which [Log Sources](/docs/obs/events/reference/) to capture and which destinations to publish to. If you're familiar with other logging systems, they may call this a *listener*, a *hook*, a *probe* or a *tap*. When you create a Log Export, you select: * One or more Log Sources to subscribe to * One or more Log Destinations to publish the subscribed logs to Log Exports can be created on the [ngrok Dashboard](https://dashboard.ngrok.com/log-exporting) or via the [ Log Exporting API Resource](/docs/api-reference/eventsubscriptions/create). ## Log sources Log Sources choose which logs a Log Export captures. Other logging systems may call this a *selector*. The [ngrok Log Sources reference](/docs/obs/events/reference) documents the full list of Log Sources and their fields. ### Versioning and payload stability Log Sources include a version number in their name. Each Log Source ends with a version string after a dot, for example, `api_key_created.v0`. New fields may be added to the payloads of a Log Source without a version change. Any other change to the log representation will lead to the introduction of a new version of the Log Source. ### Audit logs ngrok emits [Audit Logs](/docs/obs/events/reference/#audit-events) when changes are made to your account like create/update/delete of Domains, API Keys, IP Policies, etc. ### Traffic logs When traffic transits through your endpoints, ngrok emits [Traffic Logs](/docs/obs/events/reference/#traffic-events) like processing an HTTP request or TCP connection. Traffic Logs support the ability to select exactly which fields you'd like to include in the captured log. This provides you with control over how much data is sent to the destinations. Traffic Logs also support the ability to be **filtered**. As part of the Log Source definition you can specify a CEL expression which is evaluated against each log to determine if it should be captured. See the [Filters](#filters) section for further detail. #### Filters You may specify a filter on [Traffic Logs](/docs/obs/events/reference/#traffic-events) since the velocity of these logs can be quite high. Filters are a boolean expression defined in [Google's Common Expression Language (CEL)](https://github.com/google/cel-spec/blob/master/doc/langdef.md#standard). Filters are evaluated on each log as it is published to determine whether it should be sent to the Log Export's destinations. Traffic Logs are often high cardinality. Filters are supported because you may only be interested in logging a subset of traffic (for example, specific client IPs). Filters also enable you to control spend on downstream destinations where you incur costs based on the quantity of data processed and stored. The log object is made available to the CEL filter expression as the `ev` variable. `ev` corresponds to the `object` field of the captured log. Use standard [JSONPath](https://goessner.net/articles/JsonPath/) syntax to access the fields of the log you want to use in your filters. Filter expressions have the following limitations: * Only some fields of an log are available in the `ev` object. The Log Source's reference documentation will specify whether a field may be evaluated in a filter expression. * Log metadata fields like `principal` or `event_timestamp` are not available #### Filter examples ###### Filter for connections from a particular hostname ```cel theme={null} ev.conn.server_name.matches("ngrok-docs-examples\\.ngrok\\.dev") ``` ###### Filter for connections to an endpoint that didn't use HTTPS ```cel theme={null} ev.conn.server_name.matches(".*-your-org\\.ngrok\\.dev") && ev.conn.server_port == 80 ``` ###### Filter for connections to a hostname that exclude traffic from a client IP ```cel theme={null} ev.conn.client_ip != "2601:0:8200:0:4cd7:fd52:0:7823" && ev.conn.server_name == "ngrok-docs-examples.ngrok.dev" ``` ## Log destinations An Log Destination encapsulates the configuration to publish logs to other systems. Other logging systems may call this a *sink* or *output*. Log Destinations are typically third-party logging aggregators. The following destinations are currently supported: * [Amazon CloudWatch](/docs/integrations/event-destinations/amazon-cloudwatch-event-destination) * [Amazon Firehose](/docs/integrations/event-destinations/amazon-firehose-event-destination) * [Amazon Kinesis](/docs/integrations/event-destinations/amazon-kinesis-event-destination) * AWS S3 (via Kinesis Firehose) * [Azure Logs Ingestion](/docs/integrations/event-destinations/azure-logs-event-destination) * [Datadog Logs](/docs/integrations/event-destinations/datadog-event-destination) Each destination requires provider-specific configuration. If you create a destination in the ngrok dashboard, you'll be prompted to send a test log to verify the integration. When configuring AWS destinations you'll be prompted to optionally download a small helper script which will automatically configure the appropriate IAM objects necessary for integration. You may also set these values up via the AWS Console or tools like Terraform or Pulumi. Amazon S3 is not a directly supported destination. Instead, configure Amazon Firehose to [deliver logs into an S3 bucket](https://docs.aws.amazon.com/firehose/latest/dev/create-destination.html#create-destination-s3). ## Log payloads Logs are serialized as JSON when they are published to a destination. All logs include the following fields: | Name | Description | Example | | - | - | - | | `account_id` | unique identifier for the account, always prefixed with ac\_ | `ac_2OtNvAlhso10Gx6s7eupzX3F98q` | | `event_id` | unique identifier for this log, always prefixed with ev\_ | `ev_1vPlyBW3OR44bpPphS4HIZyajDD` | | `event_type` | identifies the object, action, and version of the log | `ip_policy_created.v0` | | `event_timestamp` | timestamp of when the log fired in RFC 3339 format | `2021-07-16T21:44:37Z` | | `object` | the log object | See examples below | | `principal` | an object of the principal who emitted this log, null for traffic logs | See example below | The `object` property of the log is distinct for each Log Source and contain a JSON object with additional information about the log. For [Audit Logs](/docs/obs/events/reference#audit-events), the `object` representation is identical to its API resource at the time of capture. [Traffic Logs](/docs/obs/events/reference#traffic-events) define their own `object` representation because they have no corresponding API resource definition. #### Principal object The `principal` object in every log describe the user or Service User responsible for initiating the log. Principal is defined for all Audit Logs and it is `null` for Traffic Logs (since they do not include a credential). | Name | Description | Example | | - | - | - | | id | unique identifier for the principal, either a user or Service User | `usr_2OtNv9qH5Nk4NuNeszZ39gBxZ4H` | | subject | human readable unique identifier for the principal, either a user email or a Service User name | `foo@example.com` | | source | where the principal initiated the log, either 'Dashboard' or 'API' | `API` | | credential | id and uri resource for the credential used for authentication, `null` if `source` is 'Dashboard' | `{"id": "ak_2Oxt94wYsBTLwFUoMZcJRvJTaub","uri": "https://api.ngrok.com/api_keys/ak_2Oxt94wYsBTLwFUoMZcJRvJTaub"}` | ### Example payloads #### [ip\_policy\_created.v0](/docs/obs/events/reference/#ip-policy) log ```json theme={null} { "event_id": "ev_25X2AsJ5xpvuOParTYUQWe12XKo", "event_type": "ip_policy_created.v0", "event_timestamp": "2022-02-23T23:29:29Z", "account_id": "ac_2OtNvAlhso10Gx6s7eupzX3F98q", "principal": { "id": "usr_2OtNv9qH5Nk4NuNeszZ39gBxZ4H", "subject": "foo@example.com", "source": "API", "credential": { "id": "ak_2Oxt94wYsBTLwFUoMZcJRvJTaub", "uri": "https://api.ngrok.com/api_keys/ak_2Oxt94wYsBTLwFUoMZcJRvJTaub" } }, "object": { "id": "ipp_25X2Ao39z73FlVQKZ1iReMPe6Qv", "uri": "https://api.ngrok.com/ip_policies/ipp_25X2Ao39z73FlVQKZ1iReMPe6Qv", "created_at": "2022-02-23T23:29:29Z", "description": "Home network IP", "metadata": "", "action": "allow" } } ``` #### [http\_request\_complete.v0](/docs/obs/events/reference/#http-request-complete) log ```json theme={null} { "event_id": "ev_25X3yFS6TDkig1KDJWIc4nnJO0c", "event_type": "http_request_complete.v0", "event_timestamp": "2022-02-23T23:44:16Z", "account_id": "ac_2OtNvAlhso10Gx6s7eupzX3F98q", "object": { "conn": { "client_ip": "2601:0:8200:9e:4cd7:0:c97f:7823", "server_name": "ngrok-docs-example.ngrok.app", "server_port": "" }, "http": { "request": { "first_byte_ts": null, "last_byte_ts": null, "method": "GET", "url": { "path": "/docs/obs" }, "version": "HTTP/2.0" }, "response": { "body_length": 13079, "first_byte_ts": "2022-02-23T23:44:16.732791273Z", "last_byte_ts": "2022-02-23T23:44:16.737257209Z", "status_code": 200 } } } } ``` #### [tcp\_connection\_closed.v0](/docs/obs/events/reference/#tcp-connection-closed) log ```json theme={null} { "event_id": "ev_25X4osod1q306srserDeFyghTC4", "event_type": "tcp_connection_closed.v0", "event_timestamp": "2022-02-23T23:51:14Z", "account_id": "ac_2OtNvAlhso10Gx6s7eupzX3F98q", "object": { "conn": { "bytes_in": 3437, "bytes_out": 90256, "client_ip": "2601:0:8200:9e:4cd7:0:c97f:7823", "end_ts": "2022-02-23T23:51:14.005372199Z", "server_name": "ngrok-docs-example.ngrok.app", "server_port": "", "start_ts": "2022-02-23T23:44:16.528374173Z" } } } ``` ## Events pricing Log Exports are available to free and Pay-as-you-go plans. For more details, see the [ngrok pricing](https://ngrok.com/pricing) page. The Traffic Inspector is included with all ngrok accounts with a retention period starting at 1 day and going up to 3 days. An additional 90 days can be purchased as an add-on to the Pay-as-you-go plan. For more details, see the [ngrok pricing](https://ngrok.com/pricing) page. # Event Source Reference Source: https://ngrok.com/docs/obs/events/reference Reference documentation for all ngrok Log Event Sources. ## APIKey ### api\_key\_created.v0 Triggers when an API key is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique API key resource identifier | | uri | string | URI to the API resource of this API key | | description | string | human-readable description of what uses the API key to authenticate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined data of this API key. optional, max 4096 bytes | | created\_at | string | timestamp when the api key was created, RFC 3339 format | | token | string | the bearer token that can be placed into the Authorization header to authenticate request to the ngrok API. **This value is only available one time, on the API response from key creation. Otherwise it is null.** | | owner\_id | string | If supplied at credential creation, ownership will be assigned to the specified User or Bot. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Bot. | ### api\_key\_deleted.v0 Triggers when an API key is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique API key resource identifier | | uri | string | URI to the API resource of this API key | | description | string | human-readable description of what uses the API key to authenticate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined data of this API key. optional, max 4096 bytes | | created\_at | string | timestamp when the api key was created, RFC 3339 format | | token | string | the bearer token that can be placed into the Authorization header to authenticate request to the ngrok API. **This value is only available one time, on the API response from key creation. Otherwise it is null.** | | owner\_id | string | If supplied at credential creation, ownership will be assigned to the specified User or Bot. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Bot. | ### api\_key\_updated.v0 Triggers when an API key is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique API key resource identifier | | uri | string | URI to the API resource of this API key | | description | string | human-readable description of what uses the API key to authenticate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined data of this API key. optional, max 4096 bytes | | created\_at | string | timestamp when the api key was created, RFC 3339 format | | token | string | the bearer token that can be placed into the Authorization header to authenticate request to the ngrok API. **This value is only available one time, on the API response from key creation. Otherwise it is null.** | | owner\_id | string | If supplied at credential creation, ownership will be assigned to the specified User or Bot. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Bot. | ## AgentSessionEvent ### agent\_session\_start.v0 Triggers when an agent session starts. This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | session.id | string | a resource identifier | | session.uri | string | a uri for locating a resource | | credential.id | string | a resource identifier | | credential.uri | string | a uri for locating a resource | | agent\_ip | string | the ip address from which the agent is connecting | | ingress\_server\_ip | string | the ip address of the ingress server to which the agent is connecting | | region | string | the region of the tunnel server | | ingress\_hostname | string | the hostname of the tunnel server | | user\_agent | string | the user agent provided to the tunnel server by the agent | | metadata | string | the session metadata provided by the agent on connection | | os | string | the operating system of the machine on which the agent is running | | arch | string | the CPU architecture of the machine on which the agent is running | | transport | string | the transport protocol used internally by the agent "muxado" for agents and agent libraries, "ssh" for reverse SSH tunnels | | started\_at | string | the time at which the session started | | expires\_at | string | the time at which the session expires | | stopped\_at | string | the time at which the session stopped | | deprecated.upcoming\_minimum\_version | string | the upcoming minimum supported agent version | | deprecated.upcoming\_enforcement\_date | string | the date by which the current agent must be upgraded to the upcoming minimum version | | deprecated.message | string | additional information about the agent deprecation | | error | string | on a failed session start, an explanation of the failure on a successful session start, the empty string on a session stop, the reason for the session stop | ### agent\_session\_stop.v0 Triggers when an agent session stops. This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | session.id | string | a resource identifier | | session.uri | string | a uri for locating a resource | | credential.id | string | a resource identifier | | credential.uri | string | a uri for locating a resource | | agent\_ip | string | the ip address from which the agent is connecting | | ingress\_server\_ip | string | the ip address of the ingress server to which the agent is connecting | | region | string | the region of the tunnel server | | ingress\_hostname | string | the hostname of the tunnel server | | user\_agent | string | the user agent provided to the tunnel server by the agent | | metadata | string | the session metadata provided by the agent on connection | | os | string | the operating system of the machine on which the agent is running | | arch | string | the CPU architecture of the machine on which the agent is running | | transport | string | the transport protocol used internally by the agent "muxado" for agents and agent libraries, "ssh" for reverse SSH tunnels | | started\_at | string | the time at which the session started | | expires\_at | string | the time at which the session expires | | stopped\_at | string | the time at which the session stopped | | deprecated.upcoming\_minimum\_version | string | the upcoming minimum supported agent version | | deprecated.upcoming\_enforcement\_date | string | the date by which the current agent must be upgraded to the upcoming minimum version | | deprecated.message | string | additional information about the agent deprecation | | error | string | on a failed session start, an explanation of the failure on a successful session start, the empty string on a session stop, the reason for the session stop | ## CertificateAuthority ### certificate\_authority\_created.v0 Triggers when a certificate authority is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this Certificate Authority | | uri | string | URI of the Certificate Authority API resource | | created\_at | string | timestamp when the Certificate Authority was created, RFC 3339 format | | description | string | human-readable description of this Certificate Authority. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this Certificate Authority. optional, max 4096 bytes. | | ca\_pem | string | raw PEM of the Certificate Authority | | subject\_common\_name | string | subject common name of the Certificate Authority | | not\_before | string | timestamp when this Certificate Authority becomes valid, RFC 3339 format | | not\_after | string | timestamp when this Certificate Authority becomes invalid, RFC 3339 format | | key\_usages | List\ | set of actions the private key of this Certificate Authority can be used for | | extended\_key\_usages | List\ | extended set of actions the private key of this Certificate Authority can be used for | ### certificate\_authority\_deleted.v0 Triggers when a certificate authority is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this Certificate Authority | | uri | string | URI of the Certificate Authority API resource | | created\_at | string | timestamp when the Certificate Authority was created, RFC 3339 format | | description | string | human-readable description of this Certificate Authority. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this Certificate Authority. optional, max 4096 bytes. | | ca\_pem | string | raw PEM of the Certificate Authority | | subject\_common\_name | string | subject common name of the Certificate Authority | | not\_before | string | timestamp when this Certificate Authority becomes valid, RFC 3339 format | | not\_after | string | timestamp when this Certificate Authority becomes invalid, RFC 3339 format | | key\_usages | List\ | set of actions the private key of this Certificate Authority can be used for | | extended\_key\_usages | List\ | extended set of actions the private key of this Certificate Authority can be used for | ### certificate\_authority\_updated.v0 Triggers when a certificate authority is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this Certificate Authority | | uri | string | URI of the Certificate Authority API resource | | created\_at | string | timestamp when the Certificate Authority was created, RFC 3339 format | | description | string | human-readable description of this Certificate Authority. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this Certificate Authority. optional, max 4096 bytes. | | ca\_pem | string | raw PEM of the Certificate Authority | | subject\_common\_name | string | subject common name of the Certificate Authority | | not\_before | string | timestamp when this Certificate Authority becomes valid, RFC 3339 format | | not\_after | string | timestamp when this Certificate Authority becomes invalid, RFC 3339 format | | key\_usages | List\ | set of actions the private key of this Certificate Authority can be used for | | extended\_key\_usages | List\ | extended set of actions the private key of this Certificate Authority can be used for | ## Credential ### tunnel\_credential\_created.v0 Triggers when a tunnel credential is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique tunnel credential resource identifier | | uri | string | URI of the tunnel credential API resource | | created\_at | string | timestamp when the tunnel credential was created, RFC 3339 format | | description | string | human-readable description of who or what will use the credential to authenticate. Optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this credential. Optional, max 4096 bytes. | | token | string | the credential's authtoken that can be used to authenticate an ngrok agent. **This value is only available one time, on the API response from credential creation, otherwise it is null.** | | acl | List\ | optional list of ACL rules. If unspecified, the credential will have no restrictions. The only allowed ACL rule at this time is the `bind` rule. The `bind` rule allows the caller to restrict what domains, addresses, and labels the token is allowed to bind. For example, to allow the token to open a tunnel on example.ngrok.io your ACL would include the rule `bind:example.ngrok.io`. Bind rules for domains may specify a leading wildcard to match multiple domains with a common suffix. For example, you may specify a rule of `bind:*.example.com` which will allow `x.example.com`, `y.example.com`, `*.example.com`, etc. Bind rules for labels may specify a wildcard key and/or value to match multiple labels. For example, you may specify a rule of `bind:*=example` which will allow `x=example`, `y=example`, etc. A rule of `'*'` is equivalent to no acl at all and will explicitly permit all actions. | | owner\_id | string | If supplied at credential creation, ownership will be assigned to the specified User or Service User. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Service User. Accepts one of: User ID, User email, or SCIM User ID. | ### tunnel\_credential\_deleted.v0 Triggers when a tunnel credential is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique tunnel credential resource identifier | | uri | string | URI of the tunnel credential API resource | | created\_at | string | timestamp when the tunnel credential was created, RFC 3339 format | | description | string | human-readable description of who or what will use the credential to authenticate. Optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this credential. Optional, max 4096 bytes. | | token | string | the credential's authtoken that can be used to authenticate an ngrok agent. **This value is only available one time, on the API response from credential creation, otherwise it is null.** | | acl | List\ | optional list of ACL rules. If unspecified, the credential will have no restrictions. The only allowed ACL rule at this time is the `bind` rule. The `bind` rule allows the caller to restrict what domains, addresses, and labels the token is allowed to bind. For example, to allow the token to open a tunnel on example.ngrok.io your ACL would include the rule `bind:example.ngrok.io`. Bind rules for domains may specify a leading wildcard to match multiple domains with a common suffix. For example, you may specify a rule of `bind:*.example.com` which will allow `x.example.com`, `y.example.com`, `*.example.com`, etc. Bind rules for labels may specify a wildcard key and/or value to match multiple labels. For example, you may specify a rule of `bind:*=example` which will allow `x=example`, `y=example`, etc. A rule of `'*'` is equivalent to no acl at all and will explicitly permit all actions. | | owner\_id | string | If supplied at credential creation, ownership will be assigned to the specified User or Service User. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Service User. Accepts one of: User ID, User email, or SCIM User ID. | ### tunnel\_credential\_updated.v0 Triggers when a tunnel credential is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique tunnel credential resource identifier | | uri | string | URI of the tunnel credential API resource | | created\_at | string | timestamp when the tunnel credential was created, RFC 3339 format | | description | string | human-readable description of who or what will use the credential to authenticate. Optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this credential. Optional, max 4096 bytes. | | token | string | the credential's authtoken that can be used to authenticate an ngrok agent. **This value is only available one time, on the API response from credential creation, otherwise it is null.** | | acl | List\ | optional list of ACL rules. If unspecified, the credential will have no restrictions. The only allowed ACL rule at this time is the `bind` rule. The `bind` rule allows the caller to restrict what domains, addresses, and labels the token is allowed to bind. For example, to allow the token to open a tunnel on example.ngrok.io your ACL would include the rule `bind:example.ngrok.io`. Bind rules for domains may specify a leading wildcard to match multiple domains with a common suffix. For example, you may specify a rule of `bind:*.example.com` which will allow `x.example.com`, `y.example.com`, `*.example.com`, etc. Bind rules for labels may specify a wildcard key and/or value to match multiple labels. For example, you may specify a rule of `bind:*=example` which will allow `x=example`, `y=example`, etc. A rule of `'*'` is equivalent to no acl at all and will explicitly permit all actions. | | owner\_id | string | If supplied at credential creation, ownership will be assigned to the specified User or Service User. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Service User. Accepts one of: User ID, User email, or SCIM User ID. | ## EventDestination ### event\_destination\_created.v0 Triggers when an Event Destination is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | Unique identifier for this Event Destination. | | metadata | string | Arbitrary user-defined machine-readable data of this Event Destination. Optional, max 4096 bytes. | | created\_at | string | Timestamp when the Event Destination was created, RFC 3339 format. | | description | string | Human-readable description of the Event Destination. Optional, max 255 bytes. | | format | string | The output format you would like to serialize events into when sending to their target. Currently the only accepted value is `JSON`. | | target.firehose.auth.role.role\_arn | string | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | target.firehose.auth.creds.aws\_access\_key\_id | string | The ID portion of an AWS access key. | | target.firehose.auth.creds.aws\_secret\_access\_key | string | The secret portion of an AWS access key. | | target.firehose.delivery\_stream\_arn | string | An Amazon Resource Name specifying the Firehose delivery stream to deposit events into. | | target.kinesis.auth.role.role\_arn | string | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | target.kinesis.auth.creds.aws\_access\_key\_id | string | The ID portion of an AWS access key. | | target.kinesis.auth.creds.aws\_secret\_access\_key | string | The secret portion of an AWS access key. | | target.kinesis.stream\_arn | string | An Amazon Resource Name specifying the Kinesis stream to deposit events into. | | target.cloudwatch\_logs.auth.role.role\_arn | string | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | target.cloudwatch\_logs.auth.creds.aws\_access\_key\_id | string | The ID portion of an AWS access key. | | target.cloudwatch\_logs.auth.creds.aws\_secret\_access\_key | string | The secret portion of an AWS access key. | | target.cloudwatch\_logs.log\_group\_arn | string | An Amazon Resource Name specifying the CloudWatch Logs group to deposit events into. | | target.datadog.api\_key | string | Datadog API key to use. | | target.datadog.ddtags | string | Tags to send with the event. | | target.datadog.service | string | Service name to send with the event. | | target.datadog.ddsite | string | Datadog site to send event to. | | target.azure\_logs\_ingestion.tenant\_id | string | Tenant ID for the Azure account | | target.azure\_logs\_ingestion.client\_id | string | Client ID for the application client | | target.azure\_logs\_ingestion.client\_secret | string | Client Secret for the application client | | target.azure\_logs\_ingestion.logs\_ingestion\_uri | string | Data collection endpoint logs ingestion URI | | target.azure\_logs\_ingestion.data\_collection\_rule\_id | string | Data collection rule immutable ID | | target.azure\_logs\_ingestion.data\_collection\_stream\_name | string | Data collection stream name to use as destination, located inside the DCR | | uri | string | URI of the Event Destination API resource. | ### event\_destination\_deleted.v0 Triggers when an Event Destination is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | Unique identifier for this Event Destination. | | metadata | string | Arbitrary user-defined machine-readable data of this Event Destination. Optional, max 4096 bytes. | | created\_at | string | Timestamp when the Event Destination was created, RFC 3339 format. | | description | string | Human-readable description of the Event Destination. Optional, max 255 bytes. | | format | string | The output format you would like to serialize events into when sending to their target. Currently the only accepted value is `JSON`. | | target.firehose.auth.role.role\_arn | string | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | target.firehose.auth.creds.aws\_access\_key\_id | string | The ID portion of an AWS access key. | | target.firehose.auth.creds.aws\_secret\_access\_key | string | The secret portion of an AWS access key. | | target.firehose.delivery\_stream\_arn | string | An Amazon Resource Name specifying the Firehose delivery stream to deposit events into. | | target.kinesis.auth.role.role\_arn | string | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | target.kinesis.auth.creds.aws\_access\_key\_id | string | The ID portion of an AWS access key. | | target.kinesis.auth.creds.aws\_secret\_access\_key | string | The secret portion of an AWS access key. | | target.kinesis.stream\_arn | string | An Amazon Resource Name specifying the Kinesis stream to deposit events into. | | target.cloudwatch\_logs.auth.role.role\_arn | string | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | target.cloudwatch\_logs.auth.creds.aws\_access\_key\_id | string | The ID portion of an AWS access key. | | target.cloudwatch\_logs.auth.creds.aws\_secret\_access\_key | string | The secret portion of an AWS access key. | | target.cloudwatch\_logs.log\_group\_arn | string | An Amazon Resource Name specifying the CloudWatch Logs group to deposit events into. | | target.datadog.api\_key | string | Datadog API key to use. | | target.datadog.ddtags | string | Tags to send with the event. | | target.datadog.service | string | Service name to send with the event. | | target.datadog.ddsite | string | Datadog site to send event to. | | target.azure\_logs\_ingestion.tenant\_id | string | Tenant ID for the Azure account | | target.azure\_logs\_ingestion.client\_id | string | Client ID for the application client | | target.azure\_logs\_ingestion.client\_secret | string | Client Secret for the application client | | target.azure\_logs\_ingestion.logs\_ingestion\_uri | string | Data collection endpoint logs ingestion URI | | target.azure\_logs\_ingestion.data\_collection\_rule\_id | string | Data collection rule immutable ID | | target.azure\_logs\_ingestion.data\_collection\_stream\_name | string | Data collection stream name to use as destination, located inside the DCR | | uri | string | URI of the Event Destination API resource. | ### event\_destination\_updated.v0 Triggers when an Event Destination is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | Unique identifier for this Event Destination. | | metadata | string | Arbitrary user-defined machine-readable data of this Event Destination. Optional, max 4096 bytes. | | created\_at | string | Timestamp when the Event Destination was created, RFC 3339 format. | | description | string | Human-readable description of the Event Destination. Optional, max 255 bytes. | | format | string | The output format you would like to serialize events into when sending to their target. Currently the only accepted value is `JSON`. | | target.firehose.auth.role.role\_arn | string | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | target.firehose.auth.creds.aws\_access\_key\_id | string | The ID portion of an AWS access key. | | target.firehose.auth.creds.aws\_secret\_access\_key | string | The secret portion of an AWS access key. | | target.firehose.delivery\_stream\_arn | string | An Amazon Resource Name specifying the Firehose delivery stream to deposit events into. | | target.kinesis.auth.role.role\_arn | string | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | target.kinesis.auth.creds.aws\_access\_key\_id | string | The ID portion of an AWS access key. | | target.kinesis.auth.creds.aws\_secret\_access\_key | string | The secret portion of an AWS access key. | | target.kinesis.stream\_arn | string | An Amazon Resource Name specifying the Kinesis stream to deposit events into. | | target.cloudwatch\_logs.auth.role.role\_arn | string | An ARN that specifies the role that ngrok should use to deliver to the configured target. | | target.cloudwatch\_logs.auth.creds.aws\_access\_key\_id | string | The ID portion of an AWS access key. | | target.cloudwatch\_logs.auth.creds.aws\_secret\_access\_key | string | The secret portion of an AWS access key. | | target.cloudwatch\_logs.log\_group\_arn | string | An Amazon Resource Name specifying the CloudWatch Logs group to deposit events into. | | target.datadog.api\_key | string | Datadog API key to use. | | target.datadog.ddtags | string | Tags to send with the event. | | target.datadog.service | string | Service name to send with the event. | | target.datadog.ddsite | string | Datadog site to send event to. | | target.azure\_logs\_ingestion.tenant\_id | string | Tenant ID for the Azure account | | target.azure\_logs\_ingestion.client\_id | string | Client ID for the application client | | target.azure\_logs\_ingestion.client\_secret | string | Client Secret for the application client | | target.azure\_logs\_ingestion.logs\_ingestion\_uri | string | Data collection endpoint logs ingestion URI | | target.azure\_logs\_ingestion.data\_collection\_rule\_id | string | Data collection rule immutable ID | | target.azure\_logs\_ingestion.data\_collection\_stream\_name | string | Data collection stream name to use as destination, located inside the DCR | | uri | string | URI of the Event Destination API resource. | ## EventSubscription ### event\_subscription\_created.v0 Triggers when an Event Subscription is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | Unique identifier for this Event Subscription. | | uri | string | URI of the Event Subscription API resource. | | created\_at | string | When the Event Subscription was created (RFC 3339 format). | | metadata | string | Arbitrary customer supplied information intended to be machine readable. Optional, max 4096 chars. | | description | string | Arbitrary customer supplied information intended to be human readable. Optional, max 255 chars. | | sources.type | string | Type of event for which an event subscription will trigger | | sources.uri | string | URI of the Event Source API resource. | | destinations.id | string | a resource identifier | | destinations.uri | string | a uri for locating a resource | ### event\_subscription\_deleted.v0 Triggers when an Event Subscription is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | Unique identifier for this Event Subscription. | | uri | string | URI of the Event Subscription API resource. | | created\_at | string | When the Event Subscription was created (RFC 3339 format). | | metadata | string | Arbitrary customer supplied information intended to be machine readable. Optional, max 4096 chars. | | description | string | Arbitrary customer supplied information intended to be human readable. Optional, max 255 chars. | | sources.type | string | Type of event for which an event subscription will trigger | | sources.uri | string | URI of the Event Source API resource. | | destinations.id | string | a resource identifier | | destinations.uri | string | a uri for locating a resource | ### event\_subscription\_updated.v0 Triggers when an Event Subscription is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | Unique identifier for this Event Subscription. | | uri | string | URI of the Event Subscription API resource. | | created\_at | string | When the Event Subscription was created (RFC 3339 format). | | metadata | string | Arbitrary customer supplied information intended to be machine readable. Optional, max 4096 chars. | | description | string | Arbitrary customer supplied information intended to be human readable. Optional, max 255 chars. | | sources.type | string | Type of event for which an event subscription will trigger | | sources.uri | string | URI of the Event Source API resource. | | destinations.id | string | a resource identifier | | destinations.uri | string | a uri for locating a resource | ## HTTP Request Complete ### http\_request\_complete.v0 Triggers when an HTTP request completes. This event type supports filters and selectable fields. | Name | Type | Description | Filterable? | | - | - | - | - | | backend.connection\_reused | bool | True if ngrok reused a TCP connection to transmit the HTTP request to the upstream service. | | | basic\_auth.decision | string | 'allow' if the Basic Auth module permitted the request to the upstream service, otherwise 'block' | | | basic\_auth.username | string | The username in the HTTP basic auth credentials | | | circuit\_breaker.decision | string | Whether the HTTP request was sent to the upstream service. 'allow' if the breaker was closed, 'block' if the breaker was open, 'allow\_while\_open' if the request was allowed while the breaker is open | | | compression.algorithm | string | The compression algorithm used to encode responses from the endpoint. Either 'gzip', 'deflate', or 'none'. | | | compression.bytes\_saved | int64 | The difference between the size of the raw response and the size of the response as compressed by the Compression Module | | | conn.client\_ip | string | The source IP of the TCP connection to the ngrok edge | Yes | | conn.server\_ip | string | The IP address of the server that received the request | Yes | | conn.server\_name | string | The hostname associated with this connection. | Yes | | conn.server\_port | int32 | The port that the connection for this request came in on | Yes | | conn.start\_ts | timestamp | The timestamp when the TCP connection to the ngrok edge is established | | | http.request.body\_length | int64 | The size of the request body in bytes | | | http.request.headers | Map\> | A map of normalized headers from the requesting client. Header keys are capitalized and header values are lowercased. | | | http.request.method | string | The request method, normalized to lowercase | | | http.request.url.host | string | The host component of the request URL | | | http.request.url.path | string | The path component of the request URL | | | http.request.url.query | string | The query string component of the request URL | | | http.request.url.raw | string | The full URL of the request including scheme, host, path, and query string | | | http.request.url.scheme | string | The scheme component of the request URL | | | http.request.user\_agent | string | The value of the User-Agent header in the request received by ngrok edge | | | http.response.body\_length | int64 | The size of the response body in bytes | | | http.response.headers | Map\> | A map of normalized response headers. Header keys are capitalized and header values are lowercased. | | | http.response.status\_code | int32 | The status code of the response returned by the ngrok edge | | | ip\_policy.decision | string | 'allow' if IP Policy module permitted the request to the upstream service, 'block' otherwise | | | ja4\_fingerprint | string | The JA4 fingerprint of the client TLS handshake | | | oauth.app\_client\_id | string | The OAuth application client ID | | | oauth.decision | string | 'allow' if the OAuth module permitted the request to the upstream service, 'block' otherwise | | | oauth.user.application\_user\_id | string | The ngrok application user ID for the authenticated OAuth user | | | oauth.user.email | string | The authenticated user's email returned by the OAuth provider | | | oauth.user.id | string | The authenticated user's ID returned by the OAuth provider | | | oauth.user.name | string | The authenticated user's name returned by the OAuth provider | | | principal.email | string | The ngrok IAM principal email for the private endpoint connection | | | principal.id | string | The ngrok IAM principal ID for the private endpoint connection | | | principal.name | string | The ngrok IAM principal name for the private endpoint connection | | | tls.cipher\_suite | string | The cipher suite selected during the TLS handshake | | | tls.client\_cert.serial\_number | string | The serial number of the client's leaf TLS certificate in the Mutual TLS handshake | | | tls.client\_cert.subject.cn | string | The subject common name of the client's leaf TLS certificate in the Mutual TLS handshake | | | tls.version | string | The version of the TLS protocol used between the client and the ngrok edge | | | traffic\_policy.logs | List\> | Logs collected from the log action in a Traffic Policy | | | webhook\_verification.decision | string | 'allow' if the Webhook Verification module permitted the request to the upstream service, 'block' otherwise | | ## IPPolicy ### ip\_policy\_created.v0 Triggers when an IP Policy is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this IP policy | | uri | string | URI of the IP Policy API resource | | created\_at | string | timestamp when the IP policy was created, RFC 3339 format | | description | string | human-readable description of the source IPs of this IP policy. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this IP policy. optional, max 4096 bytes. | ### ip\_policy\_deleted.v0 Triggers when an IP Policy is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this IP policy | | uri | string | URI of the IP Policy API resource | | created\_at | string | timestamp when the IP policy was created, RFC 3339 format | | description | string | human-readable description of the source IPs of this IP policy. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this IP policy. optional, max 4096 bytes. | ### ip\_policy\_updated.v0 Triggers when an IP Policy is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this IP policy | | uri | string | URI of the IP Policy API resource | | created\_at | string | timestamp when the IP policy was created, RFC 3339 format | | description | string | human-readable description of the source IPs of this IP policy. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this IP policy. optional, max 4096 bytes. | ## IPPolicyRule ### ip\_policy\_rule\_created.v0 Triggers when an IP Policy Rule is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this IP policy rule | | uri | string | URI of the IP policy rule API resource | | created\_at | string | timestamp when the IP policy rule was created, RFC 3339 format | | description | string | human-readable description of the source IPs of this IP rule. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this IP policy rule. optional, max 4096 bytes. | | cidr | string | an IP or IP range specified in CIDR notation. IPv4 and IPv6 are both supported. | | ip\_policy.id | string | a resource identifier | | ip\_policy.uri | string | a uri for locating a resource | | action | string | the action to apply to the policy rule, either `allow` or `deny` | ### ip\_policy\_rule\_deleted.v0 Triggers when an IP Policy Rule is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this IP policy rule | | uri | string | URI of the IP policy rule API resource | | created\_at | string | timestamp when the IP policy rule was created, RFC 3339 format | | description | string | human-readable description of the source IPs of this IP rule. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this IP policy rule. optional, max 4096 bytes. | | cidr | string | an IP or IP range specified in CIDR notation. IPv4 and IPv6 are both supported. | | ip\_policy.id | string | a resource identifier | | ip\_policy.uri | string | a uri for locating a resource | | action | string | the action to apply to the policy rule, either `allow` or `deny` | ### ip\_policy\_rule\_updated.v0 Triggers when an IP Policy Rule is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this IP policy rule | | uri | string | URI of the IP policy rule API resource | | created\_at | string | timestamp when the IP policy rule was created, RFC 3339 format | | description | string | human-readable description of the source IPs of this IP rule. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this IP policy rule. optional, max 4096 bytes. | | cidr | string | an IP or IP range specified in CIDR notation. IPv4 and IPv6 are both supported. | | ip\_policy.id | string | a resource identifier | | ip\_policy.uri | string | a uri for locating a resource | | action | string | the action to apply to the policy rule, either `allow` or `deny` | ## IPRestriction ### ip\_restriction\_created.v0 Triggers when an IP Restriction is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this IP restriction | | uri | string | URI of the IP restriction API resource | | created\_at | string | timestamp when the IP restriction was created, RFC 3339 format | | description | string | human-readable description of this IP restriction. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this IP restriction. optional, max 4096 bytes. | | enforced | boolean | true if the IP restriction will be enforced. if false, only warnings will be issued | | type | string | the type of IP restriction. this defines what traffic will be restricted with the attached policies. four values are currently supported: `dashboard`, `api`, `agent`, and `endpoints` | | ip\_policies.id | string | a resource identifier | | ip\_policies.uri | string | a uri for locating a resource | ### ip\_restriction\_deleted.v0 Triggers when an IP Restriction is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this IP restriction | | uri | string | URI of the IP restriction API resource | | created\_at | string | timestamp when the IP restriction was created, RFC 3339 format | | description | string | human-readable description of this IP restriction. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this IP restriction. optional, max 4096 bytes. | | enforced | boolean | true if the IP restriction will be enforced. if false, only warnings will be issued | | type | string | the type of IP restriction. this defines what traffic will be restricted with the attached policies. four values are currently supported: `dashboard`, `api`, `agent`, and `endpoints` | | ip\_policies.id | string | a resource identifier | | ip\_policies.uri | string | a uri for locating a resource | ### ip\_restriction\_updated.v0 Triggers when an IP Restriction is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this IP restriction | | uri | string | URI of the IP restriction API resource | | created\_at | string | timestamp when the IP restriction was created, RFC 3339 format | | description | string | human-readable description of this IP restriction. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this IP restriction. optional, max 4096 bytes. | | enforced | boolean | true if the IP restriction will be enforced. if false, only warnings will be issued | | type | string | the type of IP restriction. this defines what traffic will be restricted with the attached policies. four values are currently supported: `dashboard`, `api`, `agent`, and `endpoints` | | ip\_policies.id | string | a resource identifier | | ip\_policies.uri | string | a uri for locating a resource | ## ReservedAddr ### tcp\_address\_created.v0 Triggers when a TCP address is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique reserved address resource identifier | | uri | string | URI of the reserved address API resource | | created\_at | string | timestamp when the reserved address was created, RFC 3339 format | | description | string | human-readable description of what this reserved address will be used for | | metadata | string | arbitrary user-defined machine-readable data of this reserved address. Optional, max 4096 bytes. | | addr | string | hostname:port of the reserved address that was assigned at creation time | | region | string | reserve the address in this geographic ngrok datacenter. Optional, default is us. (au, eu, ap, us, jp, in, sa) | ### tcp\_address\_deleted.v0 Triggers when a TCP address is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique reserved address resource identifier | | uri | string | URI of the reserved address API resource | | created\_at | string | timestamp when the reserved address was created, RFC 3339 format | | description | string | human-readable description of what this reserved address will be used for | | metadata | string | arbitrary user-defined machine-readable data of this reserved address. Optional, max 4096 bytes. | | addr | string | hostname:port of the reserved address that was assigned at creation time | | region | string | reserve the address in this geographic ngrok datacenter. Optional, default is us. (au, eu, ap, us, jp, in, sa) | ### tcp\_address\_updated.v0 Triggers when a TCP address is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique reserved address resource identifier | | uri | string | URI of the reserved address API resource | | created\_at | string | timestamp when the reserved address was created, RFC 3339 format | | description | string | human-readable description of what this reserved address will be used for | | metadata | string | arbitrary user-defined machine-readable data of this reserved address. Optional, max 4096 bytes. | | addr | string | hostname:port of the reserved address that was assigned at creation time | | region | string | reserve the address in this geographic ngrok datacenter. Optional, default is us. (au, eu, ap, us, jp, in, sa) | ## ReservedDomain ### domain\_created.v0 Triggers when a domain is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique reserved domain resource identifier | | uri | string | URI of the reserved domain API resource | | created\_at | string | timestamp when the reserved domain was created, RFC 3339 format | | description | string | human-readable description of what this reserved domain will be used for | | metadata | string | arbitrary user-defined machine-readable data of this reserved domain. Optional, max 4096 bytes. | | domain | string | hostname of the reserved domain | | region | string | deprecated: With the launch of the ngrok Global Network domains traffic is now handled globally. This field applied only to endpoints. Note that agents may still connect to specific regions. Optional, null by default. (au, eu, ap, us, jp, in, sa) | | cname\_target | string | DNS CNAME target for a custom hostname, or null if the reserved domain is a subdomain of an ngrok owned domain (e.g. \*.ngrok.app) | | certificate.id | string | a resource identifier | | certificate.uri | string | a uri for locating a resource | | certificate\_management\_policy.authority | string | certificate authority to request certificates from. The only supported value is letsencrypt. | | certificate\_management\_policy.private\_key\_type | string | type of private key to use when requesting certificates. Defaults to ecdsa, can be either rsa or ecdsa. | | certificate\_management\_status.renews\_at | string | timestamp when the next renewal will be requested, RFC 3339 format | | certificate\_management\_status.provisioning\_job.error\_code | string | if present, an error code indicating why provisioning is failing. It may be either a temporary condition (INTERNAL\_ERROR), or a permanent one the user must correct (DNS\_ERROR). | | certificate\_management\_status.provisioning\_job.msg | string | a message describing the current status or error | | certificate\_management\_status.provisioning\_job.started\_at | string | timestamp when the provisioning job started, RFC 3339 format | | certificate\_management\_status.provisioning\_job.retries\_at | string | timestamp when the provisioning job will be retried | | acme\_challenge\_cname\_target | string | DNS CNAME target for the host \_acme-challenge.example.com, where example.com is your reserved domain name. This is required to issue certificates for wildcard, non-ngrok reserved domains. Must be null for non-wildcard domains and ngrok subdomains. | | resolves\_to.value | string | accepts an ngrok point-of-presence shortcode, or "global" | ### domain\_deleted.v0 Triggers when a domain is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique reserved domain resource identifier | | uri | string | URI of the reserved domain API resource | | created\_at | string | timestamp when the reserved domain was created, RFC 3339 format | | description | string | human-readable description of what this reserved domain will be used for | | metadata | string | arbitrary user-defined machine-readable data of this reserved domain. Optional, max 4096 bytes. | | domain | string | hostname of the reserved domain | | region | string | deprecated: With the launch of the ngrok Global Network domains traffic is now handled globally. This field applied only to endpoints. Note that agents may still connect to specific regions. Optional, null by default. (au, eu, ap, us, jp, in, sa) | | cname\_target | string | DNS CNAME target for a custom hostname, or null if the reserved domain is a subdomain of an ngrok owned domain (e.g. \*.ngrok.app) | | certificate.id | string | a resource identifier | | certificate.uri | string | a uri for locating a resource | | certificate\_management\_policy.authority | string | certificate authority to request certificates from. The only supported value is letsencrypt. | | certificate\_management\_policy.private\_key\_type | string | type of private key to use when requesting certificates. Defaults to ecdsa, can be either rsa or ecdsa. | | certificate\_management\_status.renews\_at | string | timestamp when the next renewal will be requested, RFC 3339 format | | certificate\_management\_status.provisioning\_job.error\_code | string | if present, an error code indicating why provisioning is failing. It may be either a temporary condition (INTERNAL\_ERROR), or a permanent one the user must correct (DNS\_ERROR). | | certificate\_management\_status.provisioning\_job.msg | string | a message describing the current status or error | | certificate\_management\_status.provisioning\_job.started\_at | string | timestamp when the provisioning job started, RFC 3339 format | | certificate\_management\_status.provisioning\_job.retries\_at | string | timestamp when the provisioning job will be retried | | acme\_challenge\_cname\_target | string | DNS CNAME target for the host \_acme-challenge.example.com, where example.com is your reserved domain name. This is required to issue certificates for wildcard, non-ngrok reserved domains. Must be null for non-wildcard domains and ngrok subdomains. | | resolves\_to.value | string | accepts an ngrok point-of-presence shortcode, or "global" | ### domain\_updated.v0 Triggers when a domain is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique reserved domain resource identifier | | uri | string | URI of the reserved domain API resource | | created\_at | string | timestamp when the reserved domain was created, RFC 3339 format | | description | string | human-readable description of what this reserved domain will be used for | | metadata | string | arbitrary user-defined machine-readable data of this reserved domain. Optional, max 4096 bytes. | | domain | string | hostname of the reserved domain | | region | string | deprecated: With the launch of the ngrok Global Network domains traffic is now handled globally. This field applied only to endpoints. Note that agents may still connect to specific regions. Optional, null by default. (au, eu, ap, us, jp, in, sa) | | cname\_target | string | DNS CNAME target for a custom hostname, or null if the reserved domain is a subdomain of an ngrok owned domain (e.g. \*.ngrok.app) | | certificate.id | string | a resource identifier | | certificate.uri | string | a uri for locating a resource | | certificate\_management\_policy.authority | string | certificate authority to request certificates from. The only supported value is letsencrypt. | | certificate\_management\_policy.private\_key\_type | string | type of private key to use when requesting certificates. Defaults to ecdsa, can be either rsa or ecdsa. | | certificate\_management\_status.renews\_at | string | timestamp when the next renewal will be requested, RFC 3339 format | | certificate\_management\_status.provisioning\_job.error\_code | string | if present, an error code indicating why provisioning is failing. It may be either a temporary condition (INTERNAL\_ERROR), or a permanent one the user must correct (DNS\_ERROR). | | certificate\_management\_status.provisioning\_job.msg | string | a message describing the current status or error | | certificate\_management\_status.provisioning\_job.started\_at | string | timestamp when the provisioning job started, RFC 3339 format | | certificate\_management\_status.provisioning\_job.retries\_at | string | timestamp when the provisioning job will be retried | | acme\_challenge\_cname\_target | string | DNS CNAME target for the host \_acme-challenge.example.com, where example.com is your reserved domain name. This is required to issue certificates for wildcard, non-ngrok reserved domains. Must be null for non-wildcard domains and ngrok subdomains. | | resolves\_to.value | string | accepts an ngrok point-of-presence shortcode, or "global" | ## SSHCertificateAuthority ### ssh\_certificate\_authority\_created.v0 Triggers when an SSH certificate authority is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this SSH Certificate Authority | | uri | string | URI of the SSH Certificate Authority API resource | | created\_at | string | timestamp when the SSH Certificate Authority API resource was created, RFC 3339 format | | description | string | human-readable description of this SSH Certificate Authority. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this SSH Certificate Authority. optional, max 4096 bytes. | | public\_key | string | raw public key for this SSH Certificate Authority | | key\_type | string | the type of private key for this SSH Certificate Authority | ### ssh\_certificate\_authority\_deleted.v0 Triggers when an SSH certificate authority is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this SSH Certificate Authority | | uri | string | URI of the SSH Certificate Authority API resource | | created\_at | string | timestamp when the SSH Certificate Authority API resource was created, RFC 3339 format | | description | string | human-readable description of this SSH Certificate Authority. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this SSH Certificate Authority. optional, max 4096 bytes. | | public\_key | string | raw public key for this SSH Certificate Authority | | key\_type | string | the type of private key for this SSH Certificate Authority | ### ssh\_certificate\_authority\_updated.v0 Triggers when an SSH certificate authority is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this SSH Certificate Authority | | uri | string | URI of the SSH Certificate Authority API resource | | created\_at | string | timestamp when the SSH Certificate Authority API resource was created, RFC 3339 format | | description | string | human-readable description of this SSH Certificate Authority. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this SSH Certificate Authority. optional, max 4096 bytes. | | public\_key | string | raw public key for this SSH Certificate Authority | | key\_type | string | the type of private key for this SSH Certificate Authority | ## SSHCredential ### ssh\_public\_key\_created.v0 Triggers when an SSH public key is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique ssh credential resource identifier | | uri | string | URI of the ssh credential API resource | | created\_at | string | timestamp when the ssh credential was created, RFC 3339 format | | description | string | human-readable description of who or what will use the ssh credential to authenticate. Optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this ssh credential. Optional, max 4096 bytes. | | public\_key | string | the PEM-encoded public key of the SSH keypair that will be used to authenticate | | acl | List\ | optional list of ACL rules. If unspecified, the credential will have no restrictions. The only allowed ACL rule at this time is the `bind` rule. The `bind` rule allows the caller to restrict what domains, addresses, and labels the token is allowed to bind. For example, to allow the token to open a tunnel on example.ngrok.io your ACL would include the rule `bind:example.ngrok.io`. Bind rules for domains may specify a leading wildcard to match multiple domains with a common suffix. For example, you may specify a rule of `bind:*.example.com` which will allow `x.example.com`, `y.example.com`, `*.example.com`, etc. Bind rules for labels may specify a wildcard key and/or value to match multiple labels. For example, you may specify a rule of `bind:*=example` which will allow `x=example`, `y=example`, etc. A rule of `'*'` is equivalent to no acl at all and will explicitly permit all actions. | | owner\_id | string | If supplied at credential creation, ownership will be assigned to the specified User or Bot. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Bot. | ### ssh\_public\_key\_deleted.v0 Triggers when an SSH public key is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique ssh credential resource identifier | | uri | string | URI of the ssh credential API resource | | created\_at | string | timestamp when the ssh credential was created, RFC 3339 format | | description | string | human-readable description of who or what will use the ssh credential to authenticate. Optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this ssh credential. Optional, max 4096 bytes. | | public\_key | string | the PEM-encoded public key of the SSH keypair that will be used to authenticate | | acl | List\ | optional list of ACL rules. If unspecified, the credential will have no restrictions. The only allowed ACL rule at this time is the `bind` rule. The `bind` rule allows the caller to restrict what domains, addresses, and labels the token is allowed to bind. For example, to allow the token to open a tunnel on example.ngrok.io your ACL would include the rule `bind:example.ngrok.io`. Bind rules for domains may specify a leading wildcard to match multiple domains with a common suffix. For example, you may specify a rule of `bind:*.example.com` which will allow `x.example.com`, `y.example.com`, `*.example.com`, etc. Bind rules for labels may specify a wildcard key and/or value to match multiple labels. For example, you may specify a rule of `bind:*=example` which will allow `x=example`, `y=example`, etc. A rule of `'*'` is equivalent to no acl at all and will explicitly permit all actions. | | owner\_id | string | If supplied at credential creation, ownership will be assigned to the specified User or Bot. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Bot. | ### ssh\_public\_key\_updated.v0 Triggers when an SSH public key is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique ssh credential resource identifier | | uri | string | URI of the ssh credential API resource | | created\_at | string | timestamp when the ssh credential was created, RFC 3339 format | | description | string | human-readable description of who or what will use the ssh credential to authenticate. Optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this ssh credential. Optional, max 4096 bytes. | | public\_key | string | the PEM-encoded public key of the SSH keypair that will be used to authenticate | | acl | List\ | optional list of ACL rules. If unspecified, the credential will have no restrictions. The only allowed ACL rule at this time is the `bind` rule. The `bind` rule allows the caller to restrict what domains, addresses, and labels the token is allowed to bind. For example, to allow the token to open a tunnel on example.ngrok.io your ACL would include the rule `bind:example.ngrok.io`. Bind rules for domains may specify a leading wildcard to match multiple domains with a common suffix. For example, you may specify a rule of `bind:*.example.com` which will allow `x.example.com`, `y.example.com`, `*.example.com`, etc. Bind rules for labels may specify a wildcard key and/or value to match multiple labels. For example, you may specify a rule of `bind:*=example` which will allow `x=example`, `y=example`, etc. A rule of `'*'` is equivalent to no acl at all and will explicitly permit all actions. | | owner\_id | string | If supplied at credential creation, ownership will be assigned to the specified User or Bot. Only admins may specify an owner other than themselves. Defaults to the authenticated User or Bot. | ## SSHHostCertificate ### ssh\_host\_certificate\_created.v0 Triggers when an SSH host certificate is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this SSH Host Certificate | | uri | string | URI of the SSH Host Certificate API resource | | created\_at | string | timestamp when the SSH Host Certificate API resource was created, RFC 3339 format | | description | string | human-readable description of this SSH Host Certificate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this SSH Host Certificate. optional, max 4096 bytes. | | public\_key | string | a public key in OpenSSH Authorized Keys format that this certificate signs | | key\_type | string | the key type of the `public_key`, one of `rsa`, `ecdsa` or `ed25519` | | ssh\_certificate\_authority\_id | string | the ssh certificate authority that is used to sign this ssh host certificate | | principals | List\ | the list of principals included in the ssh host certificate. This is the list of hostnames and/or IP addresses that are authorized to serve SSH traffic with this certificate. Dangerously, if no principals are specified, this certificate is considered valid for all hosts. | | valid\_after | string | the time when the ssh host certificate becomes valid, in RFC 3339 format. | | valid\_until | string | the time after which the ssh host certificate becomes invalid, in RFC 3339 format. the OpenSSH certificates RFC calls this `valid_before`. | | certificate | string | the signed SSH certificate in OpenSSH Authorized Keys format. this value should be placed in a `-cert.pub` certificate file on disk that should be referenced in your `sshd_config` configuration file with a `HostCertificate` directive | ### ssh\_host\_certificate\_deleted.v0 Triggers when an SSH host certificate is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this SSH Host Certificate | | uri | string | URI of the SSH Host Certificate API resource | | created\_at | string | timestamp when the SSH Host Certificate API resource was created, RFC 3339 format | | description | string | human-readable description of this SSH Host Certificate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this SSH Host Certificate. optional, max 4096 bytes. | | public\_key | string | a public key in OpenSSH Authorized Keys format that this certificate signs | | key\_type | string | the key type of the `public_key`, one of `rsa`, `ecdsa` or `ed25519` | | ssh\_certificate\_authority\_id | string | the ssh certificate authority that is used to sign this ssh host certificate | | principals | List\ | the list of principals included in the ssh host certificate. This is the list of hostnames and/or IP addresses that are authorized to serve SSH traffic with this certificate. Dangerously, if no principals are specified, this certificate is considered valid for all hosts. | | valid\_after | string | the time when the ssh host certificate becomes valid, in RFC 3339 format. | | valid\_until | string | the time after which the ssh host certificate becomes invalid, in RFC 3339 format. the OpenSSH certificates RFC calls this `valid_before`. | | certificate | string | the signed SSH certificate in OpenSSH Authorized Keys format. this value should be placed in a `-cert.pub` certificate file on disk that should be referenced in your `sshd_config` configuration file with a `HostCertificate` directive | ### ssh\_host\_certificate\_updated.v0 Triggers when an SSH host certificate is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this SSH Host Certificate | | uri | string | URI of the SSH Host Certificate API resource | | created\_at | string | timestamp when the SSH Host Certificate API resource was created, RFC 3339 format | | description | string | human-readable description of this SSH Host Certificate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this SSH Host Certificate. optional, max 4096 bytes. | | public\_key | string | a public key in OpenSSH Authorized Keys format that this certificate signs | | key\_type | string | the key type of the `public_key`, one of `rsa`, `ecdsa` or `ed25519` | | ssh\_certificate\_authority\_id | string | the ssh certificate authority that is used to sign this ssh host certificate | | principals | List\ | the list of principals included in the ssh host certificate. This is the list of hostnames and/or IP addresses that are authorized to serve SSH traffic with this certificate. Dangerously, if no principals are specified, this certificate is considered valid for all hosts. | | valid\_after | string | the time when the ssh host certificate becomes valid, in RFC 3339 format. | | valid\_until | string | the time after which the ssh host certificate becomes invalid, in RFC 3339 format. the OpenSSH certificates RFC calls this `valid_before`. | | certificate | string | the signed SSH certificate in OpenSSH Authorized Keys format. this value should be placed in a `-cert.pub` certificate file on disk that should be referenced in your `sshd_config` configuration file with a `HostCertificate` directive | ## SSHUserCertificate ### ssh\_user\_certificate\_created.v0 Triggers when an SSH user certificate is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this SSH User Certificate | | uri | string | URI of the SSH User Certificate API resource | | created\_at | string | timestamp when the SSH User Certificate API resource was created, RFC 3339 format | | description | string | human-readable description of this SSH User Certificate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this SSH User Certificate. optional, max 4096 bytes. | | public\_key | string | a public key in OpenSSH Authorized Keys format that this certificate signs | | key\_type | string | the key type of the `public_key`, one of `rsa`, `ecdsa` or `ed25519` | | ssh\_certificate\_authority\_id | string | the ssh certificate authority that is used to sign this ssh user certificate | | principals | List\ | the list of principals included in the ssh user certificate. This is the list of usernames that the certificate holder may sign in as on a machine authorizing the signing certificate authority. Dangerously, if no principals are specified, this certificate may be used to log in as any user. | | critical\_options | Map\ | A map of critical options included in the certificate. Only two critical options are currently defined by OpenSSH: `force-command` and `source-address`. See [the OpenSSH certificate protocol spec](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys) for additional details. | | extensions | Map\ | A map of extensions included in the certificate. Extensions are additional metadata that can be interpreted by the SSH server for any purpose. These can be used to permit or deny the ability to open a terminal, do port forwarding, x11 forwarding, and more. If unspecified, the certificate will include limited permissions with the following extension map: `{"permit-pty": "", "permit-user-rc": ""}` OpenSSH understands a number of predefined extensions. See [the OpenSSH certificate protocol spec](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys) for additional details. | | valid\_after | string | the time when the ssh host certificate becomes valid, in RFC 3339 format. | | valid\_until | string | the time after which the ssh host certificate becomes invalid, in RFC 3339 format. the OpenSSH certificates RFC calls this `valid_before`. | | certificate | string | the signed SSH certificate in OpenSSH Authorized Keys Format. this value should be placed in a `-cert.pub` certificate file on disk that should be referenced in your `sshd_config` configuration file with a `HostCertificate` directive | ### ssh\_user\_certificate\_deleted.v0 Triggers when an SSH user certificate is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this SSH User Certificate | | uri | string | URI of the SSH User Certificate API resource | | created\_at | string | timestamp when the SSH User Certificate API resource was created, RFC 3339 format | | description | string | human-readable description of this SSH User Certificate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this SSH User Certificate. optional, max 4096 bytes. | | public\_key | string | a public key in OpenSSH Authorized Keys format that this certificate signs | | key\_type | string | the key type of the `public_key`, one of `rsa`, `ecdsa` or `ed25519` | | ssh\_certificate\_authority\_id | string | the ssh certificate authority that is used to sign this ssh user certificate | | principals | List\ | the list of principals included in the ssh user certificate. This is the list of usernames that the certificate holder may sign in as on a machine authorizing the signing certificate authority. Dangerously, if no principals are specified, this certificate may be used to log in as any user. | | critical\_options | Map\ | A map of critical options included in the certificate. Only two critical options are currently defined by OpenSSH: `force-command` and `source-address`. See [the OpenSSH certificate protocol spec](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys) for additional details. | | extensions | Map\ | A map of extensions included in the certificate. Extensions are additional metadata that can be interpreted by the SSH server for any purpose. These can be used to permit or deny the ability to open a terminal, do port forwarding, x11 forwarding, and more. If unspecified, the certificate will include limited permissions with the following extension map: `{"permit-pty": "", "permit-user-rc": ""}` OpenSSH understands a number of predefined extensions. See [the OpenSSH certificate protocol spec](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys) for additional details. | | valid\_after | string | the time when the ssh host certificate becomes valid, in RFC 3339 format. | | valid\_until | string | the time after which the ssh host certificate becomes invalid, in RFC 3339 format. the OpenSSH certificates RFC calls this `valid_before`. | | certificate | string | the signed SSH certificate in OpenSSH Authorized Keys Format. this value should be placed in a `-cert.pub` certificate file on disk that should be referenced in your `sshd_config` configuration file with a `HostCertificate` directive | ### ssh\_user\_certificate\_updated.v0 Triggers when an SSH user certificate is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this SSH User Certificate | | uri | string | URI of the SSH User Certificate API resource | | created\_at | string | timestamp when the SSH User Certificate API resource was created, RFC 3339 format | | description | string | human-readable description of this SSH User Certificate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this SSH User Certificate. optional, max 4096 bytes. | | public\_key | string | a public key in OpenSSH Authorized Keys format that this certificate signs | | key\_type | string | the key type of the `public_key`, one of `rsa`, `ecdsa` or `ed25519` | | ssh\_certificate\_authority\_id | string | the ssh certificate authority that is used to sign this ssh user certificate | | principals | List\ | the list of principals included in the ssh user certificate. This is the list of usernames that the certificate holder may sign in as on a machine authorizing the signing certificate authority. Dangerously, if no principals are specified, this certificate may be used to log in as any user. | | critical\_options | Map\ | A map of critical options included in the certificate. Only two critical options are currently defined by OpenSSH: `force-command` and `source-address`. See [the OpenSSH certificate protocol spec](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys) for additional details. | | extensions | Map\ | A map of extensions included in the certificate. Extensions are additional metadata that can be interpreted by the SSH server for any purpose. These can be used to permit or deny the ability to open a terminal, do port forwarding, x11 forwarding, and more. If unspecified, the certificate will include limited permissions with the following extension map: `{"permit-pty": "", "permit-user-rc": ""}` OpenSSH understands a number of predefined extensions. See [the OpenSSH certificate protocol spec](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys) for additional details. | | valid\_after | string | the time when the ssh host certificate becomes valid, in RFC 3339 format. | | valid\_until | string | the time after which the ssh host certificate becomes invalid, in RFC 3339 format. the OpenSSH certificates RFC calls this `valid_before`. | | certificate | string | the signed SSH certificate in OpenSSH Authorized Keys Format. this value should be placed in a `-cert.pub` certificate file on disk that should be referenced in your `sshd_config` configuration file with a `HostCertificate` directive | ## Secret ### secret\_created.v0 Triggers when a secret is created. This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | identifier for Secret | | uri | string | URI of this Secret API resource | | created\_at | string | Timestamp when the Secret was created (RFC 3339 format) | | updated\_at | string | Timestamp when the Secret was last updated (RFC 3339 format) | | name | string | Name of secret | | description | string | description of Secret | | metadata | string | Arbitrary user-defined metadata for this Secret | | created\_by.id | string | a resource identifier | | created\_by.uri | string | a uri for locating a resource | | last\_updated\_by.id | string | a resource identifier | | last\_updated\_by.uri | string | a uri for locating a resource | | vault.id | string | a resource identifier | | vault.uri | string | a uri for locating a resource | | vault\_name | string | Name of the vault the secret is stored in | ### secret\_deleted.v0 Triggers when a secret is deleted. This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | identifier for Secret | | uri | string | URI of this Secret API resource | | created\_at | string | Timestamp when the Secret was created (RFC 3339 format) | | updated\_at | string | Timestamp when the Secret was last updated (RFC 3339 format) | | name | string | Name of secret | | description | string | description of Secret | | metadata | string | Arbitrary user-defined metadata for this Secret | | created\_by.id | string | a resource identifier | | created\_by.uri | string | a uri for locating a resource | | last\_updated\_by.id | string | a resource identifier | | last\_updated\_by.uri | string | a uri for locating a resource | | vault.id | string | a resource identifier | | vault.uri | string | a uri for locating a resource | | vault\_name | string | Name of the vault the secret is stored in | ### secret\_updated.v0 Triggers when a secret is updated. This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | identifier for Secret | | uri | string | URI of this Secret API resource | | created\_at | string | Timestamp when the Secret was created (RFC 3339 format) | | updated\_at | string | Timestamp when the Secret was last updated (RFC 3339 format) | | name | string | Name of secret | | description | string | description of Secret | | metadata | string | Arbitrary user-defined metadata for this Secret | | created\_by.id | string | a resource identifier | | created\_by.uri | string | a uri for locating a resource | | last\_updated\_by.id | string | a resource identifier | | last\_updated\_by.uri | string | a uri for locating a resource | | vault.id | string | a resource identifier | | vault.uri | string | a uri for locating a resource | | vault\_name | string | Name of the vault the secret is stored in | ## TCP Connection Closed ### tcp\_connection\_closed.v0 Triggers when a TCP connection to an endpoint closes. This event type supports filters and selectable fields. | Name | Type | Description | Filterable? | | - | - | - | - | | conn.bytes\_in | int64 | The number of bytes arriving at an endpoint from the frontend | | | conn.bytes\_out | int64 | The number of bytes leaving an endpoint to the frontend | | | conn.client\_ip | string | The source IP of the TCP connection to the ngrok edge | Yes | | conn.end\_ts | timestamp | The timestamp when the TCP connection to the ngrok edge is closed | | | conn.server\_ip | string | The IP address of the server that received the request | Yes | | conn.server\_name | string | The hostname associated with this connection. | Yes | | conn.server\_port | int32 | The port that the connection for this request came in on | Yes | | conn.start\_ts | timestamp | The timestamp when the TCP connection to the ngrok edge is established | | | ip\_policy.decision | string | 'allow' if IP Policy module permitted the request to the upstream service, 'block' otherwise | | | ja4\_fingerprint | string | The JA4 fingerprint of the client TLS handshake | | | traffic\_policy.logs | List\> | Logs collected from the log action in a Traffic Policy | | ## TLSCertificate ### tls\_certificate\_created.v0 Triggers when a TLS certificate is created This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this TLS certificate | | uri | string | URI of the TLS certificate API resource | | created\_at | string | timestamp when the TLS certificate was created, RFC 3339 format | | description | string | human-readable description of this TLS certificate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this TLS certificate. optional, max 4096 bytes. | | certificate\_pem | string | chain of PEM-encoded certificates, leaf first. See [Certificate Bundles](https://ngrok.com/docs/cloud-edge/endpoints#certificate-chains). | | subject\_common\_name | string | subject common name from the leaf of this TLS certificate | | subject\_alternative\_names.dns\_names | List\ | set of additional domains (including wildcards) this TLS certificate is valid for | | subject\_alternative\_names.ips | List\ | set of IP addresses this TLS certificate is also valid for | | issued\_at | string | timestamp (in RFC 3339 format) when this TLS certificate was issued automatically, or null if this certificate was user-uploaded | | not\_before | string | timestamp when this TLS certificate becomes valid, RFC 3339 format | | not\_after | string | timestamp when this TLS certificate becomes invalid, RFC 3339 format | | key\_usages | List\ | set of actions the private key of this TLS certificate can be used for | | extended\_key\_usages | List\ | extended set of actions the private key of this TLS certificate can be used for | | private\_key\_type | string | type of the private key of this TLS certificate. One of rsa, ecdsa, or ed25519. | | issuer\_common\_name | string | issuer common name from the leaf of this TLS certificate | | serial\_number | string | serial number of the leaf of this TLS certificate | | subject\_organization | string | subject organization from the leaf of this TLS certificate | | subject\_organizational\_unit | string | subject organizational unit from the leaf of this TLS certificate | | subject\_locality | string | subject locality from the leaf of this TLS certificate | | subject\_province | string | subject province from the leaf of this TLS certificate | | subject\_country | string | subject country from the leaf of this TLS certificate | ### tls\_certificate\_deleted.v0 Triggers when a TLS certificate is deleted This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this TLS certificate | | uri | string | URI of the TLS certificate API resource | | created\_at | string | timestamp when the TLS certificate was created, RFC 3339 format | | description | string | human-readable description of this TLS certificate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this TLS certificate. optional, max 4096 bytes. | | certificate\_pem | string | chain of PEM-encoded certificates, leaf first. See [Certificate Bundles](https://ngrok.com/docs/cloud-edge/endpoints#certificate-chains). | | subject\_common\_name | string | subject common name from the leaf of this TLS certificate | | subject\_alternative\_names.dns\_names | List\ | set of additional domains (including wildcards) this TLS certificate is valid for | | subject\_alternative\_names.ips | List\ | set of IP addresses this TLS certificate is also valid for | | issued\_at | string | timestamp (in RFC 3339 format) when this TLS certificate was issued automatically, or null if this certificate was user-uploaded | | not\_before | string | timestamp when this TLS certificate becomes valid, RFC 3339 format | | not\_after | string | timestamp when this TLS certificate becomes invalid, RFC 3339 format | | key\_usages | List\ | set of actions the private key of this TLS certificate can be used for | | extended\_key\_usages | List\ | extended set of actions the private key of this TLS certificate can be used for | | private\_key\_type | string | type of the private key of this TLS certificate. One of rsa, ecdsa, or ed25519. | | issuer\_common\_name | string | issuer common name from the leaf of this TLS certificate | | serial\_number | string | serial number of the leaf of this TLS certificate | | subject\_organization | string | subject organization from the leaf of this TLS certificate | | subject\_organizational\_unit | string | subject organizational unit from the leaf of this TLS certificate | | subject\_locality | string | subject locality from the leaf of this TLS certificate | | subject\_province | string | subject province from the leaf of this TLS certificate | | subject\_country | string | subject country from the leaf of this TLS certificate | ### tls\_certificate\_updated.v0 Triggers when a TLS certificate is updated This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | unique identifier for this TLS certificate | | uri | string | URI of the TLS certificate API resource | | created\_at | string | timestamp when the TLS certificate was created, RFC 3339 format | | description | string | human-readable description of this TLS certificate. optional, max 255 bytes. | | metadata | string | arbitrary user-defined machine-readable data of this TLS certificate. optional, max 4096 bytes. | | certificate\_pem | string | chain of PEM-encoded certificates, leaf first. See [Certificate Bundles](https://ngrok.com/docs/cloud-edge/endpoints#certificate-chains). | | subject\_common\_name | string | subject common name from the leaf of this TLS certificate | | subject\_alternative\_names.dns\_names | List\ | set of additional domains (including wildcards) this TLS certificate is valid for | | subject\_alternative\_names.ips | List\ | set of IP addresses this TLS certificate is also valid for | | issued\_at | string | timestamp (in RFC 3339 format) when this TLS certificate was issued automatically, or null if this certificate was user-uploaded | | not\_before | string | timestamp when this TLS certificate becomes valid, RFC 3339 format | | not\_after | string | timestamp when this TLS certificate becomes invalid, RFC 3339 format | | key\_usages | List\ | set of actions the private key of this TLS certificate can be used for | | extended\_key\_usages | List\ | extended set of actions the private key of this TLS certificate can be used for | | private\_key\_type | string | type of the private key of this TLS certificate. One of rsa, ecdsa, or ed25519. | | issuer\_common\_name | string | issuer common name from the leaf of this TLS certificate | | serial\_number | string | serial number of the leaf of this TLS certificate | | subject\_organization | string | subject organization from the leaf of this TLS certificate | | subject\_organizational\_unit | string | subject organizational unit from the leaf of this TLS certificate | | subject\_locality | string | subject locality from the leaf of this TLS certificate | | subject\_province | string | subject province from the leaf of this TLS certificate | | subject\_country | string | subject country from the leaf of this TLS certificate | ## Vault ### vault\_created.v0 Triggers when a vault is created. This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | identifier for Vault | | uri | string | URI of this Vault API resource | | created\_at | string | Timestamp when the Vault was created (RFC 3339 format) | | updated\_at | string | Timestamp when the Vault was last updated (RFC 3339 format) | | name | string | Name of vault | | description | string | description of Vault | | metadata | string | Arbitrary user-defined metadata for this Vault | | created\_by | string | Reference to who created this Vault | | last\_updated\_by | string | Reference to who created this Vault | ### vault\_deleted.v0 Triggers when a vault is deleted. This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | identifier for Vault | | uri | string | URI of this Vault API resource | | created\_at | string | Timestamp when the Vault was created (RFC 3339 format) | | updated\_at | string | Timestamp when the Vault was last updated (RFC 3339 format) | | name | string | Name of vault | | description | string | description of Vault | | metadata | string | Arbitrary user-defined metadata for this Vault | | created\_by | string | Reference to who created this Vault | | last\_updated\_by | string | Reference to who created this Vault | ### vault\_updated.v0 Triggers when a vault is updated. This event type does not support filters or selectable fields. | Name | Type | Description | | - | - | - | | id | string | identifier for Vault | | uri | string | URI of this Vault API resource | | created\_at | string | Timestamp when the Vault was created (RFC 3339 format) | | updated\_at | string | Timestamp when the Vault was last updated (RFC 3339 format) | | name | string | Name of vault | | description | string | description of Vault | | metadata | string | Arbitrary user-defined metadata for this Vault | | created\_by | string | Reference to who created this Vault | | last\_updated\_by | string | Reference to who created this Vault | # Traffic Observability Overview Source: https://ngrok.com/docs/obs/index Learn about ngrok's traffic observability features for inspecting requests, debugging issues, and exporting events to external logging systems. Troubleshoot and monitor traffic flowing through your endpoints to understand what was sent, received, and how it behaved. Inspect requests and responses in real time with Traffic Inspector, or export traffic events to your own tools for retention, analysis, and alerting. ## Features * [Traffic Inspector](/docs/obs/traffic-inspection/) - Real-time view of HTTP traffic with request replay capabilities. * [Event exporting](/docs/obs/events/) - Subscribe to log sources and publish events to external destinations. ## Use cases Use Traffic Inspector to verify that load balancing works correctly by checking which agent IPs respond to requests. View and replay requests with Traffic Inspector, then export logs to Datadog for monitoring dashboards and alerts. Use the log action in Traffic Policy to capture custom metadata for unsuccessful requests and export to your logging system. Add structured logging to your traffic policies with custom metadata fields using CEL expressions for dynamic values. ## What's next? * Get started with [Traffic Inspector](/docs/obs/traffic-inspection/) to view and debug your traffic in real-time. * Learn how to [export events](/docs/obs/events/) to external logging systems. * Browse the [event reference](/docs/obs/events/reference/) to see all available log sources. # Traffic Inspector Source: https://ngrok.com/docs/obs/traffic-inspection Learn how to use ngrok's Traffic Inspector to view, debug, and replay HTTP traffic flowing through your endpoints in real-time. ## Overview One of the main advantages of using ngrok is the ability to view the traffic going to your origin service. This can help in triaging tough bugs or testing new capabilities. ngrok offers multiple ways to inspect the traffic going to your service. | | | | - | - | | [ngrok Agent Local Traffic Inspector](/docs/gateway/agent/web-inspection-interface/) | This is available when working with the standalone agent and is usually found at [http://localhost:4040](http://localhost:4040). Look for a link in your ngrok agent console. | | [ngrok Traffic Inspector](#ngrok-traffic-inspector) | A cloud based version of the local Traffic Inspector that is available in your [ngrok Dashboard](https://dashboard.ngrok.com/traffic-inspector). See below for more details. | ## ngrok Traffic Inspector The ngrok Traffic Inspector replaces the local agent inspection interface and adds additional capabilities such as longer retention and searching. By enabling full capture mode in your ngrok settings, you are able to replay requests to your upstream directly from the browser, instead of the original source. This can dramatically speed up development of webhooks or callbacks that are difficult to trigger repeatedly. Free accounts get 24 hours of traffic retention. Hobbyist and Pay-as-you-go get 72 hours. After the retention period, traffic events are purged from the system. Pay-as-you-go customers can [purchase an add-on](https://ngrok.com/pricing) for longer retention (up to 90 days). ## Full capture mode By default, the ngrok Traffic Inspector will only store the metadata about each request and response. Full Capture mode is an opt-in setting on your ngrok account that will store the full request and response parameters, headers, and bodies for each traffic event that flows through your endpoints. This can be enabled by account admins by visiting the Observability section in the [ngrok Settings Page](https://dashboard.ngrok.com/settings#observability) after logging in. Storing the full request and response data may mean you and your team members have access to sensitive data flowing through your endpoints. Once full capture mode is enabled, ngrok will begin capturing and storing the full requests and responses up to a maximum of 10KB, after which the data will be truncated. Header values will be truncated after 1KB for each value. Data that is truncated will be indicated in the Traffic Inspector through the number of bytes that were captured as well as the number of headers. This data is stored for your account’s retention period (24 hours for Free, 72 hours for Hobbyist and Pay-as-you-go) from the time a request is received. ## Replay Replaying requests allows users to re-send an HTTP request upstream without directly accessing the endpoint. To use replay, you must activate Full Capture mode in your account. Once Full Capture is enabled, a replay button will appear for fully captured requests in the traffic event details pane of the dashboard. If this button is unavailable, it likely indicates that the request occurred before Full Capture was fully activated, or the request was too large and has been truncated. Replay works by resending your original unmodified request to your endpoint. It will be subject to the policies of that endpoint at the time of replay (which could be different than the original request). When replaying a request to your service, minimal changes are made. However, some alterations are inevitable. Here's a list of changes made to replayed requests: * The replayed request will originate from a different IP address, so any IP restrictions may prevent the replay from reaching your upstream. * Any `Connection: Keep-Alive` headers are removed as replay does not support them. * Some request bodies might not be captured, even if they were replayed successfully. This is particularly true for traffic events with an error status code. * A custom ngrok header `Ngrok-Replay-Original-Request-ID` is added that contains the ID of the original request. This allows you to trace back to the original request if needed. * If the request has been truncated, it cannot be replayed. * The request cannot be replayed if the HTTP method is unknown or undefined. * The `X-forwarded` header values are substituted with accurate values for the replay, not the original request. ## Replay with modifications ngrok Traffic Inspector allows you to edit the request before re-sending it to your backend. It can be accessed from the same drop down as Replay. The same changes are made to the replayed requests as before in addition to the following changes: * You are not allowed to modify any [forbidden headers](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name) in the replayed request. * The request can be replayed to the same endpoint as long as it is in the same account. You cannot replay to endpoints that are active in other accounts. * Modifying the trailer of the request is not supported. If you need that capability, please share feedback in the [ngrok Community Repo](https://github.com/ngrok/ngrok). # Free Plan Limits Source: https://ngrok.com/docs/pricing-limits/free-plan-limits Learn about the limits and restrictions enforced on the free ngrok for developers plan including endpoint limits and usage quotas. Learn about the limits and resources included with the free ngrok for developers plan. Free endpoints have no timeout—they can stay online indefinitely. To keep one running unattended, run it as a [background service](/docs/gateway/agent/service#running-ngrok-in-the-background). See [Pricing and Limits](https://ngrok.com/pricing?ref=docs) to learn how to increase or remove these limits. ## Plan limits and resources The free plan groups its limits into three kinds: **monthly usage quotas** that reset each billing cycle, **rate limits** that cap sustained throughput at any moment, and **concurrency and account limits** that apply at all times. **Monthly usage quotas:** these reset at the start of each monthly billing cycle. | Resource | Limit on Free | | - | - | | Data transfer out | 1 GB / month | | HTTP requests | 20,000 / month | | TCP connections | 5,000 / month | | Logs & events | 10,000 / month | | Webhook verifications | 500 / month | | Traffic identities (OAuth/OIDC MAU) | 3 / month | **Rate limits:** the maximum sustained throughput at any moment, separate from the monthly quotas above. | Resource | Limit on Free | | - | - | | HTTP request rate | 4,000 / min | | TCP connection rate | 100 / min | **Concurrency and account limits:** these apply at all times, not per month. | Resource | Limit on Free | | - | - | | Online endpoints | Up to 3 | | Concurrent agents | 3 | | Users | 1 | | Development domains | 1 | | Traffic policy rules (per policy) | 5 | | TLS endpoints | Not available | Free plans include HTTPS endpoints with automatic TLS certificates and encryption. The **TLS endpoints** row above refers to [raw TLS endpoints](/docs/gateway/endpoints/tls/) that you terminate yourself, which require a paid plan. Features included for free on all plans: * HTTPS Tunnels * Web Inspection Agent * Replay Requests * ngrok SDKs * ngrok Kubernetes Operator * Remote Agent Management * Circuit Breaking * Automatic Certificates and Encryption * Email Support You can check your current usage against these limits [in the Usage page in the dashboard](https://dashboard.ngrok.com/usage). ## Domains The free plan includes one automatically assigned **dev domain** (for example, `your-assigned-name.ngrok-free.app`) tied to your account. You can point up to 3 online endpoints at it: ```bash theme={null} ngrok http 8080 ``` The dev domain is the only domain available on the free plan. Upgrading to a [paid plan](https://ngrok.com/pricing?ref=docs-domain-limits) unlocks: * **Custom domain names** for example, `--url https://my-custom-name.ngrok.app` * **Reserved static domains** you control * **Randomly generated URLs** for example, `--url 'https://'` * **Your own domains** for example, `app.yourdomain.com` * **Wildcard domains** ## Increasing your limits If you hit a limit or run out of credit, [upgrade to a plan with higher limits](https://ngrok.com/pricing?ref=docs). Choose **Pay-as-you-go** if you want your endpoints to always stay online and available. See [Pricing and Limits](/docs/pricing-limits) for the full plan comparison. ## Removing the interstitial page To deter [phishing attacks](https://en.wikipedia.org/wiki/Phishing), ngrok shows an [interstitial page](https://ngrok.com/blog-post/fighting-abuse-on-the-ngrok-platform) in front of all HTML browser traffic on the free tier. * It tells visitors the site is served by ngrok. * When the visitor clicks **Visit** to continue, a cookie suppresses the interstitial for that domain for 7 days. This does not impact users serving APIs or accessing ngrok endpoints programmatically. You can remove the interstitial by upgrading to [any paid plan](https://ngrok.com/pricing?ref=docs-interstitial-removal) or using one of the following methods. ### Using headers From the client accessing the ngrok endpoint, add a header value of `ngrok-skip-browser-warning` and set it to any value. These requests will bypass the interstitial. ```js theme={null} axios.get(url, { 'headers': { 'ngrok-skip-browser-warning': '1' } }) .then((response => { console.log(response.data); }) .catch((error) => { console.log(error); }); ``` ```js theme={null} const response = await fetch(URL, { headers: { "ngrok-skip-browser-warning": "1", }, // ... }); ``` ```js theme={null} request .get('/endpoint') .set('ngrok-skip-browser-warning', '1') .then(callback); ``` ```js theme={null} $.ajax({ url: '/endpoint', headers: { 'ngrok-skip-browser-warning': '1' }, }).then(callback); ``` ### Using a custom user agent Change your user agent by setting [the `User-Agent` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) to something non-standard to bypass the warning, such as `MyApp/0.0.1`. You can also use a browser extension to customize your browser's user agent value. Here's an [example for Chrome](https://chromewebstore.google.com/detail/requestly-intercept-modif/mdnleldcmiljblolnjhpnblkcekpdkpa?hl=en-US). # Pricing and Limits Source: https://ngrok.com/docs/pricing-limits/index Learn about ngrok pricing, limits, and licensing for paid production plans including feature comparisons and billing information. ## Pricing Learn about the pricing, limits, and licensing model for the paid ngrok for production plans. For information on free plans, see [Free Plan Limits](/docs/pricing-limits/free-plan-limits/). ## Limits and licensing For the most complete limits and pricing information, please see the [ngrok Pricing](https://ngrok.com/pricing) page. | Feature | Free Users | Hobbyist | Pay-as-You-Go | | - | - | - | - | | **Domains** | 1 auto-assigned dev domain (cannot be customized) | 1 dev domain + up to 10 ngrok-branded domains | 1 dev domain + custom domains \$0.01/hr | | **Online Endpoints** | Up to 3 | Up to 3 | Unlimited | | **TCP Addresses** | Random with credit card verification | 1 | 100 (contact ngrok for more) | | **Endpoint Hours** | Dev domain endpoints do not accrue; additional charged against credit | Dev domain endpoints do not accrue; additional charged against credit | Per active endpoint hour | | **HTTP Requests** | 20,000/month | 100,000 included (more with credit) | 100,000 included, then \$1 per 100k (volume discounts) | | **TCP/TLS Connections** | 5,000 connections/month | 5,000 included (more with credit) | 5,000 included, then \$2 per 100k (volume discounts) | | **Data Transfer Out** | 1 GB | 5 GB included (more with credit) | 5 GB included, then \$0.10/GB (volume discounts) | | **HTTP Request Rate Limit** | 4,000/min | 20,000/min | 20,000/min (contact ngrok for higher) | | **TCP Connection Rate Limit** | 100/min | 150/min | 600/min (contact ngrok for higher) | | **Traffic Policy Units (TPUs)** | Charged against credit | Charged against credit | \$.10 per 100k (volume discounts) | | **Concurrent Agents** | 3 | 3 | Unlimited | | **Users** | 1 | 1 | 3 included, then \$5 per user | ## Add-on pricing These features are available as add-ons on the Pay-as-you-go plan: | Feature | Price | | - | - | | **SSO & RBAC** | \$10 per user/month | | **Identity Governance Suite** | \$15 per user/month | | **Custom TLS Certificates** | \$0.27 per active hour | | **Dedicated Agent IPs** | \$900 per month per region | | **Custom Agent Connect URLs** | \$0.068 per endpoint hour | | **Log Exporting** | \$0.25 per 2,000 events | | **Slack/Teams Support** | \$200 per month | | **Dedicated On-Call Support** | Contact sales | Custom TLS certificates are billed once per certificate for any hour in which at least one endpoint using that certificate is active. Wildcard domains covered by the same certificate (for example `foo.acme.com` and `bar.acme.com` under a `*.acme.com` certificate) are not charged separately—see [TLS certificates pricing](/docs/gateway/domains/tls-certificates/#tls-certificates-pricing). You can check your usage [in the ngrok dashboard](https://dashboard.ngrok.com/billing). ### Refreshing limits Your usage refreshes on the first day of each month. ### Endpoint limits Your endpoint limit is the number of endpoints you can have online at the same time. On Free and Hobbyist, you can have up to 3 online endpoints. This limit does not refresh at the end of each calendar month. On the Pay-as-you-go plan, there is no limit on the number of online endpoints. An endpoint that has outgoing traffic during a clock hour is counted as an active endpoint for that hour and charged one endpoint hour. Free and Hobbyist plans allow you to use your included credit to start endpoints. ## Wildcard endpoints Wildcard endpoints allow you to create a single endpoint that receives traffic for all subdomains matching a wildcard endpoint pattern, such as `*.example.com`. For example, if you create the wildcard endpoint `https://*.example.com`, it will receive traffic for `https://foo.example.com`, `https://bar.example.com`, and any other matching subdomains. ### Billing for wildcard endpoints For any wildcard endpoint you create, you will be charged **one endpoint hour** for the wildcard endpoint itself, regardless of how many subdomains match the wildcard pattern or how many internal endpoints the wildcard endpoint forwards traffic to. ### Wildcard Cloud Endpoints forwarding to internal endpoints When you use a wildcard Cloud Endpoint that forwards traffic to multiple internal endpoints using Traffic Policy actions (such as the `forward-internal` action), billing works as follows: * **Endpoint hours**: You are charged one endpoint hour for the wildcard Cloud Endpoint. The internal endpoints that receive forwarded traffic do not incur additional endpoint hour charges, as they are internal-only endpoints. * **Traffic Policy (TP)**: Traffic Policy actions executed on the wildcard Cloud Endpoint are charged according to standard [Traffic Policy Unit (TPU) pricing](/docs/pricing-limits/traffic-policy-unit-pricing/). This includes any actions used to route or forward traffic to internal endpoints. * **Individual endpoints vs. wildcard endpoints**: If you create individual endpoints for specific subdomains (for example, `https://api1.example.com` and `https://api2.example.com`) instead of using a wildcard endpoint, each individual endpoint or wildcard endpoint will be charged separately for endpoint hours. For more information on creating and using wildcard endpoints, see the [wildcard endpoints documentation](/docs/gateway/endpoints/http/#wildcard-endpoints). ## How to see how your account stacks up against your limits [The usage page in the dashboard](https://dashboard.ngrok.com/usage). ## Pay-as-you-go FAQs ### What is the Pay-as-you-go plan? The Pay-as-you-go plan is a flexible pricing model that allows you to pay only for the resources you use, without any upfront commitment. It is ideal for production workloads that require scalability and flexibility. ### How does the Pay-as-you-go plan work? You pay a `$20` per month base fee that includes `$20` in usage covering data transfer, endpoints, connections, and other resources. Usage beyond the included amount is billed monthly based on actual consumption. You can scale your usage up or down as needed. ### Is bandwidth generated by malicious traffic counted towards usage? The ngrok cloud service automatically protects all Endpoints from attacks with its proprietary, out-of-the-box [DDoS Firewall](/docs/gateway/domains/ddos-protection#ddos-firewall). Before an attack is detected, bandwidth that is generated may count towards the usage based portion of your Pay-As-You-Go invoice. ## ngrok's free plan See [Free Plan Limits and Resources](/docs/pricing-limits/free-plan-limits) for more details on the free plan. **Monthly usage quotas:** these reset at the start of each monthly billing cycle. | Resource | Limit on Free | | - | - | | Data transfer out | 1 GB / month | | HTTP requests | 20,000 / month | | TCP connections | 5,000 / month | | Logs & events | 10,000 / month | | Webhook verifications | 500 / month | | Traffic identities (OAuth/OIDC MAU) | 3 / month | **Rate limits:** the maximum sustained throughput at any moment, separate from the monthly quotas above. | Resource | Limit on Free | | - | - | | HTTP request rate | 4,000 / min | | TCP connection rate | 100 / min | **Concurrency and account limits:** these apply at all times, not per month. | Resource | Limit on Free | | - | - | | Online endpoints | Up to 3 | | Concurrent agents | 3 | | Users | 1 | | Development domains | 1 | | Traffic policy rules (per policy) | 5 | | TLS endpoints | Not available | Free plans include HTTPS endpoints with automatic TLS certificates and encryption. The **TLS endpoints** row above refers to [raw TLS endpoints](/docs/gateway/endpoints/tls/) that you terminate yourself, which require a paid plan. Features included for free on all plans: * HTTPS Tunnels * Web Inspection Agent * Replay Requests * ngrok SDKs * ngrok Kubernetes Operator * Remote Agent Management * Circuit Breaking * Automatic Certificates and Encryption * Email Support # Pricing Glossary Source: https://ngrok.com/docs/pricing-limits/pricing-glossary Definitions for every feature and metric that appears in the ngrok pricing docs. Definitions for every feature and metric that appears in the [ngrok pricing docs](/docs/pricing-limits/). ## Online endpoints The number of endpoints you can have online at the same time. [Learn More](/docs/gateway/endpoints/agent-endpoints/) ## Development endpoint hours Public endpoints started with your development domain do not accrue endpoint hours. [Learn More](/docs/pricing-limits/#limits-and-licensing) ## Active endpoint hours An endpoint is active if it has outgoing traffic during the hour. [Learn More](/docs/pricing-limits/#limits-and-licensing) ## Endpoint protocols These are the different protocols available to you as a subscriber of each plan. [Learn More](/docs/gateway/endpoints/agent-endpoints#protocols%2C-binding-and-pooling) ## Endpoint hours The amount of time your endpoints are online. ## Load balancing Load balancing at ngrok is called endpoint pooling. [Learn More](/docs/gateway/endpoints/endpoint-pooling) ## Domains Domains you own registered in the ngrok dashboard. [Learn More](/docs/gateway/domains/) ## Development domain Your development domain is specific to your account, and does not incur usage charges. You can use anything for free on your sandbox domain within your account's limits. [Learn More](/docs/gateway/domains/) ## ngrok-branded domains Use any ngrok-branded domain that you pick from ngrok's pool. [Learn More](/docs/gateway/domains/) ## Traffic Policy Units Traffic Processing Units (TPUs) are ngrok’s usage-based metric for measuring the work your Traffic Policies perform. [Learn More](/docs/pricing-limits/traffic-policy-unit-pricing) ## Bring your own custom domains Use any custom domain name that you already own with ngrok. [Learn More](/docs/gateway/domains/custom-domains/) ## Wildcard Domains You can create an endpoint which will receive traffic for all of the subdomains matching a given wildcard domain like \*.example.com. [Learn More](/docs/gateway/endpoints/http/#wildcard-endpoints) ## TCP Addresses TCP Addresses enable you to create public TCP Endpoints on a fixed address. [Learn More](/docs/gateway/domains/tcp-addresses/) ## Data transfer out The total volume of data transferred outbound from ngrok's network to clients, including traffic forwarded to agents. [Learn More](/docs/pricing-limits/#limits-and-licensing) ## Requests to HTTP/s endpoints The maximum number of HTTP/s requests a client can make to an account's endpoints in a month. [Learn More](/docs/gateway/endpoints/http/) ## Connections to TCP / TLS endpoints The maximum number of TCP/TLS connections a client can make to an account's endpoints in a month. [Learn More](/docs/gateway/endpoints/tcp/) ## HTTP Requests The maximum number of HTTP requests across all endpoints per minute. [Learn More](/docs/pricing-limits/how-ngrok-charges/) ## TCP Connections The maximum number of TCP connections across all endpoints per minute. [Learn More](/docs/pricing-limits/how-ngrok-charges/) ## Bring your own certificates Upload your own TLS certificates if you don't want to use the TLS certificates that ngrok automatically provisions for you. [Learn More](/docs/gateway/endpoints/tls/) ## End to End TLS Terminate TLS at your upstream service or at the ngrok agent to achieve end-to-end encryption. [Learn More](/docs/gateway/domains/tls-termination/) ## Mutual TLS Mutual TLS Authentication (mTLS) is a network security protocol that ensures both the client and server authenticate each other using digital certificates. [Learn More](/docs/gateway/traffic-policy/) ## Traffic Policy Units (TPUs) This is a combination of the actions and variables applied to a request. WAF, mTLS, and more are included in TPUs. [Learn More](/docs/pricing-limits/traffic-policy-unit-pricing/) ## Traffic Identities OAuth/SAML/OIDC. This is calculated by the number of end users that authenticate into your app or service via the Traffic Policy action. [Learn More](/docs/gateway/traffic-policy/) ## Traffic Inspector Retention This is the number of hours ngrok retains your traffic data in Traffic Inspector. [Learn More](/docs/obs/traffic-inspection) ## Traffic Log Exporting Export event logs when traffic transits through your endpoints to S3, Datadog, Azure Logs, CloudWatch Logs + more. [Learn More](/docs/obs/) ## Concurrent Agents The maximum number of ngrok agents that can be simultaneously connected to the ngrok cloud service under a single account. [Learn More](/docs/gateway/agent/) ## Dedicated Agent Connect IPs Get a constant, dedicated IP for your account's agents. [Learn More](/docs/gateway/agent/) ## Custom Agent Connect URLs Customize the URL that the agent connects to. [Learn More](/docs/gateway/agent/) ## Remote Agent Update Operations Run ngrok in the background as a service. [Learn More](/docs/gateway/agent/) ## Users Members of your account that can view or create endpoints. [Learn More](/docs/iam/users/) ## Service Users Service users are accounts for automated systems that programmatically interact with your ngrok accounts either by starting ngrok Agents or making requests to the API. [Learn More](/docs/iam/service-users/) ## SSO/RBAC Federate auth to your Identity Provider (IdP) with SAML or OpenID Connect. [Learn More](/docs/iam/sso/) ## Identity and Access Governance Suite SCIM, Domain Controls, Account-Wide IP Restrictions, Audit Logs. [Learn More](/docs/iam/) ## Authtoken ACLs Authtoken ACLs restrict what endpoints an ngrok agent can create when using that authtoken. [Learn More](/docs/gateway/agent/config/) ## Basic support Email support and best-effort response times. ## Slack and MS Teams Dedicated channel with 24 hour response SLA. ## Dedicated On-Call Committed/Contractual Uptime SLA and Support SLA. ## Region-specific routing Configure your domains to only route traffic through specific geographic regions. ## HIPAA / BAAs Self-serve Pay-as-you-go add-on: enable it from the dashboard billing page and sign the BAA yourself. Usage-based charges are billed at +50%. ## SOC2 Independent verification that your data is secure. ## Security questionnaires If your team requires a security questionnaire, ngrok can prepare it. ## Invoicing If your team requires an invoice for billing purposes, ngrok can send it. # Traffic Policy Unit (TPU) Pricing Source: https://ngrok.com/docs/pricing-limits/traffic-policy-unit-pricing Learn about how ngrok uses Traffic Processing Units (TPUs) to measure and charge for Traffic Policy usage. ## Traffic Policy Units (TPUs) Traffic Processing Units (TPUs) are ngrok's usage-based metric for measuring the work your Traffic Policies perform. Because requests can include any mix of expressions and actions—often applied only to a subset of traffic—charging a flat rate per request or connection would usually result in overcharging. Instead, ngrok calculates TPUs dynamically for each request, based only on the parts of the Traffic Policy that were actually executed. TPUs are sold in bundles of 100,000 for \$.10 and are charged as your policies execute. ## TPU pricing breakdown * [**Actions**](/docs/gateway/traffic-policy/how-it-works#actions): Define what happens when a Traffic Policy rule matches (such as adding headers, denying a request, or forwarding traffic). * [**Variables**](/docs/gateway/traffic-policy/variables/): Metadata about connections or requests that you can use in expressions or actions (such as the client IP or headers). ### Actions | Name | TPU Cost | | - | - | | [add-headers](/docs/gateway/traffic-policy/actions/add-headers/) | Free | | [basic-auth](/docs/gateway/traffic-policy/actions/basic-auth/) | Free | | [circuit-breaker](/docs/gateway/traffic-policy/actions/circuit-breaker/) | 10 TPUs | | [close-connection](/docs/gateway/traffic-policy/actions/close-connection/) | Free | | [compress-response](/docs/gateway/traffic-policy/actions/compress-response/) | 10 TPUs | | [custom-response](/docs/gateway/traffic-policy/actions/custom-response/) | Free | | [deny](/docs/gateway/traffic-policy/actions/deny/) | Free | | [forward-internal](/docs/gateway/traffic-policy/actions/forward-internal/) | Free | | [http-request](/docs/gateway/traffic-policy/actions/http-request/) | 1 TPU | | [jwt-validation](/docs/gateway/traffic-policy/actions/jwt-validation/) | 10 TPUs | | [log](/docs/gateway/traffic-policy/actions/log/) | Free | | [oauth](/docs/gateway/traffic-policy/actions/oauth/) | Free | | [oidc](/docs/gateway/traffic-policy/actions/oidc/) | Free | | [owasp-crs-request](/docs/gateway/traffic-policy/actions/owasp-crs-request/) | 10 TPUs | | [owasp-crs-response](/docs/gateway/traffic-policy/actions/owasp-crs-response/) | 10 TPUs | | [rate-limit](/docs/gateway/traffic-policy/actions/rate-limit/) | 1 TPU | | [redirect](/docs/gateway/traffic-policy/actions/redirect/) | 10 TPUs | | [remove-headers](/docs/gateway/traffic-policy/actions/remove-headers/) | 1 TPU | | [restrict-ips](/docs/gateway/traffic-policy/actions/restrict-ips/) | 10 TPUs | | [set-vars](/docs/gateway/traffic-policy/actions/set-vars/) | Free | | [terminate-tls](/docs/gateway/traffic-policy/actions/terminate-tls/) | 100 TPUs | | [url-rewrite](/docs/gateway/traffic-policy/actions/url-rewrite/) | 10 TPUs | | [verify-webhook](/docs/gateway/traffic-policy/actions/verify-webhook/) | 1 TPU | ### Variables | Name | TPU Cost | | - | - | | [actions.ngrok.add\_headers.headers\_added](/docs/gateway/traffic-policy/variables/action/#add-headers) | Free | | [actions.ngrok.basic\_auth.credentials.presented](/docs/gateway/traffic-policy/variables/action/#basic-auth) | Free | | [actions.ngrok.basic\_auth.credentials.username](/docs/gateway/traffic-policy/variables/action/#basic-auth) | Free | | [actions.ngrok.basic\_auth.credentials.authorized](/docs/gateway/traffic-policy/variables/action/#basic-auth) | Free | | [actions.ngrok.compress.already\_compressed](/docs/gateway/traffic-policy/variables/action/#compress-response) | Free | | [actions.ngrok.compress.negotiated\_algorithm](/docs/gateway/traffic-policy/variables/action/#compress-response) | Free | | [actions.ngrok.http\_request.error.code](/docs/gateway/traffic-policy/variables/action/#http-request) | Free | | [actions.ngrok.http\_request.error.message](/docs/gateway/traffic-policy/variables/action/#http-request) | Free | | [actions.ngrok.http\_request.attempts](/docs/gateway/traffic-policy/variables/action/#http-request) | Free | | [actions.ngrok.http\_request.req](/docs/gateway/traffic-policy/variables/action/#http-request) | Free | | [actions.ngrok.http\_request.res](/docs/gateway/traffic-policy/variables/action/#http-request) | Free | | [actions.ngrok.jwt\_validation.tokens](/docs/gateway/traffic-policy/variables/action/#jwt-validation) | Free | | [actions.ngrok.jwt\_validation.error.code](/docs/gateway/traffic-policy/variables/action/#jwt-validation) | Free | | [actions.ngrok.jwt\_validation.error.message](/docs/gateway/traffic-policy/variables/action/#jwt-validation) | Free | | [actions.ngrok.log.metadata](/docs/gateway/traffic-policy/variables/action/#log) | Free | | [actions.ngrok.oauth.error.code](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.error.message](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.identity.id](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.identity.email](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.identity.name](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.identity.provider\_user\_id](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.identity.current\_provider\_session\_id](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.access\_token](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.refresh\_token](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.expires\_at](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.session\_timed\_out](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oauth.session\_max\_duration\_reached](/docs/gateway/traffic-policy/variables/action/#oauth) | Free | | [actions.ngrok.oidc.error.code](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.error.message](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.identity.id](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.identity.email](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.identity.name](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.identity.provider\_user\_id](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.identity.current\_session\_id](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.identity\_token](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.access\_token](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.refresh\_token](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.expires\_at](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.session\_timed\_out](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.session\_max\_duration\_reached](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.oidc.userinfo\_refreshed](/docs/gateway/traffic-policy/variables/action/#openid-connect-oidc) | Free | | [actions.ngrok.rate\_limit.bucket\_key](/docs/gateway/traffic-policy/variables/action/#rate-limit) | Free | | [actions.ngrok.rate\_limit.limited](/docs/gateway/traffic-policy/variables/action/#rate-limit) | Free | | [actions.ngrok.rate\_limit.error.code](/docs/gateway/traffic-policy/variables/action/#rate-limit) | Free | | [actions.ngrok.rate\_limit.error.message](/docs/gateway/traffic-policy/variables/action/#rate-limit) | Free | | [actions.ngrok.redirect.matches](/docs/gateway/traffic-policy/variables/action/#redirect) | Free | | [actions.ngrok.redirect.url](/docs/gateway/traffic-policy/variables/action/#redirect) | Free | | [actions.ngrok.redirect.error.code](/docs/gateway/traffic-policy/variables/action/#redirect) | Free | | [actions.ngrok.redirect.error.message](/docs/gateway/traffic-policy/variables/action/#redirect) | Free | | [actions.ngrok.remove\_headers.headers\_removed](/docs/gateway/traffic-policy/variables/action/#remove-headers) | Free | | [actions.ngrok.restrict\_ips.action](/docs/gateway/traffic-policy/variables/action/#restrict-ips) | Free | | [actions.ngrok.restrict\_ips.matched\_cidr](/docs/gateway/traffic-policy/variables/action/#restrict-ips) | Free | | [actions.ngrok.restrict\_ips.error.code](/docs/gateway/traffic-policy/variables/action/#restrict-ips) | Free | | [actions.ngrok.restrict\_ips.error.message](/docs/gateway/traffic-policy/variables/action/#restrict-ips) | Free | | [actions.ngrok.url\_rewrite.matches](/docs/gateway/traffic-policy/variables/action/#url-rewrite) | Free | | [actions.ngrok.url\_rewrite.url](/docs/gateway/traffic-policy/variables/action/#url-rewrite) | Free | | [actions.ngrok.url\_rewrite.error.code](/docs/gateway/traffic-policy/variables/action/#url-rewrite) | Free | | [actions.ngrok.url\_rewrite.error.message](/docs/gateway/traffic-policy/variables/action/#url-rewrite) | Free | | [actions.ngrok.verify\_webhook.verified](/docs/gateway/traffic-policy/variables/action/#verify-webhook) | Free | | [conn.bytes\_in](/docs/gateway/traffic-policy/variables/connection/#connbytes-in) | Free | | [conn.bytes\_out](/docs/gateway/traffic-policy/variables/connection/#connbytes-out) | Free | | [conn.client\_ip](/docs/gateway/traffic-policy/variables/connection/#connclient-ip) | Free | | [conn.client\_port](/docs/gateway/traffic-policy/variables/connection/#connclient-port) | Free | | [conn.server\_ip](/docs/gateway/traffic-policy/variables/connection/#connserver-ip) | Free | | [conn.server\_port](/docs/gateway/traffic-policy/variables/connection/#connserver-port) | Free | | [conn.server\_region](/docs/gateway/traffic-policy/variables/connection/#connserver-region) | Free | | [conn.ts.start](/docs/gateway/traffic-policy/variables/connection/#conntsstart) | Free | ## Traffic Policy Unit (TPU) discounts at scale The Pay-as-you-go plan provides discounts on TPUs for large scale deployments so you always get the best price as you grow. Discounted pricing is as follows: | Count | TPU Cost | | - | - | | 0+ | \$.10/100k | | 1M+ | \$.075/100k | | 10M+ | \$.055/100k | | 100M+ | \$0.035/100k | | 1B+ | \$0.025/100k | # Share Local APIs Source: https://ngrok.com/docs/share-localhost/apis Share your local API with teammates or external consumers for testing and collaboration without deploying. When building APIs, you often need to share your work with teammates or partners before deploying. ngrok lets you expose your local API server with a public HTTPS URL so others can interact with it right away. ## How it works 1. [Install ngrok](https://ngrok.com/download) 2. Start your API server locally 3. Run `ngrok http ` to create a public endpoint 4. Share the ngrok URL with anyone who needs to test your API ## Quick example ```bash theme={null} ngrok http 8080 ``` Share the HTTPS forwarding URL with your team. They can make requests to your local API as if it were deployed. ## Collaboration benefits * **Instant sharing:** Share a working API with teammates without pushing to a staging environment. * **Real-time iteration:** Make changes locally and have them immediately reflected for anyone using the URL. * **Frontend-backend collaboration:** Frontend developers can point their app at your local API while you iterate on it. * **External testing:** Share your API with partners or clients for early integration testing. ## Inspect API traffic Use ngrok's [Traffic Inspector](/docs/share-localhost/inspection) to monitor all API requests and responses in real time. This is especially helpful for debugging integration issues when working with external consumers. ## Add authentication Protect your shared API by adding authentication with a [Traffic Policy](/docs/gateway/traffic-policy/). You can require OAuth or other forms of authentication without changing your application code. See the [authentication guide](/docs/share-localhost/auth) for setup instructions. ## Next steps * [Quickstart](/docs/share-localhost/quickstart): get started with ngrok in minutes * [Authentication](/docs/share-localhost/auth): protect your shared API with authentication * [Network security](/docs/share-localhost/security): restrict access by IP or other criteria # Authentication Source: https://ngrok.com/docs/share-localhost/auth Add authentication to your ngrok endpoints to control who can access your locally shared applications. ngrok makes it easy to add authentication to any endpoint without modifying your application code. Using [Traffic Policy](/docs/gateway/traffic-policy/), you can require visitors to authenticate before they can access your local app. ## Add OAuth with Traffic Policy The quickest way to protect your endpoint is with the [OAuth Traffic Policy action](/docs/gateway/traffic-policy/actions/oauth/). Add the following to your Traffic Policy configuration to require Google authentication: ### Using a Managed Provider The following [Traffic Policy](/docs/gateway/traffic-policy/) configuration will provide your app with a google authentication step. ```yaml policy.yml theme={null} on_http_request: - actions: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@acme.com')" actions: - type: deny ``` ```json policy.json theme={null} { "on_http_request": [ { "actions": [ { "type": "oauth", "config": { "provider": "google" } } ] }, { "expressions": [ "!actions.ngrok.oauth.identity.email.endsWith('@acme.com')" ], "actions": [ { "type": "deny" } ] } ] } ``` The `provider` value can be replaced with any of the [Supported Providers](/docs/gateway/traffic-policy/actions/oauth/#supported-providers) that have an a managed app available. This example uses a managed Google OAuth application owned by ngrok. This is useful for testing and development, but you should use your own OAuth application when moving to production. See the [OAuth action documentation](/docs/gateway/traffic-policy/actions/oauth/#managed-applications) for details. ## Supported authentication methods | Method | Description | | - | - | | [OAuth](/docs/gateway/traffic-policy/actions/oauth/) | Require login via Google, GitHub, Microsoft, and other providers | | [OpenID Connect](/docs/gateway/traffic-policy/actions/oidc/) | Integrate with any OIDC-compliant identity provider | | [SAML](/docs/gateway/traffic-policy/actions/saml/) | Use SAML-based SSO for enterprise authentication | | [Basic Auth](/docs/gateway/traffic-policy/actions/basic-auth/) | Require a username and password | ## Restrict access to specific users You can restrict access to specific email addresses by defining your Traffic Policy in your ngrok configuration file: ```yaml title="ngrok.yml" theme={null} on_http_request: - actions: - type: oauth config: provider: google - expressions: - "actions.ngrok.oauth.identity.email == 'teammate@acme.com'" actions: - type: deny ``` This is ideal for sharing your app with a specific teammate while keeping it private from everyone else. ## Next steps * [OAuth action reference](/docs/gateway/traffic-policy/actions/oauth/): full configuration options for OAuth * [Network security](/docs/share-localhost/security): combine auth with IP restrictions and other controls * [Traffic Policy](/docs/gateway/traffic-policy/): explore all available Traffic Policy actions # Inspect Traffic and Replay Requests Source: https://ngrok.com/docs/share-localhost/inspection Inspect and replay HTTP requests and responses with ngrok's Traffic Inspector to speed up debugging and development. ngrok includes a built-in Traffic Inspector that lets you see every HTTP request and response flowing through your tunnel in real time, and replay any captured HTTP request with a single click. This is useful for debugging webhooks, API integrations, and frontend-backend communication. You can enable Traffic Inspector for your endpoints in [the ngrok dashboard](https://dashboard.ngrok.com/traffic-inspector). ## Inspecting request bodies The Traffic Inspector automatically formats and syntax-highlights common content types including JSON, XML, HTML, and form data. This makes it easy to verify that webhook payloads, API requests, and form submissions contain the expected data. ## Filtering traffic Use the search bar in the Traffic Inspector to filter requests by path, method, status code, or any other attribute. This helps you focus on the specific requests you're debugging. ## Traffic inspection via the API You can also access captured traffic programmatically through the [Agent API](/docs/gateway/agent/api). This is useful for building automated tests or integrating traffic inspection into your development workflow. ## How replays work 1. Start your tunnel with `ngrok http ` 2. Open [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) in ngrok dashboard 3. Trigger a request (or wait for a webhook delivery) 4. Click on the request in Traffic Inspector 5. Click **Replay** on any captured request to resend it to your local server 6. ``` ``` The replayed request is sent with the same method, headers, and body as the original, giving you a consistent way to test your handler without external dependencies. ### Use cases for request replays * **Webhook development:** Replay webhook deliveries instead of waiting for the provider to retry. * **Bug reproduction:** Capture a request that triggers a bug, fix your code, and replay to verify the fix. * **Load testing:** Replay requests multiple times to observe how your application handles repeated traffic. ### Modifying replayed requests You can edit a request before replaying it. Change headers, query parameters, or the request body to test different scenarios without needing external tools like cURL or Postman. ## Next steps * [Webhooks](/docs/share-localhost/webhooks): use inspection to debug webhook integrations * [Agent API](/docs/gateway/agent/api): access traffic data programmatically # Test Mobile Apps Locally Source: https://ngrok.com/docs/share-localhost/mobile Use ngrok to connect your mobile app to a local backend for testing and development without deploying. When developing mobile apps, you need your device or emulator to reach your local backend API. ngrok makes this simple by providing a public HTTPS URL that routes traffic to your local development server. ## How it works 1. [Install ngrok](https://ngrok.com/download) 2. Start your backend API on your development machine 3. Run `ngrok http ` to get a public HTTPS URL 4. Point your mobile app at the ngrok URL instead of `localhost` 5. Test your app against your local backend from any device ## Quick example ```bash theme={null} ngrok http 8080 ``` Use the HTTPS forwarding URL in your mobile app's API configuration. Since ngrok provides a valid TLS certificate, your app works without any certificate pinning workarounds. ## Benefits for mobile development * **Test on real devices:** Access your local server from physical phones and tablets on any network. * **No deployment required:** Skip deploying to staging just to test a backend change. * **HTTPS by default:** ngrok provides a valid TLS certificate, so your app's HTTPS requirements are satisfied automatically. * **Inspect traffic:** Use the [Traffic Inspector](/docs/share-localhost/inspection) to see exactly what requests your mobile app is making and what responses it receives. ## Stable URLs On paid plans, you can use the `--url` flag to get a consistent domain for your endpoint. This way you don't need to update your mobile app's configuration every time you restart ngrok: ```bash theme={null} ngrok http 8080 --url your-app.ngrok.dev ``` ## Next steps * [Quickstart](/docs/share-localhost/quickstart): get started with ngrok in minutes * [Authentication](/docs/share-localhost/auth): add auth to your endpoint to restrict access during testing * [Traffic inspection](/docs/share-localhost/inspection): debug requests from your mobile app # Share Localhost Overview Source: https://ngrok.com/docs/share-localhost/overview Use ngrok to instantly share your local development server with anyone on the internet for demos, testing, and collaboration. ngrok makes it easy to [share your localhost](https://ngrok.com/use-cases/share-localhost) with anyone on the internet. Whether you're demoing a feature to a teammate, testing webhooks from a third-party service, or debugging a mobile app against your local backend, ngrok gives you a secure public URL that forwards traffic directly to your local machine. No deployment required. ## Why use ngrok to share localhost? * **Instant public URLs:** Get a publicly accessible HTTPS URL for your local app in seconds with a single command. * **No deployment needed:** Skip staging environments and cloud deploys. Share what's running on your machine right now. * **Built-in security:** Every endpoint comes with TLS encryption out of the box. You can also add authentication, IP restrictions, and more with Traffic Policy. * **Traffic inspection:** See every request and response in real time with ngrok's built-in Traffic Inspector, and replay requests to speed up debugging. ## Use cases Receive webhook callbacks from third-party services on your local machine Test mobile apps against your local backend without deploying Share your local API with teammates or external consumers for testing ## The ngrok platform Learn what happens when you put a local service on a public URL Inspect HTTP requests and responses in real time Add OAuth, basic auth, or other authentication to your endpoints Control access with IP restrictions, mTLS, and more ## Next steps Get your local app online in minutes with the ngrok Agent CLI Learn about ngrok's global points of presence and edge network # Share Localhost Quickstart Source: https://ngrok.com/docs/share-localhost/quickstart Get your local app online and shareable in minutes using the ngrok Agent CLI. The simplest way to get started is to create an Agent Endpoint that forwards public traffic to your localhost using the [ngrok Agent CLI](/docs/gateway/agent/). This quickstart walks you through that process, as well as how to implement basic security measures by requiring visitors to log in with a Google account to access your app. ## What you'll need * An [ngrok account](https://dashboard.ngrok.com/signup). * Your [ngrok auth token](https://dashboard.ngrok.com/get-started/your-authtoken). * On Mac OS, you'll need [homebrew](https://brew.sh/). ## 1. Install the ngrok Agent CLI Run the command that corresponds to your operating system to install the Agent CLI: Install ngrok via [Homebrew](https://brew.sh/). ```bash theme={null} brew install ngrok ``` ```bash theme={null} curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \ | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \ && echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \ | sudo tee /etc/apt/sources.list.d/ngrok.list \ && sudo apt update \ && sudo apt install ngrok ``` Install via the Windows App Store Or [follow the direct installation guide](https://download.ngrok.com?tab=download) if you can't use one of the options above. To test that it's been installed correctly, run the following command in your terminal and confirm that ngrok prints its help text. ```bash theme={null} ngrok help ``` ## 2. Connect your account Connect your agent to your ngrok account by providing your auth token as shown below—replace `$YOUR_TOKEN` with the string given to you [in the dashboard](https://dashboard.ngrok.com/get-started/your-authtoken). ```bash theme={null} ngrok config add-authtoken $YOUR_TOKEN ``` ## 3. Start your app or service Start up the app or service you'd like to put online. This is the app that your Agent Endpoint will forward online traffic to. If you don't have an app to work with, you can create a minimal app in your language of choice using the following code to set up a basic HTTP server at port `8080`. ```javascript title="service.js" theme={null} const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/html' }); const html = ` Test Page

Hello from Node.js HTTP Server!

`; res.end(html); }); const port = 8080; server.listen(port, () => { console.log(`Serving custom HTML at http://localhost:${port}`); }); ``` ```go title="service.go" theme={null} package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go HTTP Server!") } func main() { http.HandleFunc("/", handler) fmt.Println("Starting server at http://localhost:8080") err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Println("Server failed:", err) } } ``` ```python title="service.py" theme={null} from http.server import BaseHTTPRequestHandler, HTTPServer class MyHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() html = b""" Test Page

Hello from Python HTTP Server!

""" self.wfile.write(html) def run(server_class=HTTPServer, handler_class=MyHandler, port=8080): server_address = ('', port) httpd = server_class(server_address, handler_class) print(f"Serving custom HTML at http://localhost:{port}") httpd.serve_forever() if __name__ == "__main__": run() ```
Navigate to the directory where this file is located and start the server. ## 4. Put your app online Start the ngrok agent by running the following command (replace `8080` if your app is running on a different port). ```bash theme={null} ngrok http 8080 ``` All accounts come with a free dev domain that is automatically chosen when you start an endpoint. You can add the optional `--url` flag to customize this domain on paid plans. The agent should print a console UI to your terminal to confirm that it's online and forwarding as intended. Open the forwarding URL in your browser to see your web application, which is now available over HTTPS with a valid certificate that ngrok automatically manages for you. ## 5. Secure your app ngrok makes it simple to add authentication to your app by implementing a [Traffic Policy](/docs/gateway/traffic-policy/). Using the Agent CLI, you can add a Traffic Policy to your endpoint by editing your ngrok config file, which was automatically created when you provided your auth token in step 3. Terminate the Agent Endpoint that you started up in step 5, then run the following command in your terminal to open `ngrok.yml`: ```bash theme={null} ngrok config edit ``` You should see a new console UI with your config version and auth token already set. Below the auth token, paste the following snippet into the editor and then save and exit the config file. This policy states that whenever an HTTP/S request is made to `$YOUR_DOMAIN`, the Agent Endpoint should redirect the user to Google OAuth for authentication before proceeding to the app running on port `8080`. If the email is not from the specified domain, in this case `@acme.com`, the visitor will be denied access. ```yaml title="ngrok.yml" theme={null} endpoints: - name: cli-quickstart url: $YOUR_DOMAIN traffic_policy: on_http_request: - actions: - type: oauth config: provider: google - expressions: - "!actions.ngrok.oauth.identity.email.endsWith('@acme.com')" actions: - type: deny upstream: url: 8080 protocol: http1 ``` This example uses ngrok's default Google OAuth application. To use your own, see [the OAuth Traffic Policy Action documentation](/docs/gateway/traffic-policy/actions/oauth/#google-example). Now you can start up your endpoint using its name: ```bash theme={null} ngrok start cli-quickstart ``` When you visit your public URL you should be prompted to authenticate by logging in with a Google account before you can access your app. ## What's next? In this guide, you learned how to create an Agent Endpoint to forward traffic from the internet to an app running on your local device using the ngrok agent CLI. You were introduced to some of the most common commands you should know, and you implemented a Traffic Policy, which can be used for many kinds of actions beyond the basic authentication example here. You also saw how to edit your config file to make your Agent Endpoints repeatable and scalable by assigning a name you can refer back to with URLs, protocols, and actions already defined. What else can you do with these features? * If you need to interact with ngrok Agent Endpoints programmatically, use one of the Agent SDKs which are available for [JavaScript (Node.js)](/docs/getting-started/javascript/), [Go](/docs/getting-started/go/), [Python](/docs/getting-started/python/), and [Rust](/docs/getting-started/rust/). * If your use case calls for a centrally managed, always-on endpoint instead of one that is only available when an agent is running, you should proceed to [getting started with Cloud Endpoints](/docs/getting-started/cloud-endpoints-quickstart/). * This quickstart barely scratches the surface of what's possible with a Traffic Policy—check out the [Actions overview](/docs/gateway/traffic-policy/actions/) to see what else you can do. * Visit the [Agent CLI command reference](/docs/gateway/agent/cli/) for a complete list of available commands. # Network Security Source: https://ngrok.com/docs/share-localhost/security Control access to your ngrok endpoints with IP restrictions, mTLS, and other network-level security controls. ngrok provides multiple layers of network security that you can apply to your endpoints using [Traffic Policy](/docs/gateway/traffic-policy/). These controls let you restrict who can reach your local application at the network level. ## IP restrictions Limit access to your endpoint to specific IP addresses or CIDR ranges: ```yaml title="ngrok.yml" theme={null} endpoints: - name: my-app url: $YOUR_DOMAIN traffic_policy: on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - "203.0.113.0/24" - "198.51.100.42" upstream: url: 8080 protocol: http1 ``` ## Mutual TLS (mTLS) For environments that require client certificate verification, ngrok supports mutual TLS. This ensures that only clients presenting a valid certificate can connect to your endpoint. See the [Agent TLS Termination](/docs/gateway/agent/agent-tls-termination) documentation for configuration details. ## Combining security controls You can layer multiple security controls on a single endpoint. For example, combine IP restrictions with OAuth authentication: ```yaml title="ngrok.yml" theme={null} endpoints: - name: my-app url: $YOUR_DOMAIN traffic_policy: on_http_request: - actions: - type: restrict-ips config: enforce: true allow: - "203.0.113.0/24" - type: oauth config: provider: google upstream: url: 8080 protocol: http1 ``` ## Next steps * [IP Restrictions action reference](/docs/gateway/traffic-policy/actions/restrict-ips/): full configuration options for IP restrictions * [Authentication](/docs/share-localhost/auth): add user-level authentication to your endpoints * [Traffic Policy](/docs/gateway/traffic-policy/): explore all available security and traffic management actions # How Sharing Localhost Works Source: https://ngrok.com/docs/share-localhost/tunnels Learn what happens when you put a service running on your machine on the internet, and what that means for access and security. Your app runs on `localhost`, where only you can reach it. Sharing localhost gives it a public URL that anyone you choose can open, without deploying your code, changing your network, or opening a port on your machine. ## What happens when you share Start the ngrok agent and point it at the port your app already listens on: ```bash theme={null} ngrok http 8080 ``` The agent opens an outbound connection to ngrok's cloud service, which hands back a public URL. Requests to that URL travel over that connection and arrive at your app on `localhost:8080`. Your code keeps running where it always did. Edit a file, restart your server, and the next request at the same URL hits the new version. Two things follow from the agent dialing out rather than listening: * **Your machine stays closed.** Nothing accepts inbound connections on it, so there are no ports to forward and no firewall rules to change. The connection works from home networks, coffee shop Wi-Fi, and corporate networks that block inbound traffic. * **The traffic is encrypted.** The connection between your machine and ngrok runs over TLS, and your public URL is served over HTTPS with a certificate ngrok provisions and renews for you. ## What you can share | What you're sharing | Command | | - | - | | A web app, API, or webhook receiver | `ngrok http 8080` | | A database, SSH server, RDP host, or game server | `ngrok tcp 3389` | | A service that presents its own TLS certificate | `ngrok tls 8443` | See the [protocols documentation](/docs/gateway/endpoints/protocols) for the details of each. ## Who can reach it Your URL is public as soon as it exists. Anyone who has it can open it, so treat it like a link to a live service rather than a private address—and remember that whatever you share is running on your own machine. You control access without changing your app: * [Add authentication](/docs/share-localhost/auth) so visitors sign in before they reach you. * [Review the security model](/docs/share-localhost/security) to understand what you expose and how to limit it. * [Apply a Traffic Policy](/docs/gateway/traffic-policy/) to require credentials, restrict IP addresses, rate limit requests, or rewrite traffic in flight. When you stop the agent, the URL stops working and your machine is unreachable again. What you created while it ran is an [Agent Endpoint](/docs/gateway/endpoints/agent-endpoints), an endpoint that exists only for the life of the agent process. ## Next steps * [Quickstart](/docs/share-localhost/quickstart): share your first local service. * [Inspect traffic and replay requests](/docs/share-localhost/inspection): see every request that reaches your app and send it again. * [Agent CLI reference](/docs/gateway/agent/cli): the full set of commands and flags. # Test Webhooks Locally Source: https://ngrok.com/docs/share-localhost/webhooks Use ngrok to receive webhook callbacks from third-party services directly on your local development machine. Webhooks require a publicly accessible URL, which makes local development tricky. ngrok solves this by giving your local server a public HTTPS endpoint that webhook providers can deliver events to. ## How it works 1. [Install ngrok](https://ngrok.com/download) 2. Start your local webhook handler on any port 3. Run `ngrok http ` to create a public URL 4. Register the ngrok URL with your webhook provider 5. Receive webhook events directly on your local machine ## Quick example ```bash theme={null} ngrok http 8080 ``` Copy the HTTPS forwarding URL from the agent console and paste it into your webhook provider's configuration (for example, Stripe, GitHub, or Twilio). ## Inspect webhook payloads ngrok's built-in [Traffic Inspector](/docs/share-localhost/inspection) lets you see every webhook request in real time, including headers, body, and response. This makes it easy to debug webhook integrations without adding logging to your application. ## Replay failed webhooks Instead of waiting for the webhook provider to retry a failed delivery, you can [replay the request](/docs/share-localhost/inspection.mdx#how-replays-work) instantly from the Traffic Inspector. This dramatically speeds up the development cycle when building webhook handlers. ## Verify webhook signatures Webhook providers sign each request with a secret so you can confirm it's authentic. Instead of writing verification logic in your application, you can use the [verify-webhook Traffic Policy action](/docs/gateway/traffic-policy/actions/verify-webhook/) to validate incoming webhook signatures and confirm requests originate from the expected provider. Add a `verify-webhook` action to your Traffic Policy configuration with your provider name and signing secret: ```yaml title="policy.yml" theme={null} on_http_request: - actions: - type: verify-webhook config: provider: gitlab secret: secret! - type: custom-response config: status_code: 200 headers: content-type: text/plain body: GitLab webhook verified ``` Then start your endpoint with the policy file: ```bash theme={null} ngrok http 8080 --traffic-policy-file /path/to/policy.yml ``` If verification succeeds, the request proceeds to the next action defined in your Traffic Policy, which in this case will send a custom HTTP response. If it fails, the request is terminated with a `403 Forbidden` response. ### Debugging When debugging, testing, and crafting custom responses, you may want to allow further Traffic Policy actions to be processed even if the webhook signature isn't verified. To do so, set `enforce` to `false`: ```yaml title="policy.yml" {6} theme={null} on_http_request: - actions: - type: verify-webhook config: provider: github secret: your-webhook-secret enforce: false ``` With `enforce: false`, unverified requests still reach your server, but you can inspect the verification result using [action result variables](/docs/gateway/traffic-policy/actions/verify-webhook/#action-result-variables) in subsequent actions. ### Supported providers for webhook verification The `verify-webhook` action supports [many providers](/docs/gateway/traffic-policy/actions/verify-webhook/#supported-providers), including GitHub, Stripe, Shopify, Slack, and Twilio. ## Next steps * [Verify webhooks](/docs/gateway/traffic-policy/actions/verify-webhook) using your signing key * [Inspect requests and responses](/docs/share-localhost/inspection) in real-time * Browse [webhook integration guides](/docs/integrations/) for provider-specific setup instructions # Get started with ngrok Source: https://ngrok.com/docs/start ngrok makes it easy to put your apps, APIs, and services online. ngrok is developer infrastructure that routes and secures traffic to your apps, APIs, and AI models. We give you ways to manage connectivity between all of the tools and platforms you use to put your services online, no matter where they're deployed or what network they're on. ## Problems we solve Put your local app on a public URL in seconds with the ngrok CLI. Deliver and secure your production APIs with Traffic Policy. Route, secure, and transform traffic to any AI model. Control IoT devices in the field with secure remote access. Connect to APIs and databases in customer networks. Use the ngrok Operator for ingress to apps in any K8s cluster. ## You may also need to ## Start building Explore our example gallery to learn how to use ngrok as a gateway for your API, database, webhooks, and more, all protecting your services from malicious actors. Use our Traffic Policy examples to get started routing, authenticating, filtering, and modifying headers on requests to your ngrok endpoints. ## Use ngrok with popular tools Create and manage ngrok endpoints programmatically in your Python, Go, Node, or Rust applications using an Agent SDK. Use ngrok with your favorite enterprise-grade auth, webhook, and observability services like Okta SSO, Microsoft Entra ID, Datadog, and more. ## Learn about pricing See how ngrok's pricing breaks down based on usage for each account plan. # Using ngrok with CGNAT Source: https://ngrok.com/docs/using-ngrok-with/cgnat Learn how to use ngrok with CGNAT networks like Starlink to expose local services without router port forwarding. This guide shows you how to use ngrok with CGNAT networks like Starlink to expose local services without router port forwarding. ngrok is a good solution when you don't have access to open ports on your router. This is the case with Starlink and other networks that use CGNAT. For more details, see [Don Simpson's blog post about using ngrok with CGNAT](https://www.donaldsimpson.co.uk/2017/01/30/using-ngrok-to-work-around-carrier-grade-nat-cgnat/). ## What you'll need * [ngrok](https://download.ngrok.com) installed. * Your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). ## 1. Install and configure ngrok [Install ngrok](https://download.ngrok.com) using the method that works best for your system. Add your ngrok authtoken: ```bash theme={null} ngrok config add-authtoken TOKEN ``` ## 2. Start an ngrok endpoint Start an ngrok endpoint: ```bash theme={null} ngrok http 80 ``` No changes are needed for your network or router. # Using ngrok with Docker Compose Source: https://ngrok.com/docs/using-ngrok-with/docker/compose Learn how to put your apps and services online using ngrok with Docker Compose. If you're more comfortable using Docker Compose, you can use the following as a starting point. Copy the contents below into a new file named `docker-compose.yaml`, then run `docker compose up` in that directory. This Docker Compose file assumes you have an `ngrok.yml` file in the same directory with at least one endpoint defined. See the [ngrok agent config file documentation](/docs/gateway/agent/config/) for help creating a configuration file. To reuse the same configuration file as your local ngrok agent, find the default config file's location with `ngrok config check`. ```yaml theme={null} services: ngrok: image: ngrok/ngrok:latest restart: unless-stopped command: - "start" - "--all" - "--config" - "/etc/ngrok.yml" volumes: - ./ngrok.yml:/etc/ngrok.yml ports: - 4040:4040 ``` If you define your endpoints directly in `docker-compose.yaml` rather than using an `ngrok.yml` file, the configuration looks a little different. The command runs an `ngrok http` command, and on Mac or Windows you point it at the special URL `host.docker.internal` so the container can reach a service on your host (see [Networking on Docker Desktop](https://docs.docker.com/desktop/features/networking/networking-how-tos/)). The following example runs ngrok alongside the `dockersamples/static-site` image. ```yaml theme={null} services: static-site: image: dockersamples/static-site build: . ports: - "80:80" restart: always ngrok: image: ngrok/ngrok:latest command: - "http" - "http://host.docker.internal:80" environment: NGROK_AUTHTOKEN: ${NGROK_AUTHTOKEN} ports: - 4040:4040 ``` # Using ngrok Docker Desktop Source: https://ngrok.com/docs/using-ngrok-with/docker/desktop Learn how to use ngrok with Docker Desktop. The ngrok Docker Desktop Extension allows you to use ngrok's API Gateway cloud service to forward traffic from internet-accessible endpoint URLs to your local Docker containers. The extension provides tools to: * Create and manage endpoints * Apply Traffic Policy * Configure custom URLs and endpoint binding type * Enable endpoint pooling for load balancing scenarios ## Installation You need [Docker Desktop](https://docs.docker.com/desktop/) installed first. Once it's running, [add the ngrok extension to Docker Desktop](https://open.docker.com/extensions/marketplace?extensionId=ngrok/ngrok-docker-extension). ## Quick start 1. After installing the extension, you will be prompted to add your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). 2. Start an endpoint by clicking `+` icon on the container you want to put online. 3. Optionally specify a custom URL and [Traffic Policy](/docs/gateway/traffic-policy/). You now have an endpoint URL for your container that you can share. # Using ngrok with Docker Source: https://ngrok.com/docs/using-ngrok-with/docker/index Learn how to use ngrok with Docker to expose your local applications and services to the internet. For detailed instructions on using ngrok with Kubernetes, check out the [k8s quickstart](/docs/gateway/k8s/). ngrok provides [pre-built docker images](https://hub.docker.com/r/ngrok/ngrok) for the ngrok Agent with instructions for getting started. An example command for starting a tunnel to port `80` on the host machine looks like this. ```bash theme={null} docker run --net=host -it -e NGROK_AUTHTOKEN=xyz ngrok/ngrok:latest http 80 ``` ```bash theme={null} docker run -it -e NGROK_AUTHTOKEN=xyz ngrok/ngrok:latest http host.docker.internal:80 ``` For MacOS and Windows, you must use the special URL `host.docker.internal` as described in the [Docker networking documentation](https://docs.docker.com/desktop/features/networking/networking-how-tos/). This also applies to the `upstream.url` endpoint property in your ngrok config file. For example: ```yaml theme={null} endpoints: - name: example url: https://example.ngrok.app upstream: url: http://host.docker.internal:80 ``` Note: the Docker version of ngrok follows the same convention as the agent, for example: ```bash theme={null} docker run -it -e NGROK_AUTHTOKEN=xyz ngrok/ngrok:latest http 80 # secure public URL for port 80 web server docker run -it -e NGROK_AUTHTOKEN=xyz ngrok/ngrok:latest http --url baz.ngrok.dev 8080 # port 8080 available at baz.ngrok.dev docker run -it -e NGROK_AUTHTOKEN=xyz ngrok/ngrok:latest http foo.dev:80 # tunnel to host:port instead of localhost docker run -it -e NGROK_AUTHTOKEN=xyz ngrok/ngrok:latest http https://localhost:5001 # expose a local https server running on port 5001 docker run -it -e NGROK_AUTHTOKEN=xyz ngrok/ngrok:latest tcp 22 # tunnel arbitrary TCP traffic to port 22 docker run -it -e NGROK_AUTHTOKEN=xyz ngrok/ngrok:latest tls --url foo.com 443 # TLS traffic for foo.com to port 443 docker run -it -e NGROK_AUTHTOKEN=xyz ngrok/ngrok:latest start foo bar baz # start tunnels from the configuration file ``` ### Basic usage The ngrok docker image wraps the ngrok agent executable. Read the documentation for the [ngrok agent CLI docs](/docs/gateway/agent/cli/) for all commands. #### Run an ngrok Agent pointed at localhost:80 ```bash theme={null} docker run -it -e NGROK_AUTHTOKEN=your_token ngrok/ngrok http host.docker.internal:80 ``` ### Choose a URL If you don't choose a URL, ngrok will assign one for you. ```bash theme={null} docker run -it -e NGROK_AUTHTOKEN=your_token ngrok/ngrok http host.docker.internal:80 --url https://your-url-here.ngrok.app ``` ### Add a Traffic Policy [Traffic Policy](/docs/gateway/traffic-policy/) is a configuration language that offers you the flexibility to filter, match, manage, and orchestrate traffic to your endpoints. ```bash theme={null} docker run -it -v $(pwd)/traffic-policy.yml:/etc/traffic-policy.yml ngrok/ngrok:alpine http host.docker.internal:80 --traffic-policy-file /etc/traffic-policy.yml ``` ##### `traffic-policy.yml` ```yaml theme={null} on_http_request: - actions: - type: basic-auth config: credentials: - user:password123 ``` #### Run in the background ```bash theme={null} docker run -d --restart unless-stopped -e NGROK_AUTHTOKEN=your_token --name ngrok-agent ngrok/ngrok http host.docker.internal:80 ``` ### Use a configuration file Run the ngrok agent with the config file `./ngrok.yml` from the host machine: ```bash theme={null} docker run -it -v $(pwd)/ngrok.yml:/etc/ngrok.yml -e NGROK_CONFIG=/etc/ngrok.yml ngrok/ngrok:alpine http host.docker.internal:80 ``` #### Pull the ngrok container image ```bash theme={null} docker pull ngrok/ngrok ``` ## Traffic Inspection #### Traffic Inspector Use [Traffic Inspector](https://dashboard.ngrok.com/traffic-inspector) on your ngrok dashboard #### Local web inspection on localhost:4040 (Legacy) The agent serves this web interface on port 4040 so you'll need to publish it as well with `-p 4040:4040` ```bash theme={null} docker run -it -p 4040:4040 ngrok/ngrok http host.docker.internal:80 ``` If you are unable to view the web inspection interface typically available at `http://localhost:4040`, you may need to map your host port `4040` to port `4040` on the container, for example: ```bash theme={null} docker run -p 4040:4040 -it -e NGROK_AUTHTOKEN=xyz ngrok/ngrok:latest http host.docker.internal:80 ``` # Using ngrok with FastAPI Source: https://ngrok.com/docs/using-ngrok-with/fastAPI Learn how to embed ngrok into FastAPI applications using the ngrok-python library for secure ingress. This guide shows you how to embed the ngrok agent into FastAPI applications using the [ngrok-python](https://github.com/ngrok/ngrok-python) library. It covers setting up a FastAPI application with ngrok integration for secure ingress. ngrok-python and FastAPI libraries used in this example are sensitive to versions. Use of Virtual Environments is highly recommended. ## What you'll need * Python installed on your machine. * An [ngrok account](https://dashboard.ngrok.com/signup). * Your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). ## 1. Install dependencies Create a `requirements.txt` file with the following dependencies: ```sh theme={null} # requirements.txt ngrok==1.4.0 uvicorn==0.29.0 fastapi==0.111.0 loguru==0.7.2 ``` Install the dependencies: ```sh theme={null} pip install -r requirements.txt ``` ## 2. Create the FastAPI application Create a `main.py` file with the following code: ```python theme={null} # main.py from contextlib import asynccontextmanager from os import getenv import ngrok import uvicorn from fastapi import FastAPI from loguru import logger NGROK_AUTH_TOKEN = getenv("NGROK_AUTH_TOKEN", "") APPLICATION_PORT = 5000 @asynccontextmanager async def lifespan(app: FastAPI): logger.info("Setting up ngrok Endpoint") ngrok.set_auth_token(NGROK_AUTH_TOKEN) ngrok.forward( addr=APPLICATION_PORT, ) yield logger.info("Tearing Down ngrok Endpoint") ngrok.disconnect() app = FastAPI(lifespan=lifespan) @app.get("/") async def root(): return {"message": "Hello World"} if __name__ == "__main__": uvicorn.run("main:app", host="127.0.0.1", port=APPLICATION_PORT, reload=True) ``` # Using ngrok with Flask Source: https://ngrok.com/docs/using-ngrok-with/flask Learn how to use ngrok with Flask development servers to share your local application with others using the ngrok agent or Python SDK. This guide shows you how to use ngrok with Flask development servers to share your local application with others. You can use either the ngrok agent or the Python SDK to expose your Flask app. ## What you'll need * Python and Flask installed. * An [ngrok account](https://dashboard.ngrok.com/signup). * Your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). ## Option 1. Use the ngrok agent To share a local Flask development server with someone else, run: ```bash theme={null} ngrok http 5000 ``` You may need to update your `SERVER_NAME` and `APPLICATION_ROOT` in your Flask app configuration to the values provided by ngrok. See the Flask configuration docs for more information. For users on the latest MacOS, there is an issue where the default port 5000 (and 7000) is used by Apple AirPlay Receiver. You can use a different port for your Flask app or disable the Apple AirPlay receiver by disabling it in **System Settings > General > AirDrop & Handoff > AirPlay Receiver**. ## Option 2. Use the Python SDK You can use the [ngrok Python SDK](https://github.com/ngrok/ngrok-python) to start an endpoint to the Flask dev server via Python code, or using the [ngrok ASGI runner](https://github.com/ngrok/ngrok-python#asgi-runner---tunnels-to-uvicorn-gunicorn-django-and-more-with-no-code) with an ASGI wrapper. ## Further resources * [ngrok SDK on PyPI](https://pypi.org/project/ngrok/) * [ngrok SDK on GitHub](https://github.com/ngrok/ngrok-python) * [ngrok Python SDK docs](https://ngrok.github.io/ngrok-python/) # Using ngrok with FTP, FTPS, SFTP Source: https://ngrok.com/docs/using-ngrok-with/ftp Learn how to use ngrok with SFTP for secure file transfers, and why FTP/FTPS are not recommended due to multiple port requirements. This guide explains how to use ngrok with SFTP and why FTP or FTPS are not a good fit for TCP tunneling. ## Compatibility with FTP and FTPS FTP and FTPS (FTP over SSL) require multiple dynamic ports for data transfer between server and client, so ngrok is not a good solution for that traffic. Use SFTP instead, which uses a single port. ## ngrok with SFTP You can use ngrok for SFTP by starting a TCP endpoint on the port your SFTP server uses, usually port `22` (the same as SSH). ```bash theme={null} ngrok tcp 22 ``` TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. From there, use the resulting TCP address in your preferred SFTP client to connect to your local server. ## Alternatives to FTP for sharing files If you're just looking for a quick way to serve local files, ngrok has a [built-in file server](/docs/gateway/endpoints/http/#serving-file-directories). # Using ngrok with gRPC Source: https://ngrok.com/docs/using-ngrok-with/gRPC Learn how to use ngrok with gRPC services by specifying the HTTP/2 app protocol or using TCP endpoints. Since gRPC uses HTTP/2, you may need to specify the `app-protocol` when forwarding a gRPC endpoint: `ngrok http --app-protocol=http2 80` This works with other ngrok features like custom domains and ports. This [Stack Overflow user](https://stackoverflow.com/a/59555606/7282727) also reports that forwarding to gRPC services works using `proto: tcp`. # Configuring GoDaddy DNS for ngrok Reserved Domains Source: https://ngrok.com/docs/using-ngrok-with/godaddy Step-by-step guide for configuring GoDaddy DNS records to work with ngrok reserved domains using CNAME records. This guide explains how to configure a custom domain in GoDaddy to work with ngrok. GoDaddy does not support CNAMEs as apex records. An apex record is a DNS record at the root (or apex) of a DNS zone. For example, in the DNS zone `example.com`, an apex record also has the fully qualified name `example.com` (this is sometimes called a naked domain). By convention, the relative name `@` is used to represent apex records. ## What you'll need * A [custom domain added in ngrok](/docs/gateway/domains/custom-domains/) with a subdomain (for example, `myapp.example.com`). GoDaddy requires a subdomain because it does not support CNAMEs as apex records. * A GoDaddy account with a domain. ## 1. Find your reserved domain in ngrok In your ngrok dashboard, go to [**Domains**](https://dashboard.ngrok.com/domains) and click the domain you want to use. Click **DNS Targets**. This provides you with the relevant CNAME values to use in later steps. ## 2. Add a CNAME record Open the DNS management page for your domain in GoDaddy and add a new **CNAME** record. GoDaddy's [Add a CNAME record](https://www.godaddy.com/help/add-a-cname-record-19236) guide covers where to find these controls in their DNS panel. Use the values from ngrok for the record: * **Host** (or **Name**): the subdomain you want to use (for example, `www` if your domain in ngrok is `www.example.com`). * **Points to** (or **Value**): the CNAME target provided by ngrok (for example, `ptenvq2ejdlptgyr.5mql8tts8aeqxhrtv.ngrok-cname.com`). Leave the TTL at its default and save the record. ## 3. Verify DNS status in ngrok Once you've configured the DNS records in GoDaddy, return to the ngrok dashboard and click the **Check Status** button on the bottom of the DNS Details page. It may take a few minutes to an hour for the DNS records to propagate. ngrok will continue to check at a regular interval. Once you see a green checkmark, your domain is configured. To use your new domain in the ngrok agent, add `--url your.domain.com` to the end of any command. For example: `ngrok http 80 --url your.domain.com`. # Using ngrok with Google Colab Source: https://ngrok.com/docs/using-ngrok-with/googleColab Learn how to use ngrok with Google Colab projects using the pyngrok library to expose local services. This guide shows you how to use ngrok from a Google Colab notebook using the [pyngrok](https://pyngrok.readthedocs.io/en/latest/integrations.html#google-colaboratory) library. It covers installing `pyngrok`, adding your ngrok authtoken, and starting a tunnel from Colab. ## What you'll need * A Google Colab notebook. * An [ngrok account](https://ngrok.com/signup). * Your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). ## 1. Install pyngrok in your notebook In a Colab cell, install `pyngrok`: ```bash theme={null} !pip -q install pyngrok ``` ## 2. Add your ngrok authtoken In a new cell, add your ngrok authtoken: ```python theme={null} from pyngrok import ngrok ngrok.set_auth_token("YOUR_AUTHTOKEN") ``` ## 3. Start a tunnel Start a tunnel to your local port (for example, `8080`): ```python theme={null} tunnel = ngrok.connect(8080) print(tunnel.public_url) ``` Use the printed URL to access your service through ngrok. # Using ngrok with Java Source: https://ngrok.com/docs/using-ngrok-with/java Learn how to use ngrok with Java applications using the ngrok Java SDK or API client for programmatic account management. This guide explains two ways to use ngrok from Java. It covers embedding ingress in an app with the ngrok Java SDK and managing ngrok resources programmatically with the Java API client. ## Use the ngrok Java SDK Use the [ngrok Java SDK](https://github.com/ngrok/ngrok-java) to embed ngrok secure ingress into your Java app. * [ngrok Java SDK on Maven](https://mvnrepository.com/artifact/com.ngrok/ngrok-java) * [ngrok Java SDK on GitHub](https://github.com/ngrok/ngrok-java) ## Use the ngrok Java API client Use the [ngrok Java API client](https://github.com/ngrok/ngrok-api-java) to manage your ngrok account and resources programmatically. It handles account resources such as reserved domains and IP restrictions. # Using ngrok with Laravel Source: https://ngrok.com/docs/using-ngrok-with/laravel Learn how to use ngrok with Laravel Valet to share your local Laravel development sites with others. This guide explains how to use ngrok with Laravel Valet to share your local Laravel development sites with others. ## What you'll need * Laravel Valet installed. * Your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). ## Set your ngrok authtoken ngrok is built into Laravel Valet. To learn more about using your existing ngrok account with Laravel, see the Laravel Valet documentation. ```bash theme={null} valet set-ngrok-token YOUR_TOKEN_HERE ``` Replace `YOUR_TOKEN_HERE` with your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). # Using ngrok with Minecraft Source: https://ngrok.com/docs/using-ngrok-with/minecraft Learn how to share your Minecraft server with others using ngrok TCP endpoints, including free plan limitations and troubleshooting tips. This guide explains how to share a Minecraft server using an ngrok TCP endpoint. It covers starting a TCP tunnel on the default Minecraft port (`25565`), sharing the connection address, and troubleshooting common issues. ## What you'll need * A Minecraft server running locally. To set one up, see [How to Setup a Minecraft: Java Edition Server](https://help.minecraft.net/hc/en-us/articles/360058525452-How-to-Setup-a-Minecraft-Java-Edition-Server). * The [ngrok agent](../gateway/agent/) installed. ## 1. Start a TCP tunnel Use the ngrok agent to open a TCP endpoint on port `25565`: ```bash theme={null} ngrok tcp 25565 ``` ## 2. Share the connection address Connect to your Minecraft server using the TCP address shown in the ngrok agent output (for example, `tcp://1.tcp.ngrok.io:12345`). If you need to restrict access to specific public IP addresses, follow the [secure Minecraft server example](/docs/gateway/examples/minecraft/). ## Minecraft on ngrok's free plan The [free plan](../pricing-limits/free-plan-limits/) has the following restrictions: * A valid credit or debit card is required. * To help combat abuse on the platform, a valid credit or debit card must be [added to your account](https://dashboard.ngrok.com/settings#id-verification) before using TCP endpoints on the free tier. * This card is not charged. * There is no permanent TCP address. * When you restart the agent, you need to send a new TCP address to your players. * Paid accounts let you reserve a TCP address for reuse. * For example, start the TCP endpoint with `ngrok tcp --url tcp://1.tcp.ngrok.io:12345 25565`. * Bandwidth is limited. * The free plan includes a limited amount of bandwidth per month. * When you exceed it, players can't connect to your server. Static/unchanging addresses and increased bandwidth limits are available with the [Pay-as-you-go plan](https://ngrok.com/pricing). ## Troubleshooting ### Bandwidth limit exceeded If you exceed the bandwidth limit on your account, players can no longer connect to your server. Players attempting to connect may see a **Disconnected** message in the Minecraft client. Your server logs may show a client connecting and immediately disconnecting. Check your [usage](https://dashboard.ngrok.com/usage) page to ensure you haven't exceeded your "Bandwidth Limit Out" limit. # Using ngrok with MongoDB Source: https://ngrok.com/docs/using-ngrok-with/mongodb Expose your local MongoDB server to the internet using ngrok TCP endpoints on port 27017. This guide explains how to expose your local MongoDB server using ngrok TCP endpoints. ## What you'll need * MongoDB installed and running locally. * An ngrok account. * Your ngrok authtoken. * A valid payment method added to your account (TCP endpoints are only available on a free plan after adding a valid payment method). ## Expose your MongoDB server Use a TCP endpoint to expose your local MongoDB server. Open an ngrok TCP endpoint to your local MongoDB service, which is usually running on port `27017`. ```bash theme={null} ngrok tcp 27017 ``` ## Connect to your database Once ngrok is forwarding traffic to your MongoDB service, use the TCP address and port from the ngrok output to connect from your preferred client. # Using ngrok with MySQL Source: https://ngrok.com/docs/using-ngrok-with/mysql Connect to your local MySQL database remotely using ngrok TCP endpoints on port 3306. This guide explains how to connect to your local MySQL database remotely using ngrok TCP endpoints. ## What you'll need * MySQL installed and running locally. * An ngrok account. * Your ngrok authtoken. * A valid payment method added to your account (TCP endpoints are only available on a free plan after adding a valid payment method). ## Expose your MySQL database To connect to a database using ngrok, you need to use a TCP endpoint. Once you have your database up and running, you can remotely connect to it using an ngrok TCP endpoint and the default port of `3306`. ```bash theme={null} ngrok tcp 3306 ``` ## Connect to your database Once ngrok is forwarding traffic, use the TCP address and port from the ngrok output to connect from your preferred MySQL client. ## Further resources * [Configure Site-to-Site Connectivity for Databases](/docs/gateway/site-to-site-connectivity/) * [Configure Site-to-Site Connectivity for Databases with mTLS](/docs/gateway/site-to-site-connectivity/) # Using ngrok with NGINX Source: https://ngrok.com/docs/using-ngrok-with/nginx Learn how to use ngrok with NGINX by setting the host header flag to properly route requests to your virtual hosts. It's a good idea to get a background on how [NGINX processes a request](https://nginx.org/en/docs/http/request_processing.html). What that usually means is you need to ensure the `--host-header` flag is set when creating an endpoint for your service. # Using ngrok with OpenVPN Connect Source: https://ngrok.com/docs/using-ngrok-with/openvpn Learn how to tunnel an OpenVPN server through CGNAT using ngrok TCP endpoints on port 443. This guide explains how to tunnel an OpenVPN server through CGNAT using an ngrok TCP endpoint on port `443`. It covers starting the endpoint, downloading your `.ovpn` profile, and updating it to use the ngrok TCP address and port. For a detailed community walkthrough, see [Tunneling openvpn server out from behind cgnat](https://forums.openvpn.net/viewtopic.php?t=30787) on the OpenVPN forums. ## What you'll need * An OpenVPN server running behind CGNAT. * OpenVPN Connect installed. * [ngrok](https://download.ngrok.com) installed. * Your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). ## 1. Start a TCP endpoint on port 443 Open a TCP endpoint to port `443`: ```bash theme={null} ngrok tcp 443 ``` TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. ## 2. Download your OpenVPN profile Open the TCP address shown in the ngrok agent output in your browser. Download the `.ovpn` profile you use with OpenVPN Connect. ## 3. Update the profile to use the ngrok port Open the downloaded `.ovpn` file in a text editor. Update the profile to use the TCP address and port shown by ngrok instead of `443`. Import the updated profile into OpenVPN Connect. You should be able to connect to your VPN as normal. # Using ngrok with an outbound proxy Source: https://ngrok.com/docs/using-ngrok-with/outboundProxy Learn how to configure ngrok to work through HTTP or SOCKS5 outbound proxies using environment variables or configuration files. ngrok works correctly through an HTTP or SOCKS5 proxy. ngrok respects the standard unix environment variable `http_proxy`. You may also set proxy configuration explicitly in the ngrok configuration file: * [Set the configuration variable `proxy_url`](/docs/gateway/agent/config/v3/#proxy-url) # Using ngrok with PostgreSQL Source: https://ngrok.com/docs/using-ngrok-with/postgresql Connect to your local PostgreSQL database remotely using ngrok TCP endpoints on port 5432. This guide walks you through connecting to your local PostgreSQL database remotely using ngrok TCP endpoints. ## What you'll need * PostgreSQL installed and running locally. * An ngrok account. * Your ngrok authtoken. * A valid payment method added to your account (TCP endpoints are only available on a free plan after adding a valid payment method). ## Expose your PostgreSQL database After installing the database, you can connect to it using a standard ngrok TCP endpoint sending traffic to port `5432`. ```bash theme={null} ngrok tcp 5432 ``` ## Connect to your database Once ngrok is forwarding traffic to your PostgreSQL service, you can use the TCP address provided to connect to it remotely. ```bash theme={null} psql -h 0.tcp.ngrok.io -p 12345 -U postgres -d postgres ``` # Using ngrok with Puppet Source: https://ngrok.com/docs/using-ngrok-with/puppet Learn how to use the ngrok Puppet module to install and configure ngrok resources and ensure the agent process is running. This guide explains how to install and configure ngrok with Puppet using the [`gabe-ngrok` module](https://forge.puppet.com/gabe/ngrok). It covers installing the module, configuring your authtoken, and declaring tunnels. ## What you'll need * Puppet installed. See [Installing Puppet](https://www.puppet.com/docs/puppet/7/install_puppet.html). * An [ngrok account](https://ngrok.com/signup). * Your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). ## 1. Install the Puppet module Install the module from Puppet Forge: ```bash theme={null} puppet module install gabe-ngrok ``` ## 2. Configure ngrok and declare a tunnel Configure the `ngrok` class with your authtoken, then declare at least one `ngrok::tunnel` resource. Currently ngrok requires an authtoken for every endpoint, so set it even when you only tunnel HTTP. For example, declare a single HTTP tunnel to port `80`: ```puppet theme={null} class { 'ngrok': authtoken => 'YOUR_AUTHTOKEN', } ngrok::tunnel { 'web traffic': proto => 'http', addr => '80', } ``` To expose a raw TCP port instead, keep the `ngrok` class declaration above and set `proto` to `tcp` on the tunnel: ```puppet theme={null} ngrok::tunnel { 'webhook': proto => 'tcp', addr => '8170', } ``` ## 3. Verify ngrok is running After your Puppet run completes, verify ngrok is running and your tunnels are active. If you use `facter`, the module provides an `ngrok` fact you can query: ```bash theme={null} facter -p ngrok ``` # Using ngrok with Redis Source: https://ngrok.com/docs/using-ngrok-with/redis Connect to your local Redis server remotely using ngrok TCP endpoints on port 6379. This guide explains how to reach a Redis server running on your machine from another network using ngrok TCP endpoints. ## What you'll need * Redis installed and running locally (default port `6379` unless you changed it). * An ngrok account. * Your ngrok authtoken. * A valid payment method added to your account (TCP endpoints are only available on the free plan after adding a valid payment method). ## Security Redis is often configured with no password on `localhost`. A TCP tunnel exposes that port on the public internet, so anyone who can reach the tunnel can run Redis commands unless you protect the instance. Before tunneling, enable authentication (`requirepass` or ACL users in Redis 6+), use a strong secret, and consider combining the tunnel with [IP restrictions](/docs/gateway/traffic-policy/concepts/ip-policies/) or other access controls on your endpoint. For long-lived or production-style access, prefer [site-to-site connectivity](/docs/gateway/site-to-site-connectivity/) patterns. ## Expose your Redis server Use a TCP endpoint aimed at the port Redis listens on (commonly `6379`). ```bash theme={null} ngrok tcp 6379 ``` If Redis listens on another port, substitute that port in the command. ## Connect to Redis When ngrok shows the forwarding address, use your Redis client with that host and port. For example, with [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli/): ```bash theme={null} redis-cli -h -p ``` Replace `` and `` with the values from the ngrok agent output or dashboard. TLS-terminated Redis (stunnel, cloud proxies, or Redis with TLS on a different local port) can still be forwarded the same way. Point the TCP endpoint at the port where your TLS listener accepts connections. ## Further resources * [TCP endpoints](/docs/gateway/endpoints/tcp/) * [Configure site-to-site connectivity for databases](/docs/gateway/site-to-site-connectivity/) # Using ngrok with Real-Time Messaging Protocol (RTMP) Source: https://ngrok.com/docs/using-ngrok-with/rtmp Learn how to share your RTMP streaming server using ngrok TCP endpoints on port 1935 for live video streaming. This guide walks you through sharing your Real-Time Messaging Protocol (RTMP) streaming server using an ngrok TCP endpoint. ## Start a TCP endpoint Start a TCP endpoint on port `1935` (the default RTMP port). ```bash theme={null} ngrok tcp 1935 ``` ## Use the endpoint in your RTMP configuration Once the ngrok agent is running, use the TCP endpoint in your configuration where you would normally use the IP address. For example: ```bash theme={null} rtmp://your-ngrok-tcp-address:ngrok-port/live/ ``` Streaming data can use a lot of bandwidth quickly. If you're on a free plan, you may run into the [data transfer limits](../pricing-limits/free-plan-limits#limits-and-licensing) and need to upgrade to a paid plan. # Using ngrok with SMTP Mail Servers Source: https://ngrok.com/docs/using-ngrok-with/smtp Learn how to connect to your local SMTP mail server using ngrok TCP endpoints on ports 25, 465, 587, or 2525. The common port for the SMTP protocol used by most mail servers is `25` for insecure traffic and `465` for secure traffic. You can use an ngrok TCP endpoint to connect to a local mail server with the following command. ```bash theme={null} ngrok tcp 25 ``` ```bash theme={null} ngrok tcp 465 ``` ngrok works with whatever port your mail server is running on, including `587` or `2525`. Just adjust the command above accordingly. TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. # Using ngrok with SSH Source: https://ngrok.com/docs/using-ngrok-with/ssh Learn how to use ngrok TCP endpoints to access your local SSH server remotely on port 22 with proper port specification. This guide shows you how to expose your local SSH server with an ngrok TCP endpoint and connect from a remote client. ## Expose your SSH server ngrok's TCP endpoints work well for SSH traffic. Start a TCP endpoint to port `22` and you're ready to connect. ```sh theme={null} ngrok tcp 22 ``` TCP endpoints are only available on a free plan after [adding a valid payment method](https://dashboard.ngrok.com/settings#id-verification) to your account. ## Connect via the ngrok TCP address When connecting through the ngrok TCP address, specify the port separately. ```sh theme={null} ssh -p PORT user@NGROK_TCP_ADDRESS ``` If you're getting a failed connection, the cause is usually a misconfiguration on the server you're connecting to or the credentials you're using. # Using ngrok with Virtual hosts (MAMP, WAMP, etc) Source: https://ngrok.com/docs/using-ngrok-with/virtualHosts Learn how to expose virtual hosts from MAMP, WAMP, and similar web servers using ngrok's host header rewriting feature. Popular web servers such as MAMP and WAMP rely on a technique popularly referred to as 'Virtual Hosting' which means that they consult the HTTP request's `Host` header to determine which of their multiple sites they should serve. To expose a site like this it is possible to ask ngrok to rewrite the `Host` header of all requests to match what your web server expects. You can do this by using the `--host-header` option (see: [Rewriting the Host header](/docs/gateway/endpoints/http/#rewrite-host-header)) to pick which virtual host you want to target. For example, to route to your local site `myapp.dev`, you would run: ```bash theme={null} ngrok http --host-header=myapp.dev 80 ``` You can also use `rewrite` to tell ngrok to use the local address hostname. ```bash theme={null} ngrok http --host-header=rewrite 80 ``` # Using ngrok with Visual Studio / IIS Express Source: https://ngrok.com/docs/using-ngrok-with/visualStudio Learn how to use the ngrok Visual Studio extension to add ngrok support directly into your Visual Studio development workflow. This guide explains how to add ngrok support to your Visual Studio workflow using a Visual Studio extension. It covers installing the extension and connecting it to your ngrok account. ## What you'll need * Visual Studio installed. * An [ngrok account](https://ngrok.com/signup). * Your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). ## 1. Install the Visual Studio extension Install the [ngrok extension for Visual Studio](https://marketplace.visualstudio.com/items?itemName=DavidProthero.NgrokExtensions). This extension is community-maintained rather than published by ngrok, so its setup and behavior follow the extension's own documentation. ## 2. Configure the extension Open Visual Studio and configure the extension with your ngrok authtoken. Follow the extension's documentation for the exact configuration fields and workflow. ## 3. Start an ngrok tunnel from Visual Studio Start a tunnel using the extension. Use the generated URL to reach your local app through ngrok. # Using ngrok with VSCode Source: https://ngrok.com/docs/using-ngrok-with/vsCode Learn how to use the ngrok VSCode extension to add ngrok support directly into your VSCode development environment. This guide explains how to add ngrok support to your VS Code workflow using a VS Code extension. It covers installing the extension and connecting it to your ngrok account. ## What you'll need * VS Code installed. * An [ngrok account](https://ngrok.com/signup). * Your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). ## 1. Install the VS Code extension Install the [ngrok extension for VS Code](https://marketplace.visualstudio.com/items?itemName=philnash.ngrok-for-vscode). This extension is community-maintained rather than published by ngrok, so its setup and behavior follow the extension's own documentation. ## 2. Configure the extension Open VS Code and configure the extension with your ngrok authtoken. Follow the extension's documentation for the exact configuration fields and workflow. ## 3. Start an ngrok tunnel from VS Code Start a tunnel using the extension. Use the generated URL to reach your local app through ngrok. # Using ngrok with Websockets Source: https://ngrok.com/docs/using-ngrok-with/websockets ngrok HTTP endpoints support WebSockets without any special configuration. WebSocket endpoints work through ngrok's HTTP endpoints without any changes. However, there is currently no support for introspecting WebSockets beyond the initial `101 Switching Protocols` response. If you want to use WebSockets with HTTP, you only need one ngrok endpoint: you can start a server where listeners for both protocols share a single port. ## What you'll need * Node.js installed on your machine. * An [ngrok account](https://dashboard.ngrok.com/signup). * Your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken). ## 1. Install dependencies ```sh theme={null} npm install ws ``` ## 2. Create the server Create a `server.js` file: ```js theme={null} import { createServer } from 'http'; import { WebSocketServer } from 'ws'; const server = createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('OK\n'); }); const wss = new WebSocketServer({ server }); wss.on('connection', (ws) => { ws.on('message', (data) => ws.send(`Echo: ${data}`)); ws.send('Hello from WebSocket'); }); server.listen(8000); ``` ## 3. Start ngrok A single ngrok endpoint handles traffic for both protocols. ```sh theme={null} ngrok http 8000 ``` Connect to your WebSocket using the `wss://` scheme: ```js theme={null} const ws = new WebSocket('wss://'); ```