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:
| Header | Value | Purpose |
|---|---|---|
X-Content-Type-Options | nosniff | Prevents MIME-type sniffing |
X-Frame-Options | DENY | Prevents clickjacking via iframes |
Content-Security-Policy | default-src 'self'; connect-src 'self' wss: https:; frame-src 'none'; object-src 'none' | Restricts resource loading |
Strict-Transport-Security | max-age=31536000; includeSubDomains | Forces HTTPS for 1 year |
X-XSS-Protection | 1; mode=block | Enables 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
- The client IP is resolved from the
X-Forwarded-Forheader (first entry), falling back to the peer address - Each IP gets a counter that resets when the time window expires
- When the counter exceeds
max_requests, requests are rejected with429 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 Key | Env Var | Description |
|---|---|---|
rate_limit.window_ms | APOLLON__RATE_LIMIT__WINDOW_MS | Time window in milliseconds |
rate_limit.max_requests | APOLLON__RATE_LIMIT__MAX_REQUESTS | Maximum requests per IP per window |
Exempt Paths
The following paths bypass rate limiting:
| Path | Description |
|---|---|
/health | System health check |
Error Response
When rate limited, the API returns 429 Too Many Requests:
{
"error": "Too many requests",
"retryAfter": 45
}
| Field | Type | Description |
|---|---|---|
error | string | Error description |
retryAfter | u64 | Seconds until the rate limit window resets (minimum 1) |
IP Resolution
The rate limiter resolves client IP in this order:
X-Forwarded-Forheader -- takes the first comma-separated IP (trimmed)- Peer address from the TCP connection
- Falls back to
"unknown"if neither is available
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 Key | Env Var | Description |
|---|---|---|
cors.allowed_origins | APOLLON__CORS__ALLOWED_ORIGINS | Comma-separated list of allowed origins |
CORS Settings
| Setting | Value |
|---|---|
| Allowed methods | GET, POST, PUT, DELETE, OPTIONS |
| Allowed headers | Content-Type, Authorization, X-Requested-With, X-API-Key |
| Exposed headers | Content-Range, X-Content-Range |
| Credentials | Supported |
| Max age | 86400 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.