SecurityFrontend

How to Secure your Single Page Application

Sep 1, 20247 min readRami Mustaklem

Why SPA security is different

Single Page Applications shift a lot of logic to the browser — which means the attack surface shifts too. Unlike traditional server-rendered apps, your SPA is running your code in an environment you don’t control. This changes how you need to think about XSS, CSRF, authentication, and data exposure.

Cross-Site Scripting (XSS)

XSS is the most common SPA vulnerability. It happens when an attacker injects malicious scripts into your app’s output. Modern frameworks like React escape output by default — but there are still ways to get burned.

  • Never use dangerouslySetInnerHTML with untrusted content
  • Sanitize any user-generated HTML with a library like DOMPurify
  • Set a strict Content Security Policy (CSP) header
  • Avoid storing sensitive data in localStorage — it’s accessible to any script on the page

CSRF Protection

Cross-Site Request Forgery tricks authenticated users into making requests they didn’t intend. If your API uses cookie-based auth, you need CSRF protection.

  • Use SameSite=Strict or SameSite=Lax on session cookies
  • Validate the Origin and Referer headers server-side
  • If using Laravel, the built-in CSRF middleware handles this automatically for state-changing requests

Authentication strategies

The session-vs-JWT debate matters here. For SPAs consuming your own API, HttpOnly cookies with short-lived sessions are generally safer than storing JWTs in localStorage. See my related post on this topic.

If a JWT is stolen from localStorage, an attacker can use it indefinitely until it expires. A stolen HttpOnly cookie is much harder to extract via JavaScript.

Security headers

A hardened set of HTTP response headers goes a long way. Configure these at your CDN or server level:

Content-Security-Policy: default-src 'self'; script-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

Dependency hygiene

Your app is only as secure as its weakest dependency. Run npm audit regularly, pin versions where possible, and audit third-party scripts before adding them. Supply chain attacks are increasingly common.


Security isn’t a feature you add at the end — it’s a mindset you apply throughout. Start with the basics above and you’ll be ahead of the vast majority of SPAs in production today.

R
Rami Mustaklem
Senior Full Stack Developer