Sse Cors
title: "SSE CORS Configuration" description: "Learn how to configure Cross-Origin Resource Sharing (CORS) for Server-Sent Events to enable cross-origin event streaming in browsers." weight: 21 date: 2026-06-28 lastmod: 2026-06-28 tags: ["apis", "sse"]
CORS configuration is essential when your SSE server and client are on different origins. Without proper CORS headers, browsers block cross-origin EventSource requests.
## What You'll Learn
- CORS requirements for SSE
- Required CORS headers
- Credentials and cookies in SSE
- Preflight requests and SSE
- Debugging CORS issues
## Why It Matters
Modern web applications often serve APIs from different domains than the frontend. Proper CORS configuration enables SSE to work in these distributed architectures.
## Real-World Use
A microservices architecture has SSE endpoints on `api.events.example.com` while the frontend runs on `app.example.com`. CORS headers allow the browser to connect to the cross-origin SSE endpoint.
## Flow Chart
```mermaid
sequenceDiagram
participant B as Browser (app.example.com)
participant S as Server (api.events.example.com)
B->>S: GET /events
Note over B: Origin: https://app.example.com
S-->>B: 200 text/event-stream
Note over S: Access-Control-Allow-Origin: https://app.example.com
B->>B: Allow connection
Code Examples
Example 1: CORS Middleware for SSE
const express = require('express');
const cors = require('cors');
const app = express();
// CORS configuration
const corsOptions = {
origin: ['https://app.example.com', 'https://admin.example.com'],
methods: ['GET'],
allowedHeaders: ['Content-Type', 'Last-Event-ID'],
credentials: true,
maxAge: 86400,
};
// Apply CORS to SSE route specifically
const sseCors = cors(corsOptions);
app.get('/events', sseCors, (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
// Additional headers for CORS
'Access-Control-Allow-Origin': corsOptions.origin[0],
'Access-Control-Allow-Credentials': 'true',
});
// Handle preflight OPTIONS request
if (req.method === 'OPTIONS') {
res.writeHead(204);
res.end();
return;
}
const interval = setInterval(() => {
res.write(`data: ${JSON.stringify({ time: Date.now() })}\n\n`);
}, 2000);
req.on('close', () => clearInterval(interval));
});
// Alternative: Global CORS
app.use(cors());
app.listen(3000);
Expected output: SSE endpoint allows cross-origin requests from configured origins with credentials support.
Example 2: Raw Node.js CORS Handling
const http = require('http');
const ALLOWED_ORIGINS = [
'https://app.example.com',
'https://staging.example.com',
];
http.createServer((req, res) => {
const origin = req.headers.origin;
const isAllowedOrigin = ALLOWED_ORIGINS.includes(origin);
// Handle CORS preflight
if (req.method === 'OPTIONS') {
if (isAllowedOrigin) {
res.writeHead(204, {
'Access-Control-Allow-Origin': origin,
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Last-Event-ID',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Max-Age': '86400',
});
} else {
res.writeHead(204);
}
res.end();
return;
}
if (req.url === '/events') {
// Set CORS headers for the SSE response
if (isAllowedOrigin) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
} else if (origin) {
res.writeHead(403);
res.end('Origin not allowed');
return;
}
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
const interval = setInterval(() => {
res.write(`data: ${JSON.stringify({ message: 'SSE event' })}\n\n`);
}, 2000);
req.on('close', () => clearInterval(interval));
} else {
res.writeHead(404);
res.end();
}
}).listen(3000);
console.log('SSE server with CORS on port 3000');
Expected output: Raw Node.js SSE server with proper CORS preflight handling and origin validation.
Example 3: Client-Side CORS Configuration
// Client-side EventSource with credentials
const source = new EventSource('https://api.events.example.com/events', {
withCredentials: true, // Send cookies with cross-origin requests
});
source.onmessage = (event) => {
console.log('Cross-origin event:', event.data);
};
source.onerror = (event) => {
if (source.readyState === EventSource.CLOSED) {
console.error('CORS error: Connection failed');
// Check common CORS issues
if (event.message?.includes('CORS')) {
console.error(
'CORS error detected. Ensure the server sends ' +
'Access-Control-Allow-Origin header.'
);
}
}
};
// Test CORS configuration
async function testCORS() {
try {
const response = await fetch('https://api.events.example.com/events', {
method: 'OPTIONS',
headers: {
'Origin': window.location.origin,
},
});
const corsHeaders = {
origin: response.headers.get('Access-Control-Allow-Origin'),
credentials: response.headers.get('Access-Control-Allow-Credentials'),
methods: response.headers.get('Access-Control-Allow-Methods'),
};
console.log('CORS headers:', corsHeaders);
if (corsHeaders.origin === '*' || corsHeaders.origin === window.location.origin) {
console.log('CORS is properly configured');
} else {
console.warn('CORS may be misconfigured');
}
} catch (error) {
console.error('CORS test failed:', error.message);
}
}
testCORS();
Expected output: Client connects to cross-origin SSE endpoint with credentials, including CORS testing utility.
Common Mistakes
| Mistake | Explanation |
|---|---|
| Setting Access-Control-Allow-Origin to * with credentials | When using withCredentials, the origin must be explicit, not wildcard |
| Forgetting OPTIONS preflight | Browsers send OPTIONS before GET for cross-origin requests with non-standard headers |
| Ignoring Last-Event-ID in allowed headers | The Last-Event-ID header must be in Access-Control-Allow-Headers for reconnection |
| Not validating origin server-side | Always validate the Origin header server-side to prevent unauthorized access |
| Missing Vary: Origin header | Without Vary: Origin, caches may serve incorrect CORS headers to different origins |
Practice Questions
- What CORS headers are required for SSE?
- How do you handle CORS preflight (OPTIONS) requests for SSE?
- What is the purpose of the withCredentials option?
- Why can you not use wildcard origin with credentials?
- How do you debug CORS errors for SSE?
Challenge
Set up an SSE infrastructure with two different frontends (app.example.com and admin.example.com) connecting to a single SSE API endpoint. Implement proper CORS configuration with origin validation, credentials, and preflight handling.
FAQ
Mini Project
Build a multi-origin SSE infrastructure with three services: a frontend on app.example.com, an admin panel on admin.example.com, and an SSE API on api.example.com. Implement proper CORS for both origins, test with and without credentials, and add debugging endpoints for CORS validation.
What's Next
Build a complete SSE project
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro