Research Report
State of Web Security in YC Startups 2026
I pointed our scanner at 100 Y Combinator companies across three batches. Nine sites had already gone dark or refused the handshake. The rest — 91 of them — had the same story: headers the browser would have honored, if anyone had set them.
Headline Number
94%
of scanned YC startups ship with 2 or more critical security headers missing. 86 out of 91 reachable sites.
TL;DR
- → 94% are missing 2+ headers. Over half (56%) are missing five or more.
- → Only 8 of 91 sites ship a Content-Security-Policy — the header that turns XSS from a takeover into a console error.
- → 86% can be loaded into an iframe. Clickjacking surface is wide open.
- → Zero sites still accepted TLS 1.0 or 1.1. The CDN tier killed that decade-old finding.
- → One site leaked a single
.DS_Store. No.env, no.git, no database dumps anywhere. - → Fixing each header is a config-file edit. Nothing here demands a rearchitecture.
What the scan did
Sample: 100 YC companies drawn from three recent batches (W25, S24, W24). For each, the scanner fetched the homepage over HTTPS, then evaluated six response headers. Next, a Python ssl handshake probed support for TLS 1.0 through 1.3 against the same hostname. Last, a GET hit seven historical trouble-paths — .env, .git/config, wp-config.php.bak, and so on — with content-signature checks so that catch-all SPA 404s serving HTML did not count as a hit.
Nothing about this is adversarial. Same requests Googlebot already makes every day, same rate limit. The whole run finished in about eight minutes.
The numbers
94%
Missing 2+ headers
86 of 91
92%
Missing 3+ headers
84 of 91
84%
Missing 4+ headers
77 of 91
56%
Missing 5+ headers
51 of 91
Which header, specifically?
| Header | Missing |
|---|---|
| Permissions-Policy | 96% |
| Content-Security-Policy | 91% |
| Referrer-Policy | 87% |
| X-Frame-Options | 86% |
| X-Content-Type-Options | 59% |
| Strict-Transport-Security | 18% |
Two findings that genuinely surprised me
I expected both of these to show up — neither did.
Zero of 91 sites accepted TLS 1.0 or 1.1.
Ten years ago weak TLS was the most common finding in any web audit. Now Cloudflare, Vercel and Fastly all refuse the old protocols at the edge by default, and every YC site in the sample sits behind one of them. The startups inherited the fix without filing a ticket.
Only one site leaked any sensitive file — a single .DS_Store.
It belonged to a company that had clearly deployed a built-and-uploaded static site from a macOS machine. Zero sites leaked .env, zero leaked .git/config, zero leaked database dumps. Modern build pipelines bury that stuff outside the web root before anyone has the chance to ship it.
What I think this means
The classic 2015 web audit checklist (TLS version, exposed build artifacts, directory listings) is nearly empty. The 2026 checklist is almost entirely headers the team intended to add and never did.
The classic findings — weak TLS, exposed .env files, backup copies of wp-config.php — have mostly been killed by infrastructure. Pick a modern host and the edge tier handles it for you.
The headers, though, do not show up by default. No CDN adds a Content-Security-Policy for you. No framework scaffolder writes Permissions-Policy into your config on your behalf. If they are missing, it is because the header was on somebody’s “later” list and later never came.
Each fix is a config-file edit, not a refactor. The companies in this dataset are well-funded, technical teams. They know how to write CSP. They just have not written it yet.
How to fix it on your own site
If you are on Next.js, drop this into next.config.js and redeploy:
async headers() {
return [{
source: "/(.*)",
headers: [
{ key: "Content-Security-Policy", value: "default-src 'self'; script-src 'self'" },
{ key: "X-Frame-Options", value: "DENY" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
],
}];
}Express users reach for the helmet middleware. On Nginx it is an add_header block inside the server context. On Cloudflare, a Transform Rule on the response side. All of these take longer to explain than to implement.
One caveat on CSP: starting with default-src 'self' will break a site that loads from a CDN or ingests third-party widgets. Use a report-only policy first (Content-Security-Policy-Report-Only), watch the violations for a week, then tighten. Skip that step and your landing page will go blank on deploy.
If you’d rather not hand-roll it, the CSP generator on this site gives you a starting policy, and the header checker confirms what is actually going out in production after deploy.
Methodology
- Source
- YC public API at api.ycombinator.com/v0.1/companies, filtered by batch.
- Scan date
- 2026-04-17
- Sample
- 100 companies across YC W25, S24, W24. 91 reachable at scan time.
- Requests
- Passive HTTPS GETs, User-Agent
ismycodesafe-audit/1.0, 1 request per path, approximately 1 request per second pacing. - Header check
- Single GET to the homepage. Headers parsed case-insensitively.
- TLS check
- Python ssl module, minimum_version and maximum_version set to each target version. Raw TCP connection, no HTTP request sent.
- Path probe
- GET to seven known sensitive paths. Content validated against path-specific signatures (for example
.git/configmust start with[core]) to reject SPA catch-all 404s serving HTML. - Code
- Open source: yc_security_audit.py. Reproducible with one command.
Get the raw data
The anonymized CSV has all 91 reachable sites with their per-header findings, TLS versions, and exposed paths. Company names are not included. The data is licensed CC BY 4.0. Republish freely with attribution.
FAQ
What is a security header?▼
An HTTP response header the server adds to every page load. The browser reads it and tightens what scripts can run, whether the page can be embedded, whether HTTPS is mandatory, and so on. If the header is absent, the browser defaults to the permissive option. Missing headers are not an exploit by themselves — they remove the safety net that would catch a bug elsewhere in the app.
Why does 91% missing CSP matter more than the other gaps?▼
Content-Security-Policy is the one header that turns a reflected XSS bug from instant account takeover into a harmless error in the browser console. Without it, a single stored comment that renders user HTML becomes a full breach. The other missing headers widen attack surface; CSP is the difference between a bug and an incident.
Does this mean YC companies are less secure than average?▼
No — these numbers track what we see in the broader population of production web apps. YC came up because their company list is public and the batches give a comparable time-series. Most SaaS sites outside YC look the same.
How do I check my own site?▼
Paste your URL into ismycodesafe.com for the full 160-check scan, or use securityheaders.com for a header-only readout. Both take under 30 seconds and do not require signup for the basic output.
When do you re-run the audit?▼
July 2026. Same three batches plus S25 once that batch has companies with live sites. The follow-up will diff each company against its April baseline — who fixed what, who shipped new exposure.
Can I republish the dataset?▼
Yes. The CSV is CC BY 4.0. Credit ismycodesafe.com and link back. The methodology section below lets you reproduce the run yourself.
Want to check your own site against these findings?
160 security checks in 60 seconds. Free, no signup.
Scan My WebsiteNext run: July 2026. We will publish a diff showing what each batch fixed between scans.