SSE Headers and Configuration — Complete Guide
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
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