The Config File
All SBproxy configuration lives in a YAML file called sb.yml. It has two top-level keys:
proxy- Server settings (bind ports, TLS, global options)origins- A map of hostnames to origin configurations
proxy:
http_bind_port: 8080
https_bind_port: 8443
origins:
"app.example.com":
action:
type: proxy
url: https://backend.example.com
Each key under origins is a hostname. Wildcard hostnames are supported: *.example.com matches any single-level subdomain (e.g., api.example.com, app.example.com) but not deeper subdomains (e.g., deep.sub.example.com).
An origin needs at minimum a hostname and an action.
Origins
An origin is a hostname-to-backend mapping with optional security, caching, and transformation layers. Here are all the top-level fields an origin can have, grouped by purpose:
| Group | Fields |
|---|---|
| Action (required) | action |
| Security | authentication, policies |
| Request pipeline | request_modifiers, request_rules, forward_rules, must_match_rules, allowed_methods |
| Response pipeline | response_modifiers, transforms, response_cache |
| Hooks and values | on_request, on_response, variables |
| Session | session_config |
| Error handling | error_pages, fallback_origin |
| Transport | cors, compression, proxy_headers, streaming_proxy_config, force_ssl |
The key structural rule: authentication, policies, request_modifiers, response_modifiers, transforms, forward_rules, and response_cache are all siblings of action at the origin level. They are never nested inside the action block.
Action
The action block is required. It determines what happens to the request. Every action has a type field, and the remaining fields depend on the type.
The most common action types:
proxy- Reverse proxy to an upstream URLai_proxy- Multi-provider LLM gateway with OpenAI-compatible APIstatic- Return a fixed response (health checks, maintenance pages)redirect- HTTP redirect (301, 302, 307, 308), with optional bulk-redirect tablesload_balancer- Distribute across multiple backendswebsocket- WebSocket proxying with connection poolinggraphql- GraphQL-aware proxy with query validation and depth limitinggrpc- gRPC proxying with HTTP/2mcp- Model Context Protocol server for LLM tool integrationa2a- Google Agent-to-Agent protocolstorage- Serve files from cloud storage (S3, GCS, Azure)mock- Synthetic responses with simulated delay for testingecho- Return request details for debugging
Proxy action
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
timeout: 30s
skip_tls_verify_host: false
AI proxy action
origins:
"ai.example.com":
action:
type: ai_proxy
providers:
- name: openai
api_key: sk-your-openai-key
models: [gpt-4o, gpt-4o-mini]
- name: anthropic
api_key: sk-ant-your-anthropic-key
models: [claude-sonnet-4-20250514]
default_model: gpt-4o-mini
routing:
strategy: fallback_chain
For production, use secrets management instead of inline keys.
Static action
origins:
"health.example.com":
action:
type: static
status_code: 200
content_type: application/json
json_body: {"status": "ok"}
See Action Types for the full reference on all action types and their fields.
Authentication
The authentication block defines how requests are authenticated. It runs after policy enforcement. Available types:
api_key- Static keys checked via header or query parameterbasic_auth- Username/password pairsbearer- Token-based authentication via Authorization headerjwt- JSON Web Tokens with JWKS auto-discovery and key rotationforward_auth- Delegate to an external authentication servicedigest- HTTP Digest authenticationbot_auth- Web Bot Auth (RFC 9421 HTTP Message Signatures) for verifying signed AI agents
Authenticated claims are available as template variables via {{auth.data.*}} in later stages.
API key authentication
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
authentication:
type: api_key
api_keys:
- my-secret-key-1
- my-secret-key-2
curl -H "Host: api.example.com" -H "X-API-Key: my-secret-key-1" http://localhost:8080/
JWT authentication
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
authentication:
type: jwt
jwks_url: https://auth.example.com/.well-known/jwks.json
issuer: https://auth.example.com
audience: my-api
Basic auth
origins:
"admin.example.com":
action:
type: proxy
url: https://backend.example.com
authentication:
type: basic_auth
users:
- username: admin
password: "{{secrets.admin_password}}"
See Authentication for the full reference.
Policies
Policies are an array of security rules that execute before authentication, in the order they appear. If any policy blocks the request, processing stops immediately. Available types:
rate_limiting- Token bucket and fixed window rate limitingip_filter- Whitelist/blacklist by IP address or CIDR rangewaf- Web Application Firewall with ModSecurity rules and OWASP CRSddos- Adaptive thresholds with challenge-response mitigationsecurity_headers- HSTS, CSP, X-Frame-Options, and morecsrf- Double-submit cookie pattern with signed tokensrequest_limit- Request size, header count, and body size limitsconcurrent_limit- Cap in-flight requests per route, IP, or API keyopenapi_validation- Reject requests that do not match a published OpenAPI 3.0 specpage_shield- Stamp a CSP header on every response and collect violation reportsexposed_credentials- Detect known-leaked passwords on Basic-auth requestsdlp- Data Loss Prevention scan over request URI and headersai_crawl_control- HTTP 402 challenges for AI crawlers without payment tokensprompt_injection_v2- Score inbound prompts against a heuristic detector
Each policy supports an optional match field to scope it to specific paths or methods.
Rate limiting
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
policies:
- type: rate_limiting
requests_per_second: 60
burst: 10
key: ip
IP filter
origins:
"internal.example.com":
action:
type: proxy
url: https://backend.example.com
policies:
- type: ip_filter
whitelist:
- 10.0.0.0/8
- 192.168.1.0/24
See Security Policies and Policies Reference for the full reference.
Request Modifiers
Request modifiers change the request before it reaches the action. Each modifier can target headers, query parameters, the URL path, the HTTP method, or the body.
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
request_modifiers:
- headers:
set:
X-Request-ID: "{{request.id}}"
X-Forwarded-User: "{{auth.data.sub}}"
delete:
- X-Internal-Debug
url:
path:
prefix: /v2
For complex request logic, use an inline Lua script:
request_modifiers:
- lua_script: |
function modify_request(req, ctx)
if req.method == "POST" then
return { set_headers = { ["X-Modified"] = "true" } }
end
end
See Rules & Modifiers for the full reference.
Response Modifiers
Response modifiers change the response before it reaches the client. They can adjust headers, status codes, and body content.
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
response_modifiers:
- headers:
set:
X-Response-Time: "{{request.duration}}"
Cache-Control: "public, max-age=300"
delete:
- X-Powered-By
- Server
See Rules & Modifiers for the full reference.
Transforms
Transforms modify response content at the body level. They run after the action returns a response but before response modifiers. Types include:
json- Remove empty fields, pretty print, JSONPath rulesjson_projection- Include or exclude specific JSON fieldslua_json- Lua-scripted JSON transformationsjs_json- JavaScript-scripted JSON transformations (QuickJS)javascript- General-purpose JavaScript body transformwasm- WASM module transform with a sandboxed module per requesthtml- HTML formatting and tag injectionoptimize_html- Lightweight HTML minificationhtml_to_markdown- Convert HTML responses to Markdownmarkdown- Markdown to HTML renderingreplace_strings- Find-and-replace with optional regexnormalize- Normalize whitespace, line endings, and Unicode formsencoding- Convert between text encodingsformat_convert- Convert between supported formatspayload_limit- Cap the response body sizesse_chunking- Rechunk Server-Sent Events for downstream readerstemplate- Render the response body through the template engine
JSON projection
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
transforms:
- type: json_projection
include:
- id
- name
- email
Lua JSON transform
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
transforms:
- type: lua_json
lua_script: |
function modify_json(data, ctx)
return {
set_fields = {
processed = true,
timestamp = os.time()
},
remove_fields = { "internal_id", "debug_info" }
}
end
See Transforms for the full reference.
Forwarding Rules
Forwarding rules route requests to different origin configurations based on path, headers, method, or expressions. Each rule can embed an inline origin with its own action, authentication, and policies. Rules are evaluated in order; the first match wins.
origins:
"app.example.com":
action:
type: static
status_code: 200
body: "Default response"
forward_rules:
- origin:
hostname: api-backend
action:
type: proxy
url: https://api-backend.internal
authentication:
type: api_key
api_keys:
- my-api-key
rules:
- path:
prefix: /api/
- origin:
hostname: static-backend
action:
type: proxy
url: https://cdn.example.com
rules:
- path:
prefix: /assets/
See Forwarding Rules for the full reference.
Response Cache
The response_cache block enables caching of responses at the origin level. Cache hits skip action execution entirely.
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
response_cache:
enabled: true
ttl: 5m
conditions:
status_codes:
- 200
- 301
methods:
- GET
- HEAD
stale_while_revalidate:
enabled: true
duration: 30s
See Caching Guide for the full multi-tier caching reference.
Variables and Webhooks
The variables map holds static values available to templates as {{ variables.name }}. The on_request and on_response hooks fire HMAC-signable webhook notifications as traffic crosses the origin; they notify external systems, they do not inject data into the request.
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
variables:
api_version: "v2"
request_modifiers:
- headers:
set:
X-API-Version: "{{ variables.api_version }}"
on_request:
- url: https://audit.internal/hook
secret: ${AUDIT_HMAC_SECRET}
on_error: ignore
Template variables use {{ scope.path }} syntax. Scopes: request, variables, secrets, config, session, env, server.
See Template Engine, Callbacks, and Secrets Management for details.
Session Config
The session_config block controls cookie-based session tracking. Session data is available as {{session.*}} template variables.
origins:
"app.example.com":
action:
type: proxy
url: https://backend.example.com
session_config:
cookie_name: sb_session
cookie_max_age: 86400
cookie_same_site: Lax
See Session Management for the full reference.
Error Pages
The error_pages array defines custom responses for specific HTTP status codes. Supports inline content, JSON, base64-encoded bodies, and template variables.
origins:
"app.example.com":
action:
type: proxy
url: https://backend.example.com
error_pages:
- status:
- 404
content_type: application/json
body: '{"error": "not found", "path": "{{request.path}}"}'
template: true
- status:
- 500
- 502
- 503
content_type: text/html
body: "Service Unavailable
Please try again later.
"
See Custom Error Pages for the full reference.
Transport Options
Several origin-level fields control transport behavior:
origins:
"api.example.com":
action:
type: proxy
url: https://backend.example.com
force_ssl: true
allowed_methods:
- GET
- POST
- PUT
- DELETE
cors:
enable: true
allow_origins:
- "https://app.example.com"
allow_methods:
- GET
- POST
allow_headers:
- Content-Type
- Authorization
max_age: 86400
compression:
enable: true
algorithms:
- zstd
- br
- gzip
min_size: 1024
Putting It All Together
Here is a complete, realistic origin configuration that combines the most common features:
proxy:
http_bind_port: 8080
origins:
"api.example.com":
# Action: reverse proxy to backend
action:
type: proxy
url: https://backend.example.com
timeout: 30s
# Security: JWT auth with rate limiting
authentication:
type: jwt
jwks_url: https://auth.example.com/.well-known/jwks.json
issuer: https://auth.example.com
audience: my-api
policies:
- type: rate_limiting
requests_per_second: 120
burst: 20
key: ip
- type: ip_filter
blacklist:
- 192.0.2.0/24
# Request pipeline: add tracing headers
request_modifiers:
- headers:
set:
X-Request-ID: "{{request.id}}"
X-User-ID: "{{auth.data.sub}}"
# Response pipeline: strip internal headers, cache GET responses
response_modifiers:
- headers:
delete:
- X-Powered-By
- Server
response_cache:
enabled: true
ttl: 5m
conditions:
methods:
- GET
# Static template values
variables:
api_version: "v2"
# Error handling
error_pages:
- status:
- 500
- 502
- 503
content_type: application/json
body: '{"error": "service unavailable"}'
curl -H "Host: api.example.com" -H "Authorization: Bearer your-jwt-token" http://localhost:8080/