Skip to content
Plurafy

CORS and Security Headers

Handling cross-origin requests and security

Safely handle cross-origin requests and protect against common web vulnerabilities.

CORS controls which origins can access your resources. Security headers provide additional protection.

const express = require('express');
const cors = require('cors');
const app = express();
// Enable CORS for all routes
app.use(cors());
// CORS for specific route
app.get('/api/data', cors(), (req, res) => {
res.json({ data: 'public' });
});
// Configure CORS options
app.use(cors({
origin: 'https://example.com',
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 3600
}));
// Preflight requests
app.options('*', cors());
app.use((req, res, next) => {
const origin = req.headers.origin;
const allowedOrigins = [
'http://localhost:3000',
'https://example.com'
];
if (allowedOrigins.includes(origin)) {
res.header('Access-Control-Allow-Origin', origin);
}
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Max-Age', '3600');
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
const helmet = require('helmet');
app.use(helmet());
// Or configure manually
app.use((req, res, next) => {
// Prevent clickjacking
res.header('X-Frame-Options', 'DENY');
// Prevent MIME type sniffing
res.header('X-Content-Type-Options', 'nosniff');
// XSS protection
res.header('X-XSS-Protection', '1; mode=block');
// Content Security Policy
res.header(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
);
// Referrer Policy
res.header('Referrer-Policy', 'strict-origin-when-cross-origin');
// Permissions Policy
res.header(
'Permissions-Policy',
'camera=(), microphone=(), geolocation=()'
);
next();
});
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
const session = require('express-session');
app.use(cookieParser());
app.use(session({ secret: 'secret' }));
const csrfProtection = csrf({ cookie: false });
// Generate CSRF token
app.get('/form', csrfProtection, (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// Verify CSRF token
app.post('/form', csrfProtection, (req, res) => {
res.json({ message: 'Form accepted' });
});
const csp = require('helmet-csp');
app.use(csp({
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'", 'cdn.example.com'],
scriptSrc: ["'self'", 'cdn.example.com'],
imgSrc: ["'self'", 'data:', 'https:'],
frameSrc: ["'none'"],
objectSrc: ["'none'"]
}
}));
app.set('trust proxy', 1);
app.use(session({
secret: 'secret',
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JavaScript access
sameSite: 'strict',// CSRF protection
maxAge: 1000 * 60 * 60 * 24 // 24 hours
}
}));
// Enforce HTTPS
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https') {
res.redirect(`https://${req.header('host')}${req.url}`);
} else {
next();
}
});
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests
message: 'Too many requests'
});
app.use(limiter);
// Specific routes
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
skipSuccessfulRequests: true
});
app.post('/login', loginLimiter, (req, res) => {
// Handle login
});
const { body, validationResult } = require('express-validator');
app.post('/user', [
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
body('name').trim().escape()
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Process validated input
});