Cloudflare acts as a reverse proxy in front of the Axion report server. Static assets (JS, CSS, fonts, images) are cached at Cloudflare's 300+ global edge locations. The free tier provides unlimited bandwidth with no egress fees.
Axion already sets proper Cache-Control and ETag headers on all assets — Cloudflare respects these automatically.
| Path | Cache-Control | Source |
|---|---|---|
/css/components.css |
DEV=60s, UAT=300s, PROD=3600s | ObjCss palette CSS |
/javascript/* |
DEV=60s, UAT=300s, PROD=86400s | Static JS files |
/style/* |
DEV=60s, UAT=300s, PROD=86400s | Static CSS files |
/fonts/* |
DEV=7min, UAT=35min, PROD=7days | Font files |
/sounds/* |
DEV=7min, UAT=35min, PROD=7days | Audio files |
/documents/* |
Per-document from def_document.CacheTtl | User documents |
/report/* |
Not cached at edge (dynamic) | Report HTML with ETag for 304s |
No code changes needed — the headers we set do the rest.
The Axion config already has Cloudflare credentials:
cloudflare:
domain: "yourdomain.co.za"
email: "cto@technocore.co.za"
token: "<api-token>"
Run the setup script:
python factory.deploy/ObjCloudflare.py setup
This will:
On the free tier (3 Page Rules), recommended rules:
*domain.co.za/javascript/* → Cache Level: Cache Everything, Edge TTL: 1 day*domain.co.za/style/* → Cache Level: Cache Everything, Edge TTL: 1 day*domain.co.za/fonts/* → Cache Level: Cache Everything, Edge TTL: 7 daysThese are optional — Cloudflare already caches based on file extension and our Cache-Control headers.
After a deployment with updated JS/CSS:
python factory.deploy/ObjCloudflare.py purge
Or purge specific URLs:
python factory.deploy/ObjCloudflare.py purge --url /javascript/axion.js
Check response headers:
curl -I https://yourdomain.co.za/css/components.css
Look for:
cf-cache-status: HIT — served from Cloudflare edgecf-cache-status: MISS — fetched from origin, now cachedcf-cache-status: DYNAMIC — not cached (HTML responses)Axion uses Caddy as a local reverse proxy in front of uvicorn. The full chain:
Browser → Cloudflare edge (CDN) → Caddy (TLS + routing) → uvicorn (Axion)
Set to Full (Strict) in Cloudflare dashboard → SSL/TLS. Caddy auto-provisions certificates via Let's Encrypt, so both legs are encrypted:
reports.technocore.co.za {
reverse_proxy localhost:9400
}
# If serving multiple services:
api.technocore.co.za {
reverse_proxy localhost:9500
}
No special cache headers needed in Caddy — Axion sets Cache-Control and ETag on all responses, and Caddy passes them through to Cloudflare untouched.
Avoid these directives which would conflict with Axion's cache headers:
# Don't do this — Axion already handles caching
header /javascript/* Cache-Control "max-age=86400"
header /style/* Cache-Control "max-age=86400"
Cloudflare adds CF-Connecting-IP with the real client IP. Caddy forwards it via X-Forwarded-For. If you need the real IP in Axion, read from the request headers:
real_ip = request.headers.get(
"CF-Connecting-IP",
request.headers.get(
"X-Forwarded-For", ""
).split(",")[0].strip()
)
If Caddy runs on the same host as uvicorn, configure Caddy to trust Cloudflare's IP ranges so X-Forwarded-For is reliable:
{
servers {
trusted_proxies cloudflare
}
}
This uses Caddy's built-in Cloudflare IP range list.
Cloudflare can monitor origin health. Add a lightweight endpoint:
reports.technocore.co.za {
handle /health {
respond "ok" 200
}
handle {
reverse_proxy localhost:9400
}
}
Then in Cloudflare dashboard → Traffic → Health Checks, point to https://reports.technocore.co.za/health.