Frontend & Web
15 practices · OWASP Cheat Sheets + current frontend security guidance
What ships to the browser — headers, cookies, sanitization, and the rule that no client-side check is ever the real check.
Ticking these off for a specific project? The Project Security Checklist tracks all nine categories in one place. For why these patterns keep recurring, see Security Best Practices.
1. No Secrets in the Client Bundle
DescriptionAnything shipped to the browser — including source maps — is public. Scan builds for accidentally-bundled API keys, and restrict any key that must be client-side by domain/referrer at the provider.
Examplewebpack-bundle-analyzer + a CI grep for
/sk_live_|AKIA|BEGIN PRIVATE KEY/ across dist/
2. HTTPS + HSTS Everywhere
DescriptionEvery page and API call runs over HTTPS, with HSTS (ideally with preload) so browsers refuse to fall back to plain HTTP even if a user types the wrong URL or clicks an old link.
ExampleStrict-Transport-Security: max-age=63072000;
includeSubDomains; preload
3. Strict Content-Security-Policy
DescriptionSet a CSP with default-src 'self' and no unsafe-inline/unsafe-eval, using nonces or hashes for any script that genuinely must be inline — this is the single biggest structural defense against XSS.
ExampleContent-Security-Policy: default-src 'self';
script-src 'self' 'nonce-r4nd0m'; object-src 'none'
4. Full HTTP Security Header Set
DescriptionBeyond CSP and HSTS, set X-Content-Type-Options: nosniff, Referrer-Policy, Permissions-Policy, and X-Frame-Options — a five-minute change that closes several classes of attack at once.
ExampleX-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=()
5. Sanitize Before You Render
DescriptionAny HTML that came from a CMS, user input, or a third party is run through DOMPurify (or the framework equivalent) before it touches dangerouslySetInnerHTML/innerHTML — every time, no "trusted source" exceptions.
Example__html: DOMPurify.sanitize(cms.body,
{ ALLOWED_TAGS: ['b','i','a','p'] })
6. CSRF Tokens + SameSite Cookies
DescriptionPair a CSRF token on state-changing forms with sameSite=strict (or lax) cookies — two independent layers, since either alone has edge cases the other covers.
Example<input type="hidden" name="_csrf" value="{{token}}">
cookie: { sameSite: 'strict' }
7. httpOnly Cookies, Not localStorage
DescriptionSession tokens live only in httpOnly + secure cookies, never in localStorage or sessionStorage — a single XSS bug should not be able to read the session token, full stop.
ExampleSet-Cookie: session=...; HttpOnly; Secure; SameSite=Strict
8. Strict CORS Allowlist
DescriptionNever combine origin:'*' with credentials:true. List the exact origins allowed to make credentialed requests, and keep that list as short as the product actually needs.
ExampleAccess-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
9. Subresource Integrity on Third-Party Scripts
DescriptionAny script loaded from a third-party CDN gets an integrity hash, so if that CDN is ever compromised, the browser refuses to execute the tampered file instead of silently running it.
Example<script src="https://cdn.example.com/lib.js"
integrity="sha384-oqVuAf..." crossorigin="anonymous">
</script>
10. Minimize Client-Side Storage of Sensitive Data
DescriptionlocalStorage, sessionStorage, and IndexedDB are all readable by any script running on the page — keep PII, tokens, and anything sensitive out of them, or encrypt it client-side if it truly must be cached.
Example// Cache a display name, not the auth token or profile:
localStorage.setItem('displayName', user.firstName)
11. Validate on the Server, Too
DescriptionEvery frontend check — file type, form validation, feature gating — is UX sugar for honest users. The server independently re-enforces the exact same rule, because devtools can bypass anything client-side.
ExampleFrontend: <input required maxlength=100>
Backend: z.string().min(1).max(100) — re-checked, not trusted
12. Third-Party Dependency Auditing
DescriptionRun npm audit / Snyk on every build, pin dependency versions with a lockfile, and review what a new package actually does before adding it — supply-chain compromise is a growing share of real incidents.
Examplenpm audit --audit-level=high
(fails CI on high/critical vulnerabilities)
13. Server-Side Access Control Enforcement
DescriptionHiding a button or route in the UI is not access control — the API underneath must independently check permissions, or a user can simply call the endpoint directly.
Example// Hiding the "Delete" button isn't enough —
// DELETE /posts/:id must check req.user.role === 'admin'
14. Rate Limiting at the Edge
DescriptionApply request throttling at the CDN/edge layer (Cloudflare, API Gateway) in addition to application-level limits, so brute-force and scraping traffic is absorbed before it reaches your servers at all.
ExampleCloudflare rate limiting rule:
>20 req/min to /api/auth/* from one IP
→ challenge or block
15. noopener / noreferrer on Cross-Origin Links
DescriptionAny target="_blank" link or window.open() call gets rel="noopener noreferrer", so the page it opens can't use window.opener to redirect your original tab to a phishing page.
Example<a href="https://partner.com" target="_blank"
rel="noopener noreferrer">Partner site</a>
Last updated: September 2026 · Source: Offspring — Security Best Practices.pptx (Part 4) + Security Best Practices Checklist.xlsx