What Are HTTP Trailers?
HTTP trailers are headers sent after the response body. They solve a specific problem: how do you include metadata that is only known once the body has been fully generated? You cannot put a checksum or a total row count in the initial response headers if you are streaming the data.
Trailers are an underused but powerful feature for streaming APIs, gRPC-over-HTTP, and integrity verification of large transfers.
Required Headers
For HTTP/1.1, trailers require two setup steps:
- The response must use chunked transfer encoding (
Transfer-Encoding: chunked) - The server must declare which trailers to expect via the
Trailerheader
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Trailer: X-Checksum, X-Row-Count
5\r\n
Hello\r\n
6\r\n
World\r\n
0\r\n
X-Checksum: sha256=abc123...\r\n
X-Row-Count: 42\r\n
\r\n
The client TE request header signals trailer support:
TE: trailers, gzip
Without TE: trailers, the server should not send trailers to HTTP/1.1 clients as they may not be able to parse them.
HTTP/2 and HTTP/3 Trailers
HTTP/2 makes trailers first-class. They are delivered as a HEADERS frame with the END_STREAM flag set, after all DATA frames:
HEADERS frame (status 200, response headers)
DATA frame (partial body)
DATA frame (more body)
HEADERS frame (trailers) [END_STREAM=1]
HTTP/2 trailers do not require chunked encoding (HTTP/2 has its own framing) and do not need the Trailer declaration header. HTTP/3 follows the same model.
gRPC Status Trailers
gRPC runs on HTTP/2 and uses trailers as its primary error signaling mechanism. Every gRPC response stream ends with trailer fields:
grpc-status: 0 # 0 = OK, non-zero = error
grpc-message: OK
grpc-status-details-bin: <base64-encoded protobuf>
This is why gRPC requires HTTP/2 — the protocol semantically depends on trailer support. A grpc-status: 14 trailer (UNAVAILABLE) after streaming 1,000 rows tells the client the stream was aborted, even though response headers already showed 200 OK.
Practical Use Cases
Streaming Checksums
Verifying data integrity is impossible in headers when you do not know the hash before streaming starts. Trailers solve this elegantly:
# FastAPI streaming response with checksum trailer
import hashlib
from fastapi.responses import StreamingResponse
async def generate_and_hash(data):
hasher = hashlib.sha256()
for chunk in data:
hasher.update(chunk)
yield chunk
# Trailer would be appended here in HTTP/2
# Most frameworks need explicit trailer support
# Note: Full trailer support varies by framework
Server-Timing Trailers
The Server-Timing header can be sent as a trailer to report actual processing time after the response is complete — more accurate than an estimate in the initial headers:
# As a trailer (after streaming):
Server-Timing: db;dur=23.5, render;dur=45.2, total;dur=68.7
Browser DevTools display Server-Timing trailer values in the Timing tab.
Streaming Error Reporting
A 200 OK response can be sent before processing completes. If an error occurs mid-stream, trailers communicate the failure:
HTTP/2 200 OK
[... partial data frames ...]
[stream aborted due to DB error]
Trailer: grpc-status = 13 (INTERNAL)
Trailer: grpc-message = 'Database connection lost at row 5000'
Browser and Client Support
Fetch API
The Fetch API exposes trailers via the ReadableStream trailer promise (experimental):
const response = await fetch('/stream');
const reader = response.body.getReader();
// Consume all chunks
while (true) {
const { done, value } = await reader.read();
if (done) break;
process(value);
}
// Trailers available after stream completes (when supported)
// response.trailers is a Promise<Headers> in the Fetch spec
const trailers = await response.trailers;
console.log(trailers.get('x-checksum'));
curl Support
# curl shows trailers after the body with --verbose:
curl --http2 -v https://api.example.com/stream 2>&1 | grep -A5 '< '
Framework Support Matrix
| Framework | Trailer Support | Notes |
|---|---|---|
| gRPC (Go/Java/Python) | Native | Core to gRPC protocol |
| Nginx (upstream) | Passes through | Does not generate trailers |
| Node.js http2 | Native | `response.addTrailers()` |
| Django/Python | Limited | No built-in trailer API |
| curl | Read/Write | Full support with `--http2` |
Trailers are most useful when you control both client and server — as in gRPC or internal streaming pipelines — rather than in public browser-facing APIs where client support remains inconsistent.