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

Description

Anything 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.

Example
webpack-bundle-analyzer + a CI grep for
/sk_live_|AKIA|BEGIN PRIVATE KEY/ across dist/

2. HTTPS + HSTS Everywhere

Description

Every 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.

Example
Strict-Transport-Security: max-age=63072000;
includeSubDomains; preload

3. Strict Content-Security-Policy

Description

Set 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.

Example
Content-Security-Policy: default-src 'self';
script-src 'self' 'nonce-r4nd0m'; object-src 'none'

4. Full HTTP Security Header Set

Description

Beyond 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.

Example
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=()

5. Sanitize Before You Render

Description

Any 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

Description

Pair 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

Description

Session 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.

Example
Set-Cookie: session=...; HttpOnly; Secure; SameSite=Strict

8. Strict CORS Allowlist

Description

Never 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.

Example
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true

9. Subresource Integrity on Third-Party Scripts

Description

Any 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

Description

localStorage, 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

Description

Every 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.

Example
Frontend: <input required maxlength=100>
Backend:  z.string().min(1).max(100) — re-checked, not trusted

12. Third-Party Dependency Auditing

Description

Run 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.

Example
npm audit --audit-level=high
(fails CI on high/critical vulnerabilities)

13. Server-Side Access Control Enforcement

Description

Hiding 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

Description

Apply 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.

Example
Cloudflare rate limiting rule:
>20 req/min to /api/auth/* from one IP
→ challenge or block

15. noopener / noreferrer on Cross-Origin Links

Description

Any 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