Skip to content

Cloudwerk provides comprehensive security middleware to protect your application from common web vulnerabilities. This guide covers security best practices and how to implement them.

The fastest way to secure your app is with the combined security middleware:

// app/middleware.ts
import { securityMiddleware } from '@cloudwerk/security/middleware'
export const middleware = securityMiddleware({
allowedOrigins: ['https://myapp.com'],
})

This enables:

  • CSRF protection - Validates tokens on mutation requests
  • X-Requested-With validation - Forces CORS preflight
  • Security headers - Sets protective HTTP headers

Cross-Site Request Forgery (CSRF) attacks trick authenticated users into making unwanted requests. Cloudwerk uses the double-submit cookie pattern:

  1. Server sets a CSRF token in a cookie
  2. Client sends the same token in a header
  3. Server validates they match
// Server validates on mutation requests (POST, PUT, PATCH, DELETE)
// Cookie: cloudwerk.csrf-token=abc123
// Header: X-CSRF-Token: abc123

The X-Requested-With: XMLHttpRequest header provides defense-in-depth:

  • Custom headers trigger CORS preflight requests
  • Cross-origin attackers can’t set custom headers without preflight approval
  • Acts as an additional layer even if CSRF is somehow bypassed

Cloudwerk sets these headers by default:

HeaderDefaultPurpose
X-Content-Type-OptionsnosniffPrevents MIME type sniffing
X-Frame-OptionsDENYPrevents clickjacking
Referrer-Policystrict-origin-when-cross-originControls referrer information
X-XSS-Protection0Disabled (obsolete, can cause issues)

Use the client helpers to automatically include security headers:

import { secureFetch } from '@cloudwerk/security/client'
// Automatically adds X-CSRF-Token and X-Requested-With
const response = await secureFetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice' }),
})

For traditional form submissions, include the CSRF token as a hidden field:

import { getCsrfToken } from '@cloudwerk/security/client'
function ContactForm() {
return (
<form method="POST" action="/api/contact">
<input type="hidden" name="csrf_token" value={getCsrfToken() ?? ''} />
<input type="email" name="email" />
<button type="submit">Submit</button>
</form>
)
}

The csrfInput() helper returns an HTML string for use in templates:

import { csrfInput } from '@cloudwerk/security/client'
// Returns: '<input type="hidden" name="csrf_token" value="..." />'
const inputHtml = csrfInput()

Some endpoints like webhooks need to bypass security checks:

export const middleware = securityMiddleware({
csrf: {
excludePaths: ['/api/webhooks/stripe', '/api/webhooks/github'],
},
requestedWith: {
excludePaths: ['/api/webhooks'],
},
})

CSP is disabled by default because it requires app-specific configuration:

export const middleware = securityMiddleware({
csp: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", 'https://cdn.example.com'],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'", 'https://api.example.com'],
},
reportOnly: true, // Test before enforcing
},
})

For additional cross-origin protection, validate the Origin header:

export const middleware = securityMiddleware({
allowedOrigins: ['https://myapp.com'],
origin: {
allowSubdomains: true, // Also allows api.myapp.com, admin.myapp.com
rejectMissingOrigin: false, // Allow requests without Origin (needed for some webhooks)
},
})

After authentication state changes, rotate the CSRF token to prevent session fixation:

import { rotateCsrfToken } from '@cloudwerk/security'
export async function handleLogin(request: Request) {
const user = await validateCredentials(request)
const session = await createSession(user)
let response = createAuthResponse(user, session)
response = rotateCsrfToken(response) // New token bound to session
return response
}

You can disable specific protections if needed:

export const middleware = securityMiddleware({
csrf: false, // Disable CSRF (not recommended)
requestedWith: false, // Disable X-Requested-With validation
headers: false, // Disable security headers
})

Use individual middleware functions for fine-grained control:

import {
csrfMiddleware,
securityHeadersMiddleware,
cspMiddleware,
originValidationMiddleware,
requestedWithMiddleware,
} from '@cloudwerk/security/middleware'
// Apply only what you need
export const middleware = csrfMiddleware({
excludePaths: ['/api/webhooks'],
})
  1. Enable security middleware on all routes that handle user data

  2. Use secureFetch for all API requests from the client

  3. Rotate CSRF tokens after login, logout, and password changes

  4. Configure CSP once your app is stable

  5. Verify webhooks using provider signatures, not just CSRF bypass

  6. Set allowed origins for production deployments

  7. Test with report-only before enforcing CSP

The request is missing required security headers. Use secureFetch() or manually add:

  • X-CSRF-Token header with the token from the cookie
  • X-Requested-With: XMLHttpRequest header

The CSRF token rotates after authentication. Fetch a new token or reload the page to get the updated cookie.

Either add the script source to scriptSrc, use nonces, or move the script to an external file.