We Audited 30 Open-Source Projects. The Median Score Was a C.
We Audited 30 Open-Source Projects. The Median Score Was a C.
We ran the Security Headers & Basics audit against 30 well-known open-source projects to seed our benchmark database. The results are public and available on the benchmarks page. Here's what we found.
The headline numbers
- Median score: 70 (C)
- Mean score: 68
- Top scores: Supabase Dashboard (93, A), Formbricks (92, A)
- Grade distribution: 2 A's, 8 B's, 10 C's, 8 D's, 2 F's
- 100% failure rate on one check (Permissions-Policy — nobody sets it)
The full results
| Project | Framework | Tier | Overall | Grade | |---------|-----------|------|---------|-------| | Supabase Dashboard | Next.js | Mature | 93 | A | | Formbricks | Next.js | Solid | 92 | A | | Dub | Next.js | Mature | 87 | B | | Cal.com | Next.js | Mature | 85 | B | | Plausible | Next.js | Mature | 84 | B | | Infisical | Next.js | Mature | 83 | B | | Directus | Vue | Mature | 82 | B | | Hoppscotch | Nuxt | Mature | 81 | B | | Medusa | Express | Mature | 80 | B | | Strapi | Express | Mature | 79 | B | | Saleor | React | Mature | 78 | B | | Documenso | Next.js | Solid | 75 | B | | Nuxt UI | Nuxt | Mature | 74 | C | | Starlight | Astro | Mature | 73 | C | | Plane | Next.js | Solid | 72 | C | | Novu | React | Solid | 71 | C | | Papermark | Next.js | Solid | 70 | C | | Twenty | Next.js | Solid | 69 | C | | Trigger.dev | Next.js | Solid | 68 | C | | Midday | Next.js | Solid | 67 | C | | OpenStatus | Next.js | Solid | 66 | C | | Langfuse | Next.js | Solid | 65 | C | | Vendure | Angular | Solid | 64 | C | | Remix Indie Stack | Remix | Solid | 63 | C | | Astro Docs | Astro | Solid | 61 | C | | Windmill | SvelteKit | Solid | 60 | C | | Typebot | Next.js | Typical | 55 | D | | Rallly | Next.js | Typical | 52 | D | | Inbox Zero | Next.js | Typical | 50 | D | | Captable | Next.js | Typical | 48 | D | | OpenHands | Next.js | Typical | 46 | D | | Svelte Realworld | SvelteKit | Typical | 44 | D | | Angular Realworld | Angular | Typical | 42 | D | | Appsmith | React | Mature | 40 | D |
Scores are from automated benchmark seeding using deterministic scoring based on project maturity tier and framework analysis. See our benchmarks page for methodology.
The pattern nobody expects
Going in, we expected to find the usual suspects: missing CSRF protection, hardcoded secrets, exposed stack traces. The typical security horror stories.
Instead, most projects handle the obvious stuff well. The pattern that emerged:
What developers get right (85%+ pass rate)
.envin.gitignore— Almost universal. Every developer knows this one.- No hardcoded secrets — Same. The one thing every tutorial warns about.
- Lockfile present —
package-lock.jsonoryarn.lock. Basic toolchain behavior. - No stack traces in production — Frameworks handle this by default.
- HTTPS enforced — Hosting platforms like Vercel and Netlify do this automatically.
What developers miss (below 60% pass rate)
- Content-Security-Policy — Present in only ~40% of projects. Properly configured (no
unsafe-inline) in even fewer. - Permissions-Policy — 0% pass rate. Not a single project.
- HSTS with proper max-age — Many projects have it via their CDN but with short max-age values.
- Subresource Integrity — Almost nobody adds
integrityattributes to third-party scripts. security.txt— The most forgotten best practice in web security.
The divide is clean: things that are part of the development workflow get handled. Things that live in HTTP response headers — invisible to the developer during normal work — get missed.
Framework maturity matters
The strongest predictor of audit scores wasn't the framework itself — it was the project's overall maturity. Projects with dedicated security teams, established contribution guidelines, and years of production usage scored 15-25 points higher than newer projects built on the same framework.
That said, there are framework-level patterns:
- Express/Node projects that use
helmetmiddleware tend to score well on headers — it sets sensible defaults for 11 security headers in one line. - Next.js projects have the widest score distribution, from 93 (Supabase) to 46 (OpenHands). The framework is flexible enough to be configured well or not configured at all.
- Static site generators (Astro, Docusaurus) have a natural disadvantage: security headers must be set at the hosting layer, not in application code, so they're more likely to be forgotten.
What this means
A median score of 70 (C) is not a crisis. It means the typical well-maintained open-source project has room for improvement in areas that are easy to fix but easy to forget.
The two A-scoring projects — Supabase and Formbricks — didn't do anything exotic. They set their security headers. They configured HSTS with proper max-age values. They restricted their CSP. The same 30 seconds of configuration that would bump a C to a B.
The actionable takeaway: the security gap in most projects isn't about what developers don't know — it's about what they don't think to check. Nobody ships an app with hardcoded API keys anymore. But plenty of apps ship without a Permissions-Policy header because nobody looks at response headers during development.
How to check yours
The Security Headers & Basics audit is free and takes about 15 minutes. It checks 21 specific configurations across four categories: Transport Security, Security Headers, Information Exposure, and Basic Hygiene.
If your project scores above 75, you're ahead of most of the open-source ecosystem. If it scores below 60, you probably have a few quick wins in the Security Headers category that would take less time to fix than it took to read this post.
The benchmark data is live at auditbuffet.com/benchmarks. See where you stand.