http-request

Calls another URL from the policy — an auth service, a feature flag, an internal endpoint — so later rules can act on the answer.

Phaseson_http_requeston_http_response
Terminating No — the next action runs after it.
Cost 1 Traffic Policy unit per run · not on the Free plan

Example

policy.yaml
on_http_request:
  - actions:
      - type: http-request
        config:
          url: https://auth.example.com/check
          method: POST
          headers:
            content-type: application/json
          body: '{"path": "${req.url.path}", "ip": "${conn.client_ip}"}'
          timeout: 2s
          retry_condition: "last_attempt.res.status_code >= 500"
  - expressions:
      - "actions.ngrok.http_request.res.status_code != 200"
    actions:
      - type: deny
        config:
          status_code: 403

Start an agent endpoint with it: devgate http 8080 --traffic-policy-file policy.yaml, or paste it into a reserved or cloud endpoint’s Traffic Policy tab in the dashboard.

Configuration

Field Type Description
url
required
string A public http(s) URL, or one of your internal endpoints (https://name.internal/path). May use ${…}.
method string GET, PUT, POST, PATCH, DELETE or OPTIONS. Default: GET.
headers map Up to 10 request headers. Values may use ${…}.
query_params list of {key, value} Up to 32 query parameters added to the URL. Values may use ${…}.
body string The request body, e.g. JSON for a POST. May use ${…}.
timeout duration The longest the whole action may take, retries and redirects included: 1s to 30s. Default: 3s.
max_redirects integer Redirects to follow, 0 to 100. With 0 the 3xx answer itself is the result. Default: 0.
retry_condition expression A CEL expression checked after each attempt; when true the call is made again, up to 3 more times. It sees attempts, last_attempt.req, last_attempt.res and last_attempt.error.
on_error string continue moves on when the call fails; halt answers the visitor 502 and stops the policy. Default: continue.

Result variables

After it runs, later rules can read what it decided:

  • actions.ngrok.http_request.res — The last answer: status_code, header (lowercase names, values as lists), body, resolved_ip and time_ms.
  • actions.ngrok.http_request.req — What was sent: method, url, header and body.
  • actions.ngrok.http_request.attempts — One entry per attempt: response_status_code, response_header, response_time_ms, resolved_ip.
  • actions.ngrok.http_request.error.code / error.message — Why the call failed, e.g. timeout or forbidden_address; empty when it didn’t.

Behaviour

  • The answer isn’t sent to the visitor: later rules read it in actions.ngrok.http_request.* and decide.
  • For safety devgate never calls private networks, loopback (127.0.0.1, localhost), link-local addresses or cloud metadata (169.254.169.254) — checked when it connects, after DNS, and on every redirect.
  • An internal endpoint is reached through its agent; its own Traffic Policy doesn’t run for these calls.
  • The answer is read up to 256 KB; a bigger one fails the call (response_too_large).
  • A timeout, a refused connection, a blocked address or too many redirects sets error.code and error.message; with on_error: halt the visitor gets 502 and nothing after the action runs.
  • Each call carries User-Agent devgate-traffic-policy/1.0, X-Forwarded-For with the visitor’s IP and devgate-req-type: http-request (header names are case-insensitive). Calls nested more than 3 deep are refused, so endpoints calling each other can’t loop.
  • Retries wait 100, 200 and 400 ms. A call costs 1 unit however many attempts it takes.

Other actions

add-headers remove-headers compress-response basic-auth oauth openid-connect jwt-validation custom-response deny redirect url-rewrite restrict-ips rate-limit circuit-breaker verify-webhook set-vars log close-connection forward-internal