Skip to content

Sse Cors

DodaTech 5 min read

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

  1. What CORS headers are required for SSE?
  2. How do you handle CORS preflight (OPTIONS) requests for SSE?
  3. What is the purpose of the withCredentials option?
  4. Why can you not use wildcard origin with credentials?
  5. 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

Does EventSource send cookies automatically?

No, EventSource does not send cookies by default. Set withCredentials: true to include cookies in the request.

Can I use wildcard origin with SSE?

Yes, if you do not need credentials. With withCredentials: true, you must specify exact origins.

Why does my SSE work locally but not in production?

CORS is typically the issue. Localhost requests are same-origin. Production often involves cross-origin requests requiring CORS headers.

How do I handle CORS for multiple subdomains?

Set the origin dynamically based on the request's Origin header, validated against a list of allowed subdomains.

Does CORS affect SSE reconnection?

Yes, if the Last-Event-ID header is not in Access-Control-Allow-Headers, the browser may strip it during reconnection.

What is the Vary: Origin header?

The Vary: Origin header tells caches to store different responses for different origins, preventing cache poisoning.

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