Documentation Index
Fetch the complete documentation index at: https://ngrok.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
Traffic Policy uses CEL expressions and 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 to send traffic to internal endpoints.
See the action reference for configuration details.
Routing methods
| Method | Use case |
|---|
| By subdomain | Multi-tenant apps, customer-specific routing |
| By path | API versioning, microservice routing |
| By header | Customer routing, feature flags, A/B testing |
| By cookie | Session-based routing, sticky sessions |
| By query parameter | Debug routing, testing environments |
| By geographic location | Regional routing, compliance |
| By IP intelligence | Bot detection, threat-based routing |
| By device type | Mobile vs desktop experiences |
| By client certificate | mTLS-based service routing |
By subdomain
Route requests from https://*.example.com to corresponding internal services.
Extract subdomain dynamically
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
Route specific subdomains
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
By path
Route requests to different services based on URL path patterns.
API versioning
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
Microservice routing
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
Route requests based on custom header values.
Use this for customer-specific routing, feature flags, or A/B testing.
Customer-specific routing
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
Feature flag routing
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
By cookie
Route based on cookie values for session-based routing or sticky sessions.
Route by session cookie
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
A/B testing with cookies
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
By query parameter
Route based on query string parameters.
Use this for debug modes or testing environments.
Debug routing
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
Environment routing
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
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 for available geo fields.
Route by country
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
Route by continent
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
By IP Intelligence variables
Use IP Intelligence 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.
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
Route anonymous proxy traffic
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
By device type
Route mobile users to a mobile-optimized service using user agent variables.
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
By client certificate
When Mutual TLS (mTLS) is enabled, route requests based on client certificate details like the common name.
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
Combine multiple conditions
Create complex routing logic by combining multiple conditions.
Route by path and method
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
Route by content type
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