Skip to main content

Middleware

The Apollon API applies a middleware stack to every request. Middleware is registered in reverse order in Actix-web, so the execution order is:

Request
-> CORS
-> Compression
-> Security Headers
-> API Key Auth
-> Rate Limiting
-> Handler
<- Rate Limiting
<- API Key Auth
<- Security Headers
<- Compression
<- CORS
Response

Security Headers

Every response includes the following security headers, added via the security_headers() middleware:

HeaderValuePurpose
X-Content-Type-OptionsnosniffPrevents MIME-type sniffing
X-Frame-OptionsDENYPrevents clickjacking via iframes
Content-Security-Policydefault-src 'self'; connect-src 'self' wss: https:; frame-src 'none'; object-src 'none'Restricts resource loading
Strict-Transport-Securitymax-age=31536000; includeSubDomainsForces HTTPS for 1 year
X-XSS-Protection1; mode=blockEnables browser XSS filtering

These headers are applied unconditionally to all responses, including error responses.

Rate Limiting

Per-IP sliding-window rate limiter that tracks request counts within a configurable time window.

How It Works

  1. The client IP is resolved from the X-Forwarded-For header (first entry), falling back to the peer address
  2. Each IP gets a counter that resets when the time window expires
  3. When the counter exceeds max_requests, requests are rejected with 429 Too Many Requests

Configuration

Rate limiting is configured via the rate_limit section. When omitted, rate limiting is disabled (max_requests is set to u64::MAX).

[rate_limit]
window_ms = 60000 # 1 minute window
max_requests = 100 # Max requests per window per IP
Config KeyEnv VarDescription
rate_limit.window_msAPOLLON__RATE_LIMIT__WINDOW_MSTime window in milliseconds
rate_limit.max_requestsAPOLLON__RATE_LIMIT__MAX_REQUESTSMaximum requests per IP per window

Exempt Paths

The following paths bypass rate limiting:

PathDescription
/healthSystem health check

Error Response

When rate limited, the API returns 429 Too Many Requests:

{
"error": "Too many requests",
"retryAfter": 45
}
FieldTypeDescription
errorstringError description
retryAfteru64Seconds until the rate limit window resets (minimum 1)

IP Resolution

The rate limiter resolves client IP in this order:

  1. X-Forwarded-For header -- takes the first comma-separated IP (trimmed)
  2. Peer address from the TCP connection
  3. Falls back to "unknown" if neither is available
caution

When running behind a reverse proxy, ensure X-Forwarded-For is set correctly. Without it, all clients may share the same rate limit bucket.

CORS

Cross-Origin Resource Sharing is configured via the cors section:

[cors]
allowed_origins = ["http://localhost:5173", "https://app.example.com"]
Config KeyEnv VarDescription
cors.allowed_originsAPOLLON__CORS__ALLOWED_ORIGINSComma-separated list of allowed origins

CORS Settings

SettingValue
Allowed methodsGET, POST, PUT, DELETE, OPTIONS
Allowed headersContent-Type, Authorization, X-Requested-With, X-API-Key
Exposed headersContent-Range, X-Content-Range
CredentialsSupported
Max age86400 seconds (24 hours)

If any origin in the list is "*", CORS switches to fully permissive mode (allows all origins).

Compression

Response compression is enabled via Actix-web's Compress middleware using default settings. It supports:

  • gzip compression
  • brotli compression
  • deflate compression

The appropriate encoding is selected based on the client's Accept-Encoding header.