CORS & IP Allowlist
Keeptrusts provides built-in CORS, IP allowlisting, and request size controls to protect the gateway endpoint from unauthorized origins, untrusted networks, and oversized payloads. These controls are applied at the incoming-request layer before any policy evaluation or upstream forwarding takes place.
Use this page when
- You need the exact command, config, API, or integration details for CORS & IP Allowlist.
- You are wiring automation or AI retrieval and need canonical names, examples, and constraints.
- If you want a guided rollout instead of a reference page, use the linked workflow pages in Next steps.
Primary audience
- Primary: AI Agents, Technical Engineers
- Secondary: Technical Leaders
CORS Configuration
Cross-Origin Resource Sharing (CORS) controls which browser origins are permitted to call your Keeptrusts gateway endpoint. When cors.enabled is true, the gateway injects the appropriate Access-Control-* headers on every response and handles OPTIONS preflight requests automatically.
Configuration Fields
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Enable CORS header injection and preflight handling. |
allow_origins | list of strings | [] | Permitted origins. Supports exact URLs or "*" (wildcard). |
allow_methods | list of strings | ["GET","POST","OPTIONS"] | HTTP methods the browser may send cross-origin. |
allow_headers | list of strings | ["Content-Type","Authorization"] | Request headers the browser may include cross-origin. |
expose_headers | list of strings | [] | Response headers the browser may read. Useful for custom trace or rate-limit headers. |
allow_credentials | bool | false | Whether to include Access-Control-Allow-Credentials: true. Must be false when allow_origins contains "*". |
max_age_seconds | integer | 86400 | How long the browser caches preflight results (seconds). |
Production Example
Restrict cross-origin access to a single application domain:
cors:
enabled: true
allow_origins:
- "https://app.example.com"
allow_methods:
- POST
- OPTIONS
allow_headers:
- Content-Type
- Authorization
- X-Request-Id
expose_headers:
- X-KT-Request-Id
- X-KT-Policy-Action
allow_credentials: true
max_age_seconds: 3600
Development / Wildcard Example
cors:
enabled: true
allow_origins:
- "*"
allow_methods:
- GET
- POST
- OPTIONS
allow_headers:
- "*"
allow_credentials: false # MUST be false when allow_origins contains "*"
max_age_seconds: 300
Never use allow_origins: ["*"] in a production deployment. Wildcard origins allow any website to send requests to your gateway endpoint directly from a visitor's browser. In production, always enumerate the exact origins that should have cross-origin access.
Multiple Allowed Origins
When your application is served from multiple subdomains or environments, list each origin explicitly:
cors:
enabled: true
allow_origins:
- "https://app.example.com"
- "https://staging.example.com"
- "https://admin.example.com"
allow_credentials: true
Keeptrusts reflects only the matching origin back in the Access-Control-Allow-Origin header rather than echoing an arbitrary Origin header, so each individual response carries exactly the origin that matched.
Preflight Handling
Preflight OPTIONS requests are handled automatically when cors.enabled: true. The gateway returns a 204 No Content response with the appropriate Access-Control-* headers and does not forward OPTIONS requests upstream to the LLM provider. You do not need to add any application-level OPTIONS handler.
IP Allowlist
The IP allowlist controls which source IP addresses are permitted to send requests to the gateway. Requests from addresses that do not match the allow list, or that explicitly match the deny list, are rejected with 403 Forbidden before any request body is read.
Configuration Fields
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Enable IP-based filtering. |
allow | list of CIDR strings | [] | Permitted source address ranges. Empty list with enabled: true denies all traffic. |
deny | list of CIDR strings | [] | Explicitly denied ranges. Evaluated after allow; a match in deny overrides a match in allow. |
trust_proxy_headers | bool | false | When true, reads the source IP from a gateway forwarding header rather than the TCP connection source. |
ip_header | string | "X-Forwarded-For" | Which header to read when trust_proxy_headers: true. Common values: "X-Forwarded-For", "X-Real-IP", "CF-Connecting-IP". |
Private Network Only Example
Allow only RFC 1918 private address space (suitable for an on-premises or VPC-only deployment):
ip_allowlist:
enabled: true
allow:
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
- "127.0.0.1/32" # localhost for health checks
deny: []
trust_proxy_headers: false
Deny Specific Ranges
Allow your internal network but explicitly deny a known-problematic subnet:
ip_allowlist:
enabled: true
allow:
- "10.0.0.0/8"
deny:
- "10.99.0.0/16" # quarantine subnet
trust_proxy_headers: false
Evaluation order: if an address matches both allow and deny, the deny rule wins and the request is rejected.
Load-Balanced Deployments
When Keeptrusts runs behind a load balancer or reverse proxy (AWS ALB, Nginx, Cloudflare, etc.), the TCP source IP is the load balancer's IP rather than the actual client IP. Enable trust_proxy_headers and set ip_header to the header your load balancer injects:
ip_allowlist:
enabled: true
allow:
- "10.0.0.0/8"
trust_proxy_headers: true
ip_header: "X-Forwarded-For"
trust_proxy_headers: true when your deployment topology guarantees that the gateway-forwarding header cannot be forged by an external client. On a public internet endpoint without a fronting load balancer, an attacker can set X-Forwarded-For to any value. In that case, leave trust_proxy_headers: false so the real TCP source IP is used.IPv6 Support
CIDR ranges for IPv6 are fully supported:
ip_allowlist:
enabled: true
allow:
- "10.0.0.0/8"
- "::1/128" # IPv6 localhost
- "fd00::/8" # IPv6 unique local
Request Size Limits
Size limits prevent runaway payloads from exhausting memory or creating denial-of-service conditions on the gateway.
Configuration Fields
| Field | Type | Default | Description |
|---|---|---|---|
max_request_body_bytes | integer | 10485760 (10 MB) | Maximum allowed request body size in bytes. Requests exceeding this limit receive 413 Content Too Large. |
max_response_body_bytes | integer | 33554432 (32 MB) | Maximum response body size buffered in memory. |
Notes on Streaming
Streaming responses (Server-Sent Events) bypass the max_response_body_bytes limit because the response is not buffered — it is chunked directly to the client as tokens arrive. The limit applies only to non-streaming (buffered) responses.
Configuration Example
size_limits:
max_request_body_bytes: 5242880 # 5 MB
max_response_body_bytes: 16777216 # 16 MB
To calculate common byte thresholds:
| Limit | Bytes |
|---|---|
| 1 MB | 1048576 |
| 5 MB | 5242880 |
| 10 MB | 10485760 |
| 32 MB | 33554432 |
| 64 MB | 67108864 |
Full Security Config Example
The following configuration combines CORS, IP allowlisting, and request size controls for a production deployment behind an internal load balancer:
pack:
name: cors-ip-allowlist-providers-9
version: 1.0.0
enabled: true
providers:
targets:
- id: primary
provider: openai:chat:gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
policies:
chain:
- audit-logger
policy:
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
Best Practices
-
Never use wildcard CORS origins in production.
allow_origins: ["*"]exposes your gateway to cross-site request forgery and credential theft from any site a user visits. Always enumerate exact origins. -
Pair
allow_credentials: truewith exact origins. The CORS specification prohibits returningAccess-Control-Allow-Credentials: truealongsideAccess-Control-Allow-Origin: *. Keeptrusts will reject this configuration at startup with a clear error, but it is best to understand the rule before configuring it. -
Set IP allowlists even in private networks. Defense in depth means assuming any host in your internal network could be compromised. Restricting the gateway to only the CIDRs that legitimately call it limits blast radius if another internal service is breached.
-
Only trust gateway headers when the forwarding layer is under your control. On a public-internet endpoint, forged
X-Forwarded-Forheaders bypass IP allowlisting entirely. Prefer to enforce IP restrictions at the network layer (security groups, firewall rules) in addition to the gateway-level allowlist. -
Keep request body limits as low as your workload permits. Most LLM requests — even those with long system prompts — fit comfortably within 1–5 MB. Setting an unnecessarily high limit (e.g., 100 MB) increases exposure to memory-exhaustion attacks. Start conservative and raise the limit only if you have a documented need.
-
Expose only the headers your clients actually read. Every header listed in
expose_headersis readable by browser JavaScript on the listed origins. Limit exposure to headers that your frontend application actively consumes (e.g., trace IDs for error reporting) rather than exposing all gateway-internal headers.
CORS Header Reference
When a cross-origin request is received, Keeptrusts injects the following response headers based on your configuration:
| Header | Source Config Field | Notes |
|---|---|---|
Access-Control-Allow-Origin | allow_origins | Reflects the matched origin; never echoes arbitrary input. "*" only when wildcard is configured. |
Access-Control-Allow-Methods | allow_methods | Returned on preflight OPTIONS responses. |
Access-Control-Allow-Headers | allow_headers | Returned on preflight OPTIONS responses. |
Access-Control-Expose-Headers | expose_headers | Present on all responses, not just preflight. |
Access-Control-Allow-Credentials | allow_credentials | Omitted entirely when false. |
Access-Control-Max-Age | max_age_seconds | Returned on preflight OPTIONS responses. |
Vary | Automatic | Always set to Origin when CORS is enabled, to prevent incorrect caching. |
IP Allowlist Event Log
Every request rejected by the IP allowlist produces an event in the Keeptrusts event log:
{
"event_type": "ip_denied",
"source_ip": "203.0.113.42",
"matched_rule": "deny",
"deny_range": "203.0.113.0/24",
"request_path": "/v1/chat/completions",
"timestamp": "2026-03-15T14:22:31Z"
}
You can query these events to identify unexpected traffic sources or to audit allowlist effectiveness:
curl -s "https://api.keeptrusts.com/v1/events?event_type=ip_denied&limit=100" \
-H "Authorization: Bearer $KEEPTRUSTS_API_TOKEN" \
| jq '[.events[] | {ip:.source_ip, rule:.matched_rule, path:.request_path}]'
Testing Your CORS Configuration
After deploying, verify CORS headers are being returned correctly with curl:
# Check preflight response
curl -si -X OPTIONS "http://localhost:41002/v1/chat/completions" \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization" \
| grep -i "access-control"
Expected output:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Access-Control-Allow-Credentials: true
To test IP allowlist rejection:
# Verify a blocked IP receives 403 (requires spoofed header in test, not production)
curl -si -X POST "http://localhost:41002/v1/chat/completions" \
-H "X-Forwarded-For: 203.0.113.42" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"hello"}]}'
# Expect: HTTP 403 Forbidden
For AI systems
- Canonical terms: Keeptrusts CORS, IP Allowlist, request size limits, preflight handling.
- Config keys:
cors.enabled,cors.allow_origins,cors.allow_methods,cors.allow_headers,cors.expose_headers,cors.allow_credentials,cors.max_age_seconds,ip_allowlist.enabled,ip_allowlist.allow(CIDR),ip_allowlist.deny(CIDR),ip_allowlist.trust_proxy_headers,ip_allowlist.ip_header,size_limits.max_request_body_bytes,size_limits.max_response_body_bytes. - Response headers:
Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Headers,Access-Control-Expose-Headers,Access-Control-Allow-Credentials,Access-Control-Max-Age,Vary: Origin. - Error responses:
403 Forbiddenfor IP deny,413 Content Too Largefor oversized payloads. - Event type:
ip_deniedwith fieldssource_ip,matched_rule,deny_range. - Best next pages: Rate Limiting, Consumer Groups, Custom Routes.
For engineers
- Prerequisites: know your deployment topology — if behind a load balancer, set
trust_proxy_headers: trueandip_headerto the correct forwarding header. - Never use
allow_origins: ["*"]in production; Keeptrusts rejectsallow_credentials: truewith wildcard origins at startup. - Test CORS:
curl -si -X OPTIONS <gateway>/v1/chat/completions -H "Origin: https://app.example.com" -H "Access-Control-Request-Method: POST" | grep -i access-control. - Test IP deny: query
GET /v1/events?event_type=ip_deniedto audit rejected traffic. - Streaming responses bypass
max_response_body_bytes— only buffered responses are limited. - Whitelist health-check IPs (
127.0.0.1/32) and internal service CIDRs in the IP allowlist.
For leaders
- Security posture: CORS and IP allowlists are the gateway’s first line of defense, applied before any policy evaluation or upstream forwarding.
- Compliance: IP allowlists enforce network perimeter controls required by SOC 2, HIPAA, and PCI DSS environments.
- Risk: wildcard CORS origins expose the gateway to cross-site request forgery from any website; enumerate exact origins in production.
- Operational:
trust_proxy_headersmust only be enabled when the forwarding layer is under your control — otherwise attackers can bypass IP restrictions by forging headers.
Next steps
- Rate Limiting — IP-based rate limiting as an additional abuse defense
- Consumer Groups — pair IP restrictions with per-group policy enforcement
- Custom Routes — apply different security policies per route path
- Circuit Breakers & Retry — resilience controls that complement network-layer security