Skip to content

SSE Headers and Configuration — Complete Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about SSE Headers and Configuration. We cover key concepts, practical examples, and best practices to help you master this topic.

Configure SSE headers: Content-Type, Cache-Control, Connection, X-Accel-Buffering, CORS, proxy configuration, and server-specific headers for reliable Server-Sent Events streaming.

What You Learn

You will learn the required and recommended HTTP headers for SSE, how to configure them in different frameworks, how proxies affect SSE, and how to tune headers for production streaming.

Why It Matters

Missing or incorrect headers break SSE. Without Content-Type: text/event-stream, the browser buffers the response. Without Cache-Control: no-cache, proxies cache the stream. Proper headers are essential for SSE to work.

Required Headers

from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import time

class CorrectHeadersSSE(BaseHTTPRequestHandler):
    def do_GET(self):
        # Required headers
        self.send_response(200)
        self.send_header('Content-Type', 'text/event-stream')
        self.send_header('Cache-Control', 'no-cache')
        self.send_header('Connection', 'keep-alive')

        # Recommended headers
        self.send_header('X-Accel-Buffering', 'no')  # nginx
        self.send_header('Access-Control-Allow-Origin', '*')  # CORS

        self.end_headers()

        self.wfile.write(b"data: Headers configured correctly\n\n")

Header Reference

headers_guide = {
    'required': {
        'Content-Type': 'text/event-stream',
        'Description': 'Tells the browser to treat the response as an SSE stream',
    },
    'caching': {
        'Cache-Control': 'no-cache, no-store',
        'Pragma': 'no-cache',
        'Expires': '0',
        'Description': 'Prevents browsers and proxies from caching the stream',
    },
    'connection': {
        'Connection': 'keep-alive',
        'Description': 'Keeps the TCP connection open for streaming',
    },
    'proxy': {
        'X-Accel-Buffering': 'no',
        'Description': 'Disables nginx buffering for SSE',
    },
    'cors': {
        'Access-Control-Allow-Origin': '*',
        'Description': 'Allows cross-origin SSE connections',
    },
    'compression': {
        'Content-Encoding': 'identity',
        'Description': 'Disables compression (compression buffers the stream)',
    },
}

for category, info in headers_guide.items():
    print(f"\n{category.upper()}:")
    for header, value in info.items():
        if header != 'Description':
            print(f"  {header}: {value}")
    print(f"  -> {info['Description']}")

Expected output:

REQUIRED:
  Content-Type: text/event-stream
  -> Tells the browser to treat the response as an SSE stream

CACHING:
  Cache-Control: no-cache, no-store
  Pragma: no-cache
  Expires: 0
  -> Prevents browsers and proxies from caching the stream

Framework-Specific Headers

# FastAPI
from fastapi.responses import StreamingResponse

def get_sse_response(generator):
    return StreamingResponse(
        generator(),
        media_type='text/event-stream',
        headers={
            'Cache-Control': 'no-cache',
            'X-Accel-Buffering': 'no',
            'Connection': 'keep-alive',
        },
    )

# Django
from django.http import StreamingHttpResponse

def get_sse_response(generator):
    response = StreamingHttpResponse(
        streaming_content=generator(),
        content_type='text/event-stream',
    )
    response['Cache-Control'] = 'no-cache'
    response['X-Accel-Buffering'] = 'no'
    return response

# Express.js
# res.writeHead(200, {
#     'Content-Type': 'text/event-stream',
#     'Cache-Control': 'no-cache',
#     'Connection': 'keep-alive',
#     'X-Accel-Buffering': 'no',
# });

Nginx Configuration for SSE

# /etc/nginx/sites-available/app

server {
    listen 80;
    server_name api.dodatech.com;

    # SSE endpoints need special proxy configuration
    location /sse/ {
        proxy_pass http://backend:3000;

        # Disable buffering for SSE
        proxy_buffering off;
        proxy_cache off;

        # Increase buffer for streaming
        proxy_buffer_size 4k;

        # Timeout settings for long-lived connections
        proxy_read_timeout 86400s;  # 24 hours
        proxy_send_timeout 86400s;

        # HTTP/1.1 for keep-alive
        proxy_http_version 1.1;

        # Headers for SSE
        proxy_set_header Connection '';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # Disable compression for SSE
        gzip off;
    }

    # Regular API endpoints
    location /api/ {
        proxy_pass http://backend:3000;
        proxy_buffering on;
    }
}

Apache Configuration for SSE

# /etc/apache2/sites-available/app.conf

<VirtualHost *:80>
    ServerName api.dodatech.com

    # SSE proxy configuration
    ProxyPass /sse/ http://backend:3000/sse/ timeout=86400
    ProxyPassReverse /sse/ http://backend:3000/sse/

    # Disable buffering for SSE
    SetEnv proxy-sendcl 1
    SetEnv proxy-receivebuffer 0

    # Keep connection alive
    KeepAlive On
    KeepAliveTimeout 300
    MaxKeepAliveRequests 100

    # CORS headers for SSE
    Header always set Access-Control-Allow-Origin "*"
    Header always set Access-Control-Allow-Methods "GET, OPTIONS"
    Header always set Access-Control-Allow-Headers "Content-Type, Last-Event-ID"
</VirtualHost>

Headers for Production

import time
import json

class ProductionSSEHeaders:
    @staticmethod
    def get_headers(origin=None):
        headers = {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache, no-store, must-revalidate',
            'Pragma': 'no-cache',
            'Expires': '0',
            'Connection': 'keep-alive',
            'X-Accel-Buffering': 'no',
            'X-Content-Type-Options': 'nosniff',
            'X-Robots-Tag': 'noindex',
        }

        if origin:
            headers['Access-Control-Allow-Origin'] = origin

        return headers

    @staticmethod
    def set_custom_headers(response, headers):
        for key, value in headers.items():
            response.set_header(key, value)

    @staticmethod
    def log_headers(headers):
        safe_headers = {k: v for k, v in headers.items() if k != 'Authorization'}
        print(f"SSE response headers: {json.dumps(safe_headers, indent=2)}")

Common Mistakes

1. Missing Content-Type

Without text/event-stream, the browser treats the response as regular content and buffers all of it before delivering. SSE never fires.

2. Allowing Cache

Without Cache-Control: no-cache, browsers and proxies cache the response. Cached SSE streams deliver stale events or never update.

3. Compression Enabled

Gzip/deflate compression buffers the entire response, defeating real-time delivery. Disable compression for SSE endpoints.

4. Proxy Buffering

nginx buffers proxy responses by default. An SSE stream is buffered until full, then delivered in chunks. Disable proxy_buffering for SSE routes.

5. Short Timeouts

Default proxy timeouts are 60 seconds. SSE connections last hours. Increase proxy_read_timeout to 24 hours or more.

Practice Questions

1. What is the most important header for SSE?

Content-Type: text/event-stream. Without it, the browser does not know the response is an SSE stream.

2. Why disable compression for SSE?

Compression buffers data before compressing, which delays delivery. SSE needs real-time delivery, not compressed bulk transfer.

3. How does nginx buffering affect SSE?

nginx buffers proxy responses by default. An SSE stream is held in the buffer until it is full, causing delayed or batched delivery.

4. What header disables nginx buffering for SSE?

X-Accel-Buffering: no. Alternatively, configure proxy_buffering off; in the nginx config.

Challenge

Configure a production SSE deployment with: nginx reverse proxy with proxy_buffering off and 24-hour timeout, correct SSE headers set by the application, CORS allowing the dashboard domain only, disable compression for SSE routes only, and header logging for debugging.

FAQ

Can I use Transfer-Encoding: chunked with SSE?

Yes. SSE uses chunked transfer encoding by default with HTTP/1.1. Each event is sent as a chunk.

Should I set Content-Length for SSE?

No. SSE streams have unknown length. Do not set Content-Length. Use chunked transfer encoding.

Does HTTP/2 change SSE headers?

HTTP/2 does not change SSE headers. The same Content-Type, Cache-Control, and CORS headers apply. HTTP/2 removes the 6-connection limit.

How do I debug SSE header issues?

Use browser DevTools Network tab. Check the response headers of the SSE request. Look for Content-Type: text/event-stream and Cache-Control: no-cache.

Can I set custom headers with EventSource?

No. The EventSource API does not support custom headers. For auth tokens, use URL parameters or cookies.

Mini Project: Header Validation

import json

def validate_sse_headers(headers):
    issues = []

    required = {
        'Content-Type': 'text/event-stream',
    }

    recommended = {
        'Cache-Control': lambda v: 'no-cache' in (v or ''),
        'Connection': lambda v: 'keep-alive' in (v or ''),
    }

    for header, expected in required.items():
        actual = headers.get(header)
        if actual != expected:
            issues.append(f"Missing or wrong {header}: got '{actual}', expected '{expected}'")

    for header, validator in recommended.items():
        if not validator(headers.get(header)):
            issues.append(f"Recommended header missing: {header}")

    return issues

test_headers_correct = {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
}

test_headers_wrong = {
    'Content-Type': 'text/html',
    'Cache-Control': 'max-age=3600',
}

print("Correct headers:")
for issue in validate_sse_headers(test_headers_correct):
    print(f"  ISSUE: {issue}")
if not validate_sse_headers(test_headers_correct):
    print("  All headers valid")

print("\nWrong headers:")
for issue in validate_sse_headers(test_headers_wrong):
    print(f"  ISSUE: {issue}")

Expected output:

Correct headers:
  All headers valid

Wrong headers:
  ISSUE: Missing or wrong Content-Type: got 'text/html', expected 'text/event-stream'
  ISSUE: Recommended header missing: Cache-Control
  ISSUE: Recommended header missing: Connection

What's Next

Now that you understand SSE headers, explore SSE and CORS, then learn about SSE in production.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro