Skip to content

Configuration Reference

Ingress uses YAML configuration files to define routing rules, authentication, SSL certificates, and other settings.

Configuration Structure

yaml
port: 8080                     # HTTP port (default: 8080)
# enable_h2c: false            # Optional: cleartext HTTP/2 (h2c) on port; unsafe on public networks

# Cache configuration
cache:
  ttl: 30                      # Cache TTL in seconds
  # engine: redis              # Optional: use Redis cache
  # host: 127.0.0.1
  # port: 6379
  # password: '123456'
  # db: 2

# HTTPS configuration
https:
  port: 8443                   # HTTPS port
  # enable_http3: false        # Optional: HTTP/3 (QUIC) on UDP; needs TLS
  # http3_port: 8443           # Optional: UDP port (default: same as https.port)
  # http3_altsvc_max_age: 86400 # Optional: Alt-Svc ma= (seconds); negative disables header
  # redirect_from_http:
  #   enabled: true           # Optional: false by default; set to true to force HTTP -> HTTPS when https.port is set
  #   permanent: true          # Optional: true=301, false=302
  #   exclude_paths:           # Optional: exact paths to skip redirect
  #     - /healthz
  ssl:
    - domain: example.com
      cert:
        certificate: /path/to/cert.pem
        certificate_key: /path/to/key.pem

# Health check configuration
healthcheck:
  outer:
    enable: true               # Enable outer health check
    path: /healthz             # Health check endpoint path
    ok: true                   # Always return OK
  inner:
    enable: true               # Enable inner service health checks
    interval: 30               # Check interval in seconds
    timeout: 5                 # Check timeout in seconds

# Fallback service (used when no rule matches)
fallback:
  service:
    protocol: https
    name: httpbin.org

# Routing rules
rules:
  - host: example.com
    backend:
      # Omit backend.type when only one of service | handler | redirect applies — runnable twin spelling:
      # examples/basic/ingress.yaml (explicit type: service vs omission).
      service:
        name: backend-service
        port: 8080

Configuration Fields

Top-Level Fields

FieldTypeDescriptionDefault
portintHTTP port to listen on8080
enable_h2cboolCleartext HTTP/2 (h2c) on the HTTP portfalse
cacheobjectApplication ctx.Cache() engine (memory or Redis); backs matcher data and optional backend.cache entries-
httpsobjectHTTPS configuration-
healthcheckobjectHealth check configuration-
fallbackobjectFallback backend-
rulesarrayRouting rules[]
wafobjectOptional WAF baseline; route patches use rules[].waf YAML maps (WAF guide)(inactive when omitted or enabled: false)
rate_limitobjectOptional global request throttling; per-route overrides use rules[].rate_limitoff when omitted
proxyobjectReverse-proxy forwarding behavior (trust_proxy for X-Forwarded-* chain headers only)default off
securityobjectProfile-based security response headers (HSTS, frame, CSP, CORS); per-route rules[].securityoff when omitted
maintenanceobjectGlobal maintenance host registry and default 503 settings (Maintenance guide)off when omitted
scenariosobjectNamed runtime overlays; active: default uses root config (Scenarios guide)off when omitted
loggingobjectZoox logger config (console + optional file transports); see Loggingconsole only when omitted
adminobjectEmbedded operations console (Admin guide)disabled when omitted

Rate limit (rate_limit / rules[].rate_limit)

Fixed-window counters evaluated after route match (global then per-rule). Exceeded limits return 429 with X-RateLimit-Limit, X-RateLimit-Remaining (0), X-RateLimit-Reset (Unix seconds), and X-Ingress-RateLimit: 1. Allowed requests also receive X-RateLimit-* for the effective policy (per-rule when set, otherwise global). Uses in-memory counters by default; when top-level cache.engine: redis host is set, limiters share the same Redis settings.

FieldTypeDescriptionDefault
enabledboolExplicit on/off; omit ⇒ on when requests > 0
requestsintMax requests per window
periodintWindow length in seconds
keystringglobal, route, ip, or headerip
headerstringHeader name when key: header
trust_proxyboolUse X-Forwarded-For for key: ipfalse
xff_indexintXFF segment index (0 = leftmost)0

Access logs append rate_limit_block=1 on 429 responses.

Proxy (proxy)

Controls reverse-proxy forwarding-chain behavior.

FieldTypeDescriptionDefault
trust_proxyboolTrust inbound X-Forwarded-* when generating outbound forwarding headers (X-Forwarded-For/Proto/Host/Port/Target)false

Security headers (security / rules[].security)

Profile-based HTTP security headers applied after route match. See Security headers guide.

FieldTypeDescription
profilestringstrict, api, embeddable, or off
hstsstringauto (HTTPS only), on, or off
framestringinherit, deny, sameorigin, or off
content_type_optionsboolSend X-Content-Type-Options: nosniff
referrer_policystringReferrer-Policy value; off disables
cspstringContent-Security-Policy; off disables
cors.originsarrayAllowed origins (required when CORS enabled)
cors.methodsarrayAllowed methods (preflight)
cors.headersarrayAllowed request headers (preflight)
cors.credentialsboolAllow credentials
cors.max_ageintPreflight cache seconds

The api profile enables CORS and requires at least one origin. OPTIONS preflight is answered by ingress when CORS is active.

Maintenance (maintenance / rules[].backend.service.maintenance)

Evaluated after route match and WAF; returns 503 before redirect/handler/upstream. See Maintenance guide.

Global maintenance:

FieldTypeDescription
hostsarrayHost entries as { host, window? } objects; each may set window.start / window.end (RFC3339)
retry_afterintRetry-After header in seconds (0 = omit)
title / subtitlestring503 page heading / message
bypass.allow_ipsstring arrayClient IP/CIDR allowlist
bypass.pathsstring arrayExact or trailing-* prefix paths
bypass.header.name / valuestringHeader bypass pair
response_header.namestringMaintenance indicator header name
response_header.valuestringMaintenance indicator header value
status_pathstringJSON maintenance status probe path
status_response.okstringJSON template when host is not in maintenance
status_response.maintenancestringJSON template when host is in maintenance
status_response.content_typestringContent-Type for status probe responses

Built-in status probe: GET {status_path} — JSON {"status":"ok"} (200) or {"status":"maintenance",...} (503) for the request Host; see Maintenance guide.

Route rules[].backend.service.maintenance (host-level service backend only):

FieldTypeDescriptionDefault
enabledboolEnable route maintenancefalse
scopestringall or listedall
hostsarrayRequired when scope: listed; same shape as global hosts
retry_afterintOverrides global when route maintenance triggers0
title / subtitlestringOverrides global when route maintenance triggers
bypassobjectMerged with global bypass
response_header.namestringMaintenance indicator header (overrides global when route maintenance triggers)X-Ingress-Maintenance
response_header.valuestringMaintenance indicator header value1

Access logs append maintenance_block=1 on 503 maintenance responses. Maintenance 503 responses include the configured maintenance header (default X-Ingress-Maintenance: 1; optional X-Ingress-Maintenance-From / -Until when a host window is set; upstream 503 does not).

Scenarios (scenarios)

Optional overlay scenes merged at load/reload when scenarios.active is not default. Root ingress.yaml is the baseline. See Scenarios guide.

FieldTypeDescriptionDefault
activestringCurrent scene id; default = no overlaydefault when items exist
items[]arrayOverlay scenes (id, label, description, overlay)[]
items[].overlayobjectPartial cache, rate_limit, waf, maintenance, security, rules

items[].overlay.rules[]: exact host match on a baseline rule → deep-merge; otherwise insert before the first baseline rule that would match that hostname (e.g. sh.example.com before *.example.com). See Scenarios guide.

Reserved: do not use id: default in items[]. Runtime env INGRESS_SCENARIO overrides active.

WAF (waf / rules[].waf)

FieldTypeDescription
enabledboolMaster switch (false or omitted baseline keeps WAF off unless a route sets enabled: true).
trust_proxyboolUse X-Forwarded-For for client IP (default direct RemoteAddr).
xff_indexintSegment index (0 = leftmost IP; negatives count from the right).
log_onlyboolGlobal audit — [waf audit] logs, no blocking.
block_status_codeintStatus on block (unset/0403).
block_content_typestringResponse header when blocking (text/plain; charset=utf-8 default).
block_bodystringResponse body when blocking.
disable_builtinboolOmit embedded starters when true (see built-in rules).
denystring arrayIPs / CIDRs (deny first).
allowstring arrayNon-empty ⇒ only listed nets survive IP phase.
allow_hostsstring arrayHost allowlist — matching Host skips all WAF (exact, * wildcard, or Go regex; same auto-inference as route host).
rulesarrayCustom signatures (id, pattern, type, targets, optional allow_hosts, log_only, action). Same id overlays builtins or replaces inherited rule fields.

Cache Configuration

Top-level cache configures the shared Zoox ctx.Cache() backend (in-memory or Redis). It stores matcher / routing data and any HTTP response entries when per-backend backend.cache is enabled—see backend.cache below and the Caching guide.

FieldTypeDescriptionDefault
enginestringmemory or redismemory
ttlintDefault TTL in seconds (matcher keys and general use)60
hoststringRedis host (when engine: redis)-
portintRedis port6379
passwordstringRedis password-
dbintRedis database number0
prefixstringPrefix for all keys in ctx.Cache() (matcher and httpcache:v1: HTTP entries)-

HTTPS Configuration

FieldTypeDescription
portintHTTPS port to listen on
enable_http3boolEnable HTTP/3 (QUIC) on UDP when TLS is configured
http3_portintUDP port for HTTP/3; 0 means same as https.port
http3_altsvc_max_ageintAlt-Svc ma= in seconds; 0 uses server default; negative disables Alt-Svc
redirect_from_http.enabledboolEnable forced HTTP -> HTTPS redirect (false by default; set to true to activate when https.port is set)
redirect_from_http.permanentboolUse 301 when true, 302 when false
redirect_from_http.with_origin_method_and_bodyboolWhen true, use 308/307 instead of 301/302 so method and body are preserved (default false)
redirect_from_http.exclude_pathsarrayExact request paths that skip forced redirect
sslarraySSL certificate configurations

HTTP/2 over TLS is negotiated automatically when HTTPS is enabled (no extra field).

SSL Certificate

FieldTypeDescription
domainstringDomain name for the certificate
cert.certificatestringPath to certificate file
cert.certificate_keystringPath to private key file

Health Check Configuration

Outer Health Check

FieldTypeDescriptionDefault
enableboolEnable outer health checkfalse
pathstringHealth check endpoint path/healthz
okboolAlways return OKfalse

Inner Health Check

FieldTypeDescriptionDefault
enableboolEnable inner health checksfalse
intervalintCheck interval in seconds30
timeoutintCheck timeout in seconds5

Fallback Configuration

The fallback backend is used when no routing rule matches the request.

If service.request.host.rewrite is omitted, Host is still aligned to the fallback upstream. Set service.request.host.rewrite: false only when you must preserve the client Host. Optional service.mode: external documents the same default Host behavior.

yaml
fallback:
  service:
    # mode: internal            # optional — prefer service.mode; internal (default) | external
    name: fallback-service
    port: 8080
    # protocol: optional — default http
    protocol: http
    # request:
    #   host:
    #     rewrite: false        # optional: preserve client Host

Rules Configuration

Rules define how requests are routed to backend services. See the Routing Guide for detailed information.

For each backend.service, protocol is optional and defaults to http (YAML default in core/service/service.go and core/service/host.go). With protocol: https, an omitted port (or 0) defaults to 443; with http (explicit or default), omitted port defaults to 80. This affects the outbound URL and default Host header.

service.mode (internal default, external for third-party origins) controls upstream Host when request.host.rewrite is omitted; see Rewriting. Legacy backend.mode is accepted when it matches service.mode.

For backend.type, this snippet mixes styles: the rule backend sets type: service explicitly, while paths[].backend blocks omit type (they infer service or handler from their nested blocks).

yaml
rules:
  - host: example.com           # Host to match
    # host_type: optional — omit or `auto` to infer exact vs regex vs wildcard from `host` at compile time
    # explicit values: exact | regex | wildcard
    backend:
      type: service             # optional — omit when only service applies (see examples/basic/ingress.yaml)
      service:
        name: backend-service
        port: 8080
        # mode: internal        # optional — internal (default) | external; prefer service.mode over backend.mode
        # protocol: optional — default http; use https for TLS upstreams
        protocol: http
        auth:                   # Authentication (optional)
          type: basic
          basic:
            users:
              - username: admin
                password: admin123
        healthcheck:            # Service health check (optional)
          enable: true
          method: GET
          path: /health
          status: [200]
        request:
          host:
            rewrite: true       # optional explicit override; often omit when service.mode: external
          path:
            rewrites:          # Path rewrite rules
              - ^/api/v1:/api/v2
          headers:             # Additional headers
            X-Custom-Header: value
          query:               # Query parameters
            key: value
          delay: 0              # Delay in milliseconds
          timeout: 30           # Timeout in seconds
      # redirect: ...           # Only redirect block — see Routing guide (backend.type optional when unique)
    paths:                      # Path-based routing (optional)
      - path: /api
        backend:
          service:
            name: api-service
            port: 8080
            # mode: internal      # optional on path backends — prefer under service
      - path: /healthz
        backend:
          handler:
            status_code: 200
            headers:
              Content-Type: application/json
            body: |
              {"ok": true}

rules[].backend and paths[].backend fields

FieldTypeDescriptionDefault
typestringservice, handler, or redirect (often omitted and inferred)inferred
modestringLegacy: internal / external for service upstream Host defaults. Prefer service.mode; if backend.service.mode is set, it wins when both match or when backend.mode is emptyinternal
serviceobjectUpstream when type is service-
handlerobjectHandler when type is handler-
redirectobjectRedirect when type is redirect-
cacheobjectOptional HTTP response cache for service, handler, and redirect backends; see belowoff

Effective internal / external for Host rewrite is backend.service.mode if set, else legacy backend.mode. They must not disagree if both are non-empty. Applies per backend block (host-level or path-level) for proxy backends only; handler / redirect must not set service.mode.

backend.cache (HTTP response cache)

  • Default off unless cache.enabled: true.
  • Storage uses the same Zoox application cache as matcher caching (ctx.Cache()): the top-level cache block configures Redis/memory (core/prepare.go). Entries use key prefix httpcache:v1: (or httpcache:v2: when a matched path rule sets key_json) plus an MD5 or SHA-256 fingerprint of a canonical request line (method, scheme, host, path, sorted query, configured request headers with values hashed, and optional jsonkey: lines from POST JSON fields).
  • HEAD shares the same cache key as GET for the same URL; GET (and path-allowed POST) round-trips populate the cache for service (proxy), handler (response capture when configured), and redirect (final Location after template expansion; redirect store remains GET-only). Avoids replacing a full GET entry with an empty HEAD body.
  • Client bypass (no cache read/write): Cache-Control containing no-cache, no-store, or max-age=0 (configurable), Pragma: no-cache when honor_pragma_no_cache is true (default), or any Range request header.
  • Not stored (service / handler bodies): non-200; non-empty Vary blocks storage unless cache.skip_vary: true (then Vary is not stored and not sent on hits; you still serve a single variant—see Caching); Cache-Control: no-store; Cache-Control: private (unless ignore_response_private: true); Set-Cookie on the response when skip_when_set_cookie is true (default); body larger than max_body_bytes. Redirect entries store 301/302/303/307/308 with a Location header (no body; same header rules where applicable). Many public httpbin mirrors send Vary: Origin on common paths (e.g. /ip); use skip_vary: true only if you accept treating that path as one shared variant.
  • Verifying hits: send the same GET twice without bypass headers; the second response should be served from cache. Access log lines from cached responses append cache_hit=1 (service proxy, handler, redirect). Cache hits also send X-Ingress-Cache: hit by default (response_header to customize).
FieldTypeDescriptionDefault
enabledboolTurn on HTTP response cache for this backendfalse
ttlintMax freshness in seconds when the origin omits a stricter max-age / s-maxage300
max_body_bytesintDo not store bodies larger than this (0 or unset → 2MiB in code)2097152
key_hashstringFingerprint algorithm: md5 or sha256md5
methodsstring arrayCacheable methods (normalized to uppercase). Must not include POST — use per-path paths[].methods + key_json for POST APIs.GET, HEAD
key_headersstring arrayRequest header names in the fingerprint (values are hashed, not stored raw). Names are normalized with http.CanonicalHeaderKey and deduplicated case-insensitively.(none — empty omits headers from the key)
bypass_request_directivesstring arrayCache-Control tokens that force origin/handling (token match; see code for max-age=0)no-cache, no-store, max-age=0
honor_pragma_no_cacheboolTreat Pragma: no-cache like Cache-Control: no-cache for bypasstrue
ignore_response_privateboolAllow storing Cache-Control: private responsesfalse
skip_when_set_cookieboolWhen true (default), do not store responses that include Set-Cookie; set false only in advanced cases (risk of caching personalized/session responses).true
skip_varyboolWhen true, allow storing responses with Vary (unsafe unless the origin is single-variant for this URL); Vary is omitted from stored entries and not sent on cache hitsfalse
defaultstringWhen paths is non-empty: behavior for requests that match no rule — cache or bypasscache
pathsarrayOrdered path rules (first match wins); see below
response_header.namestringHeader name on cache hitsX-Ingress-Cache
response_header.valuestringHeader value on cache hitshit

backend.cache.paths[] (optional):

FieldTypeDescriptionDefault
matchstringPath pattern (required)
match_typestringauto, prefix, exact, or regexauto
actionstringcache (read/write cache) or bypass (skip cache entirely)cache
ttlintOverride backend ttl for this rule when action: cache and > 0inherit
max_body_bytesintOverride backend max_body_bytes for this rule when action: cache and > 0inherit
methodsstring arrayOverride backend methods for this path when non-empty (e.g. [POST])inherit
key_jsonstring arrayDot paths into the request JSON object for the fingerprint (e.g. product.id). Requires methods to include POST on this rule. Implies httpcache:v2: keys.
key_body_max_bytesintMax request body bytes read for JSON parsing when key_json is set (065536 at compile)65536 when key_json set

match_type: auto (same idea as host_type: auto): regexp metacharacters ( ) [ ] ^ $ | + ? \regex; trailing /prefix; otherwise exact. Rules are evaluated top to bottom; put narrower patterns before broader ones (e.g. bypass /static/private before cache /static/). When paths is omitted, all paths on the backend use cache when enabled: true (unchanged).

Examples: examples/advanced/http-response-cache.yaml (in-memory ctx.Cache()), examples/advanced/redis-cache.yaml (Redis + backend.cache), examples/advanced/http-response-cache-paths.yaml (per-path rules), examples/advanced/http-response-cache-post-json.yaml (POST + key_json).

See core/rule/backend_cache.go, core/http_cache.go, and core/build.go.

Admin (admin)

Optional embedded console (HTTP API + UI). Enabled with admin.enabled: true; starts in the same process as ingress run. Full guide: Admin console.

FieldTypeDescriptionDefault
admin.enabledboolStart admin with the proxyfalse
admin.portintAdmin listen port9080
admin.database.driverstringSQLite driver for audit / revisionssqlite
admin.database.dsnstringDatabase DSNfile:admin.db?cache=shared&_fk=1
admin.web.dev_proxyboolAPI only; UI from Vite dev serverfalse
admin.auth.typestringConsole login: none (default), basic, oauthnone
admin.auth.basic.usernamestringBootstrap super-admin RBAC usernameadmin when used with default password
admin.auth.basic.passwordstringBootstrap user password (first create only)admin when used with default username
admin.access_log_pathstringAccess log path for the log viewerfrom logging
admin.error_log_pathstringError log path for the log viewerfrom logging
yaml
admin:
  enabled: true
  port: 9080
  database:
    driver: sqlite
    dsn: file:./admin.db?cache=shared&_fk=1

Example bundle: examples/admin-console/ingress.yaml.

Logging (logging)

The logging block is zoox Config.Logger (same fields; ingress copies it to app.Config.Logger at prepare). Zoox always includes console; each transports entry is stacked on top. File routing uses go-zoox/logger/transport/file.

FieldTypeDescription
logging.enableboolWhen true, enable console + file logging. If transports is omitted, defaults to /var/log/ingress/access.log and /var/log/ingress/error.log (directory created automatically). When false, console only. When admin.enabled: true and logging is unset, defaults to enable: true with access.log / error.log beside the config file instead. Explicit logging.* always wins.
logging.levelstringMinimum log level (debug, info, warn, error).
logging.transportsarrayExtra sinks, e.g. type: file with path and optional levels. Overrides default paths when set.
logging.middleware.disabledboolIngress sets this to true (zoox HTTP request logger middleware).

Example:

yaml
logging:
  enable: true
  level: warn

Custom paths:

yaml
logging:
  enable: true
  level: warn
  transports:
    - type: file
      path: /var/log/ingress/access.log
      levels:
        error: /var/log/ingress/error.log

Omit logging for zoox default (console only).

Access Log Fields

Each access line uses a fixed text format (not Nginx log_format):

text
{client_ip} {host} -> {target} "{method} {path} {proto}" {status} {duration_ms} {extra...}

Extra fields (space-separated key=value):

  • cache_hit: 0 or 1 (HTTP response cache hit)
  • waf_block: 0 or 1 (WAF blocked this request)
  • real_ip: X-Real-IP, else client address; - when unavailable
  • referer: Referer header; - when empty
  • ua: User-Agent; - when empty
  • xff: X-Forwarded-For; - when empty (quoted when value contains spaces)
  • tls_protocol / tls_cipher: TLS names; - for plain HTTP
  • upstream_status: upstream or handler status code
  • upstream_response_length: bytes (-1 when unknown)
  • upstream_response_time: duration in milliseconds (e.g. 12ms)

Example:

text
203.0.113.44 api.example.com -> api.internal:8080 "GET /api/users HTTP/1.1" 200 12ms cache_hit=0 waf_block=0 real_ip=203.0.113.44 referer=- ua=curl/8.0 xff=- tls_protocol=- tls_cipher=- upstream_status=200 upstream_response_length=1024 upstream_response_time=12ms

Note: there is currently no standalone field exactly equivalent to Nginx $body_bytes_sent; if needed, derive it via downstream log/metrics aggregation.

Environment Variables

You can override some configuration using environment variables:

  • CONFIG: Path to configuration file
  • PORT: HTTP listen port; overrides the top-level port field in the YAML when set

Configuration Validation

Ingress validates configuration when the process starts and when you run ingress validate. Errors prevent startup or a successful reload.

Static checks include router compilation (host/path regex), HTTPS/TLS shape, per-backend mode (internal / external), and per-backend consistency (backend.type is usually omitted and inferred as service, handler, or redirect):

  • backend.type is optional. With type omitted, Ingress infers the mode when exactly one of service, handler, or redirect looks configured; otherwise validation fails and asks for an explicit backend.type.
  • With an explicit type, only the matching block is allowed (for example type: redirect requires redirect.url and forbids populated service / handler fields).

Validation errors cite the rule index, configured host pattern, and routing path: rules[N] host="..." path="...". Rule-level backends use path="/"; path backends use the configured paths[].path pattern (if that pattern string is empty, the message falls back to paths[index]). Fallback backends use fallback path="/".

Reloading Configuration

You can reload the configuration without restarting the server:

  1. Send a SIGHUP signal: kill -HUP $(cat /tmp/gozoox.ingress.pid)
  2. Use the reload command: ingress reload

The server will reload the configuration file and apply changes without dropping connections.

Released under the MIT License.