How to Secure SSR in React

Server-Side Rendering (SSR) in React enhances performance and search engine optimization, but it also introduces unique security risks that do not exist in client-side-only applications. This article provides a straightforward guide on how to secure your React SSR applications against critical vulnerabilities, such as Cross-Site Scripting (XSS), data leaks, and server-side Denial of Service (DoS) attacks.

1. Prevent XSS during Data Hydration

The most common security vulnerability in React SSR involves transferring state from the server to the client, a process known as hydration. Often, developers serialize state on the server and inject it into the HTML document inside a <script> tag like this:

<script>
  window.__PRELOADED_STATE__ = ${JSON.stringify(state)};
</script>

If the state contains user-generated input with malicious code (e.g., </script><script>alert('XSS')</script>), the browser will execute it.

How to fix it:

Never use raw JSON.stringify to inject data into script tags. Instead, use serialization libraries designed to escape HTML entities and prevent script injection.

import serialize from 'serialize-javascript';

// Safe serialization
const safeState = serialize(state, { isJSON: true });

2. Sanitize Rendered HTML

React’s dangerouslySetInnerHTML is inherently risky, but it is doubly dangerous in an SSR environment. If you render raw HTML on the server without sanitizing it first, malicious scripts can execute on both the server (if using certain Node environments) and the client.

How to fix it:

import DOMPurify from 'isomorphic-dompurify';

const cleanHtml = DOMPurify.sanitize(userContent);
const Component = () => <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />;

3. Implement a Robust Content Security Policy (CSP)

A Content Security Policy (CSP) is a critical layer of defense that restricts the resources (such as JavaScript, CSS, and images) that the browser is allowed to load.

How to fix it:

For SSR applications, use a nonce-based CSP. 1. Generate a unique, cryptographically strong random token (nonce) on the server for every single request. 2. Include this nonce in your CSP HTTP response header. 3. Apply the same nonce to all inline <script> tags in your React HTML template.

Content-Security-Policy: default-src 'self'; script-src 'nonce-RndmKey123' 'strict-dynamic';

Only scripts with the matching nonce="RndmKey123" will execute, blocking unauthorized inline scripts injected by attackers.

4. Prevent Environment Variable Leaks

In SSR, code is executed on the server before the final page is sent to the client. It is easy to accidentally bundle sensitive server-side environment variables (like API keys, database credentials, or private tokens) into the client-side JavaScript bundle.

How to fix it:

5. Mitigate Server-Side Denial of Service (DoS)

React SSR is a CPU-intensive process because the server must render the entire component tree into an HTML string for every request. Attackers can exploit this by sending a high volume of requests, overloading the server’s CPU and crashing the application.

How to fix it: