Refine scored 38/F on the Security Headers & Basics audit. That is the lowest score in our benchmark dataset outside of example/starter apps. But Refine is a well-maintained React meta-framework with 30k+ GitHub stars, backed by a funded company, with active security practices.
So what happened?
The Security Headers audit checks 20 things across four categories: Transport Security, Security Headers, Information Exposure, and Basic Hygiene. Here is what Refine's results look like:
Transport Security (30% weight):
Security Headers (30% weight):
Basic Hygiene (15% weight):
Every transport and headers check fails. Every single one. And the audit is correct: there is no HTTPS enforcement, no HSTS, no CSP, no X-Frame-Options. These headers do not exist in the Refine codebase.
They do not exist because Refine does not serve HTTP responses. It is a client-side library. It runs inside a browser. It does not have a server. It does not set headers because it does not send responses.
This is the same issue that hits every client-side framework, component library, and frontend toolkit. The security boundary looks like this:
[Refine / React / Vue / Svelte] --> Client-side library
|
v
[Next.js / Express / nginx] --> Server (sets headers)
|
v
[Vercel / AWS / your VPS] --> Infrastructure (TLS, HSTS)
Refine operates at the top layer. It builds UI. The server and infrastructure layers below it are responsible for transport security and response headers. When you build an app with Refine and deploy it on Next.js on Vercel, the headers come from Next.js and Vercel — not from Refine.
The audit does not know this. It looks at the codebase, sees no header configuration, and marks every check as failed. The score is technically accurate. The implication — that Refine is insecure — is not.
Almost never for transport-level headers. But there are a few checks in the audit that are legitimately relevant to client-side code:
Information Exposure matters everywhere. A client-side library should not ship source maps to production, should not expose stack traces to end users, and should not leak version information unnecessarily. Refine passes some of these.
Basic Hygiene is universal. Every project should have its .env in .gitignore, should avoid hardcoded secrets, and should keep a lockfile. Refine passes all of these.
CSP compatibility is relevant even for libraries. If a library injects inline scripts or styles, it forces downstream applications to use unsafe-inline in their CSP, which defeats a significant part of the protection CSP provides. Libraries should test that they work with strict CSP policies even though they do not set CSP themselves.
So the real Refine security posture is closer to: Basic Hygiene looks solid, Information Exposure is reasonable, and Transport/Headers are not applicable. That is a meaningfully different story from "38/F."
The 38/F means: "If you deploy this codebase as-is, without any server-side header configuration, your users are exposed to clickjacking, protocol downgrade attacks, MIME sniffing, and cross-site data leakage."
That is true. It is also true of literally every React component library, every npm package, and every client-side framework ever written. React itself would score similarly. So would Tailwind CSS.
The score does not mean:
It means Refine is a different kind of project than the audit was designed for.
When you see a low score, check the project type before drawing conclusions. Ask:
We are working on better handling for this in AuditBuffet — potentially flagging client-side-only projects and adjusting which checks are considered applicable. In the scoring system, checks marked as "skip" (not applicable) are excluded from both the numerator and denominator, so the score only reflects checks that are actually relevant.
Until then, treat audit scores as a starting point for investigation, not a final verdict. An F that comes entirely from inapplicable checks is not the same as an F that comes from real failures.
If you are building an application with Refine (or any client-side framework), the security headers are your responsibility. The framework gives you the UI. You provide the server. The server provides the headers.
For a typical Refine + Vite + Express setup:
// express server
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;");
next();
});
Run the Security Headers audit against your deployed application, not against the library you used to build it. That is where the score matters.