Self-Hosted vs Cloud: The Security Headers Gap
Self-Hosted vs Cloud: The Security Headers Gap
We ran the Security Headers & Basics audit against 30 open-source projects. The results split cleanly along one axis that has nothing to do with code quality: deployment model.
Cloud-deployed projects:
- Supabase: 93/A
- Formbricks: 92/A
- PostHog: 88/B
- Dub: 85/B
- Cal.com: 82/B
Self-hosted projects:
- NocoDB: 52/D
- Payload: 45/D
- Immich: 40/D
- Hoppscotch: 39/F
That is a 40-50 point gap between the top cloud projects and the self-hosted group. These are all well-maintained, heavily starred, professionally run open-source projects. The difference is not engineering quality. The difference is responsibility boundaries.
Why self-hosted projects score lower
When Supabase deploys to supabase.com, they control every layer: the application code, the hosting platform, the CDN, the TLS termination. They set Strict-Transport-Security, Content-Security-Policy, X-Frame-Options, and Referrer-Policy in their Next.js config or their Vercel settings. Those headers ship with every response.
When Payload ships a self-hosted CMS, they ship application code. The user runs it behind nginx, Caddy, Traefik, Apache, a cloud load balancer, or Docker Compose on a VPS. The project cannot set transport-level headers because it does not control the transport layer. HSTS in particular is meaningless without TLS termination, which happens at the reverse proxy — not in the application.
This is the right architecture. Payload should not be hardcoding Strict-Transport-Security: max-age=31536000 into responses that might be served over plain HTTP on a local network. But the audit does not know that. It sees missing headers and marks them as failures.
What this means for scores
Our Security Headers audit has four categories, weighted as follows:
- Transport Security (30%): HTTPS enforcement, HSTS, secure cookies, SameSite
- Security Headers (30%): CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, SRI, CORS
- Information Exposure (25%): stack traces, server version, error pages, source maps
- Basic Hygiene (15%): .env in .gitignore, no hardcoded secrets, dependency audit, lockfile, security.txt
Self-hosted projects typically ace Basic Hygiene and Information Exposure. They fail Transport Security and much of Security Headers because those checks assume the application controls its own HTTP responses end-to-end. For a self-hosted project, the Transport Security category is almost entirely the operator's responsibility.
The audit still provides value: it tells the operator exactly what their reverse proxy needs to configure. But the raw score is not an apples-to-apples comparison with cloud-deployed projects.