A short list, not a scanner dump
Most generated apps send only the headers their host adds by default. You do not need a perfect score. You do want five of them on the HTML response: Content-Security-Policy, Strict-Transport-Security (on HTTPS), X-Frame-Options or CSP frame-ancestors, X-Content-Type-Options: nosniff, and Referrer-Policy. Permissions-Policy is worth adding once the first five exist.
X-XSS-Protection is obsolete. A missing favicon is not a vulnerability. A missing CSP will not sink you by itself, but it is the header that limits damage when a secret or a third-party script ends up in the page.
What each one is for
HSTS tells the browser to keep using HTTPS, so a later visit cannot be downgraded. Set it only after the site really is HTTPS everywhere, including subdomains if you include includeSubDomains.
X-Frame-Options: DENY, or CSP frame-ancestors 'none', stops another site from embedding your login page and clicking through it. nosniff stops a browser from treating a user upload as HTML. Referrer-Policy: strict-origin-when-cross-origin keeps full paths from leaking when someone clicks out to Stripe or a docs site.
Where to set them
On Cloudflare, put the headers in a _headers file or the Workers assets config. On Vercel, vercel.json or next.config. On Netlify, netlify.toml. A host-level header is better than a meta tag, because meta tags cannot set HSTS or frame-ancestors reliably.
A Cursor prompt: “Add security headers to the HTML responses: Content-Security-Policy with a default-src that matches the scripts this app actually loads, Strict-Transport-Security, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin, and X-Frame-Options DENY. Do not break the Stripe.js or Supabase domains the app already calls.” Start the CSP in report-only if you are unsure which domains the app loads.
Heedza reads these headers on the homepage response during the free scan. Missing ones lower the grade a little. An exposed .env or a live secret key lowers it a lot more.