API Reference

Upstream headers, condition paths, operators, response bodies, and backend integration.

API Reference — Upstream Headers, Variables & Integration

The Quayel Gateway enriches every request with IP intelligence, security decisions, and metadata. This data is available through:

  1. Upstream request headers — forwarded to your backend servers
  2. Condition paths — used in firewall, rate limit, cache, and LB rules
  3. JSONL log fields — recorded in access logs
  4. Response headers — sent back to clients

Upstream request headers

These headers are automatically added to requests forwarded to backend servers.

Standard proxy headers

HeaderValueDescription
X-Forwarded-Forclient_ipClient IP appended to existing chain
X-Forwarded-Protohttp / httpsOriginal request scheme
X-Forwarded-HosthostOriginal request host

Client IP resolution

The gateway resolves the real client IP based on the client_ip configuration:

Config sourceBehavior
peer_ipUses direct TCP connection IP
headerReads IP from specified header (e.g., CF-Connecting-IP)
header_indexReads IP from Nth entry in comma-separated header (e.g., X-Forwarded-For)

The resolved client_ip is used for all IP intel lookups and rate limiting.

Condition paths

These paths can be used in the left field of any condition (firewall, rate limiting, cache, load balancer).

Request fields

PathTypeDescriptionExample Value
req.host / http.hoststringHost header"api.example.com"
req.path / http.pathstringRequest path + query"/api/v1/users?page=1"
req.method / http.methodstringHTTP method"GET"
req.scheme / http.schemestringRequest scheme"https"
req.ip / http.ipstringResolved client IP"203.0.113.50"
req.peer_ip / http.peer_ipstringDirect connection IP"10.0.0.1"
req.user_agent / http.user_agentstringUser-Agent header"Mozilla/5.0 ..."

IP intelligence fields

PathTypeDescriptionExample Value
ip.countrystringISO 3166-1 alpha-2 country code"US"
ip.regionstringRegion/state code"CA"
ip.citystringCity name"San Francisco"
ip.asnnumberAutonomous System Number16509
ip.as_orgstringASN organization name"AMAZON-02"
ip.providerstringHosting/cloud provider label"Amazon AWS"
ip.is_vpnboolIP is a known VPN providertrue
ip.is_torboolIP is a Tor exit nodefalse
ip.is_datacenterboolIP is from datacenter/hostingtrue
ip.is_proxyboolIP is a known anonymous proxyfalse
ip.is_mobileboolIP is from a mobile networknull
ip.is_residentialboolIP is residentialnull
ip.is_residential_proxyboolIP is a residential proxynull
ip.network_typestringNetwork type classification"datacenter"
ip.risk_scorenumberRisk score (0-100)10
Path PatternTypeDescription
req.header.<name>stringRequest header value
http.header.<name>stringRequest header value
req.header.<name>.jwt.<claim.path>anyDecode JWT from header (strips Bearer ), navigate dotted claim path
req.query.<name>stringQuery parameter value
http.query.<name>stringQuery parameter value
req.cookie.<name>stringCookie value
http.cookie.<name>stringCookie value

Examples:

{ "left": "req.header.X-API-Key", "operator": "exists" }
{ "left": "req.header.authorization.jwt.data.role", "operator": "equals", "value": "admin" }
{ "left": "req.query.page", "operator": "greater_than", "value": 1 }
{ "left": "req.cookie.Session-ID", "operator": "exists" }

Operators reference

OperatorDescriptionLeft TypeValue Type
equalsExact matchanyany
not_equalsNot equalanyany
containsString containsstringstring
not_containsString does not containstringstring
starts_withString prefix matchstringstring
ends_withString suffix matchstringstring
existsValue is not nullany—
not_existsValue is nullany—
inValue is in arrayanyarray
not_inValue is not in arrayanyarray
greater_thanNumeric >numbernumber
greater_than_or_equalNumeric >=numbernumber
less_thanNumeric <numbernumber
less_than_or_equalNumeric <=numbernumber
regexRegular expressionstringstring (regex)

Firewall actions

Action TypeDescriptionExtra Fields
blockReturn error responsestatus (default: 403)
allowContinue pipeline (skip remaining firewall rules)—
redirectReturn redirect responsestatus (default: 302), location
set_request_headerModify request headers before forwardingheaders (map)
set_response_headerModify response headers (reserved)headers (map)

Block response body

{
  "error": "blocked",
  "status": 403,
  "rule_id": "block-tor"
}

Redirect response

HTTP/1.1 302 Found
Location: https://example.com/blocked
Content-Length: 0

Rate limiting

Blocked response body

{
  "error": "rate_limited",
  "status": 429,
  "rule_id": "api-limit",
  "limit": 100,
  "window_seconds": 60
}

Response headers:

HeaderValue
Content-Typeapplication/json
Retry-AfterWindow seconds

Rate limit keys

KeyDescription
req.client_ipResolved client IP
req.peer_ipDirect connection IP
req.header.<name>Header value (e.g., X-API-Key)

Load balancer

No healthy upstream response

{
  "error": "no_healthy_upstream"
}

Status: 503 Service Unavailable

Upstream headers added

HeaderValue
X-Forwarded-ForClient IP (appended to chain)
X-Forwarded-ProtoOriginal scheme (http / https)
X-Forwarded-HostOriginal Host header

TLS / SNI behavior

When target protocol is https:

ScenarioSNI used
tls.sni configuredUses configured SNI
Target is IP + visitor has Host headerUses visitor's Host
Target is IP + no Host headerUses target IP as SNI
Target is hostnameUses target hostname

Cache response headers

HeaderValueDescription
Quayel-Cache-StatusNONE / HIT / MISS / EXPIRED / BYPASS / STALE / REVALIDATINGCache status

Cache status values

ValueDescription
NONENo cache rule matched
HITResponse served from fresh cache
MISSCache miss, fetched from upstream
EXPIRED(Reserved)
BYPASSCache bypassed by rule
DYNAMIC(Reserved)
STALEServed stale data (lock race fallback — another request was already revalidating)
REVALIDATINGServed stale data while another request refreshes the cache (or this request is the one refreshing)

Cache revalidation

When a cached entry becomes stale, the gateway uses singleflight revalidation via per-key locks:

  • First request that finds stale data acquires the lock and goes to origin like a normal cache miss
  • Concurrent requests see the lock is held and serve stale cached data with Quayel-Cache-Status: REVALIDATING
  • When the first request completes, the lock releases and subsequent requests see fresh cache data

This prevents cache stampedes — only one request per key goes to origin, regardless of how many concurrent requests arrive.

See Cache plugin — Singleflight revalidation for detailed flow diagrams.

JSONL log fields

See Logging for the complete log format.

Key fields for monitoring

FieldPathDescription
Client IP.client_ipResolved real client IP
Country.ip_intel.countryGeoIP country code
ASN.ip_intel.asnAutonomous System Number
Provider.ip_intel.providerHosting/cloud provider label
VPN.ip_intel.is_vpnVPN detection
Tor.ip_intel.is_torTor exit detection
Datacenter.ip_intel.is_datacenterDatacenter detection
Risk Score.ip_intel.risk_score0-100 risk score
Firewall Match.firewall.matchedAny firewall rule matched
Rate Limited.rate_limit.blockedRequest was rate limited
Cache Status.cache.statusHIT/MISS/REVALIDATING/STALE/BYPASS/NONE
LB Target.load_balancer.targetSelected upstream
Total Time.timings_us.total_usTotal processing time (μs)

Plugin pipeline order

Plugins execute in this fixed order:

1. IP Intelligence     (always runs)
2. Firewall            (can short-circuit)
3. Rate Limiting       (can short-circuit)
4. Load Balancer       (can short-circuit — selects upstream target)
5. Cache               (can short-circuit — uses upstream target for revalidation)
6. Logging             (always runs)

Short-circuit behavior

PluginCan Short-circuit?When?
IP IntelNoAlways continues
FirewallYesBlock, redirect, or set-header action
Rate LimitYesLimit exceeded → 429
CacheYesCache HIT → serve cached response; REVALIDATING/STALE → serve stale cached data
Load BalancerYesNo healthy upstream → 503

Example: complete backend integration

When your backend receives a request through Quayel, it gets:

GET /api/v1/users HTTP/1.1
Host: api.example.com
X-Forwarded-For: 203.0.113.50
X-Forwarded-Proto: https
X-Forwarded-Host: api.example.com
User-Agent: Mozilla/5.0 ...

The access log records:

{
  "timestamp": "2026-10-05T17:48:56.973054084+00:00",
  "gateway_id": "gw-prod-01",
  "request_id": "c30dc8f2-7e2d-474b-979f-23ec1eb2e244",
  "client_ip": "203.0.113.50",
  "peer_ip": "203.0.113.50",
  "client_ip_source": "X-Forwarded-For",
  "method": "GET",
  "scheme": "https",
  "host": "api.example.com",
  "path": "/api/v1/users",
  "response_status": 200,
  "ip_intel": {
    "country": "US",
    "region": "CA",
    "asn": 16509,
    "network_type": "datacenter",
    "is_vpn": false,
    "is_proxy": false,
    "is_datacenter": true,
    "is_mobile": null,
    "is_tor": false,
    "risk_score": 10
  },
  "firewall": {
    "matched": false,
    "rule_id": null,
    "action": null
  },
  "rate_limit": {
    "matched": false,
    "rule_id": null,
    "blocked": false,
    "remaining": null
  },
  "cache": {
    "rule_id": null,
    "status": "NONE",
    "key_hash": null
  },
  "load_balancer": {
    "lb_id": "default",
    "algorithm": "RoundRobin",
    "target_id": "backend-1",
    "target": "10.0.0.1"
  },
  "termination_phase": null,
  "timings_us": {
    "ip_intel_us": 42,
    "firewall_us": 3,
    "rate_limit_us": 0,
    "cache_lookup_us": null,
    "load_balancer_us": 7,
    "upstream_connect_us": null,
    "upstream_ttfb_us": null,
    "total_us": 1091
  }
}

Environment variables

VariableDefaultDescription
RUST_LOGinfoLog level (debug, info, warn, error)
QUAYEL_IP_INTEL_CACHE_SIZE65536LRU cache size for IP lookups
Copyright © 2026