Skip to main content

Rule Gallery

Explore a curated collection of example configurations spanning from common to unconventional use cases for the Traffic Policy module.

A number of these examples come from a longer article about how ngrok makes policy management accessible to developers, including a simple Go-based application for testing these and other configurations.

See the following categories for specific expressions and actions:

Authentication

Add JWT authentication and key-based rate limiting

Building from our Auth0 guide, these rules also add rate limiting based on your consumers' JWTs.

# snippet
---
on_http_request:
- expressions: []
name: Add JWT authentication and rate limiting
actions:
- type: rate-limit
config:
name: Only allow 30 requests per minute
algorithm: sliding_window
capacity: 30
rate: 60s
bucket_key:
- req.headers['x-api-key']
- type: jwt-validation
config:
issuer:
allow_list:
- value: https://<YOUR-AUTH-PROVIDER>
audience:
allow_list:
- value: <YOUR-NGROK-DOMAIN>
http:
tokens:
- type: jwt
method: header
name: Authorization
prefix: "Bearer "
jws:
allowed_algorithms:
- RS256
keys:
sources:
additional_jkus:
- https://<YOUR-AUTH-PROVIDER>/.well-known/jwks.json
on_http_response: []

Rate limiting

Rate limit for specific endpoint

This rule applies rate limiting of 30 requests per second to the endpoint /api/videos.

# snippet
---
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

Rate limit API consumers based on authentication status

Create a low rate limit for unauthenticated (likely free) users, while allowing authenticated users a higher level of capacity.

# snippet
---
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
on_http_response: []

Rate limit API consumers based on pricing tiers

Using a naming scheme in your upstream servers, and API calls using a tier header, you can quickly customize access to your API based on any number of pricing tiers.

# snippet
---
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: Bronze 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
on_http_response: []

Block unwanted requests

Disallow bots and crawlers with a robots.txt

This rule returns a custom response with a robots.txt file to deny search engine or AI crawlers on all paths.

# snippet
---
on_http_request:
- name: Add `robots.txt` to deny all bots and crawlers
expressions:
- req.url.contains('/robots.txt')
actions:
- type: custom-response
config:
status_code: 200
content: "User-agent: *\r\nDisallow: /"
headers:
content-type: text/plain

You can also extend the expression above to create specific rules for crawlers based on their user agent strings, like ChatGPT-User and GPTBot.

# snippet
---
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
content: "User-agent: ChatGPT-User\\r\\nDisallow: /"
headers:
content-type: text/plain

Block bots and crawlers by user agent

In addition to, or instead of, denying bots and crawlers with a robots.txt file, you can also take action on only incoming requests that contain specific strings in the req.user_agent request variable.

You can extend the expression to include additional user agents by extending (chatgpt-user|gptbot) like so: (chatgpt-user|gptbot|anthropic|claude|any|other|user-agent|goes|here).

# snippet
---
on_http_request:
- name: Block specific bots by user agent
expressions:
- req.user_agent.matches('(?i).*(chatgpt-user|gptbot)/\\d+.*')
actions:
- type: deny
config:
status_code: 404

Deny non-GET requests

This rule denies all inbound traffic that is not a GET request.

# snippet
---
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.

# snippet
---
on_http_request:
- expressions:
- "!('authorization' in req.headers)"
actions:
- type: custom-response
config:
status_code: 401
content: Unauthorized

Block traffic from specific countries

Remain compliant with data regulations or sanctions by blocking requests originating from one or more countries using their respective ISO country codes.

# snippet
---
on_http_request:
- expressions:
- conn.geo.country_code in ['<COUNTRY-01>', '<COUNTRY-02>']
name: Block traffic from unwanted countries
actions:
- type: custom-response
config:
status_code: 401
content: Unauthorized request due to country of origin
on_http_response: []

Limit request sizes

Prevent excessively large user uploads, like text or images, that might cause performance or availability issues for your upstream service.

# snippet
---
on_http_request:
- expressions:
- req.method == 'POST' || req.method == 'PUT'
- req.content_length >= 1000
name: Block POST/PUT requests of excessive length
actions:
- type: custom-response
config:
status_code: 400
content: "Error: content length"
on_http_response: []

Other

User agent filtering

We deliver tailored content to Microsoft Edge users by examining the User-Agent header for the case-insensitive string (?i)edg/ succeeded by digits \d. To see how this works in practice, explore the following regex101 demonstration.

To ensure correct decoding from YAML/JSON, it's necessary to properly escape the \d sequence. In YAML, if your string is not enclosed in quotes, use a single escape: \\d. However, when your string is wrapped in quotes, either in YAML or JSON, you need to double-escape: \\\\d for accurate decoding.

# snippet
---
on_http_request:
- expressions:
- "'user-agent' in req.headers"
- size(req.headers['user-agent'].filter(x, x.matches('(?i).*Edg/\\d+.*')))
> 0
actions:
- type: custom-response
config:
status_code: 200
content: Hello Edge User!

Deprecate an API version

By include a 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.

# snippet
---
on_http_request:
- expressions:
- "'2' in req.headers['X-Api-Version']"
name: Deprecate API v2
actions:
- type: custom-response
config:
status_code: 400
content: '{"error":{"message":"Version 2 of the API is no longer supported. Use
Version 3 instead."}}'
on_http_response: []

Manipulate request headers

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.

# snippet
---
on_http_request:
- expressions: []
name: Add headers to requests
actions:
- type: add-headers
config:
headers:
is-ngrok: "1"
country: ${.ngrok.geo.country_code}
on_http_response: []

Add compression

Quickly ensure all JSON responses are compressed en route to your API consumer. If your upstream service already handles compression, ngrok skips this step.

# snippet
---
on_http_request: []
on_http_response:
- expressions: []
name: Add compression
actions:
- type: compress-response
config:
algorithms:
- gzip
- br
- deflate
- compress

Enforce TLS version

Prevent obsolete and potentially vulnerable browsers, SDKs, or CLI tools like curl from attempting to access your API.

# snippet
---
on_http_request:
- expressions:
- conn.tls.version < '1.3'
name: Reject requests using old TLS versions
actions:
- type: custom-response
config:
status_code: 401
content: "Unauthorized: TLS version too old"
on_http_response: []

Log unsuccessful events

Connect your API to ngrok's event logging system for smarter troubleshooting of your API gateway and upstream services.

# snippet
---
on_http_request: []
on_http_response:
- expressions:
- res.status_code < '200' && res.status_code >= '300'
name: Log unsuccessful requests
actions:
- type: log
config:
metadata:
message: Unsuccessful request
edge_id: <YOUR-NGROK-DOMAIN>
success: false