4.2 W3C Trace Context Specification
Key Takeaways
The W3C Trace Context specification is a formal web standard that guarantees end-to-end tracing interoperability across heterogeneous APM vendors and cloud platforms.
The traceparent HTTP header is a hyphen-delimited, 4-part string: version-trace_id-parent_id-trace_flags (e.g., 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01).
A TraceID must be exactly 32 lowercase hex characters (16 bytes) and cannot be all zeros; a SpanID (parent_id) must be exactly 16 lowercase hex characters (8 bytes) and cannot be all zeros.
The trace_flags field is an 8-bit bitmap represented as 2 hex characters: bit 0 (0x01) is the sampled flag, Level 2 defines bit 1 (0x02) as the random-trace-id flag, and the remaining bits are reserved and must be set to zero.
The tracestate header carries vendor-specific routing state as an opaque list of up to 32 key-value pairs; mutating intermediaries must prepend updated entries to the leftmost position while preserving untouched entries.
4.2 W3C Trace Context Specification
Prior to the standardization of distributed tracing, every application performance monitoring (APM) vendor and open-source tracing project invented its own proprietary wire protocol. Zipkin used X-B3-TraceId, Jaeger used uber-trace-id, AWS used X-Amzn-Trace-Id, and commercial vendors used proprietary request headers. When a transaction crossed organizational or platform boundaries—such as transitioning from an enterprise microservice through an external SaaS gateway, an open-source API proxy, or a third-party payment processor—these proprietary headers were routinely dropped, stripped by security firewalls, or mangled. Distributed traces were broken into isolated, uncorrelatable fragments.
To solve this universal interoperability challenge, the World Wide Web Consortium (W3C) published the W3C Trace Context Specification. Level 1 became a W3C Recommendation in November 2021; Level 2, which adds the random-trace-id flag and which the OpenTelemetry specification follows, is a Candidate Recommendation Draft. W3C Trace Context defines a universally accepted, standardized set of HTTP headers that allow distributed traces to propagate uninterrupted across heterogeneous systems, cloud providers, and observability vendors.
The specification defines two core HTTP headers:
traceparent: A mandatory header that carries the essential distributed tracing identifiers (version,trace_id,parent_id, andtrace_flags).tracestate: An optional companion header that carries vendor-specific routing and state metadata without compromising standard correlation.
The traceparent Header in Detail
The traceparent header represents the minimal distributed context required to correlate operations across systems. It consists of four hyphen-separated fields formatted as a single string:
+---------+----------------------------------+------------------+-------------+
| version | trace_id | parent_id | trace_flags |
| (2 hex) | (32 hex) | (16 hex) | (2 hex) |
+---------+----------------------------------+------------------+-------------+
| 00 | 4bf92f3577b34da6a3ce929d0e0e4736 | 00f067aa0ba902b7 | 01 |
+---------+----------------------------------+------------------+-------------+
Canonical Representation:
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
Field-by-Field Breakdown
1. version (2 Hexadecimal Characters)
- Specification Value: The current standard version is
00. - Invalid Values: The value
ffis explicitly forbidden by the specification and reserved for future protocol revisions. Any incoming header starting withff-must be treated as completely invalid and discarded. - Forward Compatibility: If an implementation receives a version greater than
00(e.g.,01,02), it must follow the forward-compatibility parsing rules described later in this section.
2. trace_id (32 Hexadecimal Characters / 16 Bytes / 128 Bits)
- Representation: Exactly 32 lowercase hexadecimal characters representing a 128-bit globally unique identifier (e.g.,
4bf92f3577b34da6a3ce929d0e0e4736). - Zero Check: The value cannot be all zeros (
00000000000000000000000000000000is invalid). If an implementation receives an all-zerotrace_id, it must discard the entiretraceparentheader as malformed and initiate a new trace. - Case Sensitivity: Uppercase characters (e.g.,
4BF9...) violate the specification. Standard parsers strictly enforce lowercase hex digits (0-9,a-f).
3. parent_id (16 Hexadecimal Characters / 8 Bytes / 64 Bits)
- Representation: Exactly 16 lowercase hexadecimal characters representing a 64-bit unique span identifier (e.g.,
00f067aa0ba902b7). This represents the caller's SpanID, which becomes theParentSpanIDof any child span created by the receiving service. - Zero Check: The value cannot be all zeros (
0000000000000000is invalid). If an implementation receives an all-zeroparent_id, it must reject the header.
4. trace_flags (2 Hexadecimal Characters / 1 Byte / 8-Bit Bitmap)
- Representation: Exactly 2 hexadecimal characters representing an 8-bit bitmap of runtime flags.
- Bit 0 (Least Significant Bit,
0x01/01) — The Recorded / Sampled Bit:01(Sampled): Indicates that the upstream caller selected this trace for recording and export. Downstream services should record and export spans for this trace unless their own local sampling policies explicitly override it.00(Not Sampled): Indicates that the upstream caller did not sample this trace. Downstream services should still propagate the context (to preserve trace continuity for deep services that might sample), but should avoid exporting spans to backends to conserve network bandwidth and storage.
- Bit 1 (
0x02) — The Random Trace ID Flag (Level 2): When set on a new trace, at least the rightmost 7 bytes of thetrace_idwere generated randomly. OpenTelemetry's newer consistent-probability samplers rely on this randomness. A sampled trace with random IDs therefore carries flags03. - Other Bits (Reserved): Not defined. The specification says vendors MUST set unknown flags to zero; a participant does not pass unknown flag bits through.
The tracestate Header in Detail
While traceparent provides a vendor-neutral trace identifier, real-world enterprise architectures often involve multiple APM systems, custom in-house tracing proxies, and specialized cloud routing meshes. These systems need to pass proprietary metadata—such as vendor-specific sampling rates, parent tenant IDs, or internal routing hops—without polluting the shared traceparent header.
The tracestate HTTP header provides an opaque, comma-separated list of key-value pairs designed specifically for this purpose:
tracestate: rojo=123,congo=t61rcWkgMzE,vendorX=opaqueValue
Syntax and Grammar Rules
- List Members: A comma-separated list of key-value pairs (
key=value). - Key Format:
- Simple Keys: Start with a lowercase letter, then up to 255 more characters drawn from lowercase letters, digits,
_,-,*, and/(e.g.,rojo,congo,vendor/x). Maximum 256 characters. - Multi-Tenant Keys:
tenant-id@system-id, for exampletenant123@vendora. The tenant part may start with a digit and is at most 241 characters; the system part starts with a lowercase letter and is at most 14 characters.
- Simple Keys: Start with a lowercase letter, then up to 255 more characters drawn from lowercase letters, digits,
- Value Format: An opaque string of up to 256 printable ASCII characters (0x20 to 0x7E) excluding comma
,and equals=; the last character may not be a space. - Maximum Member Limit: A
tracestateheader can contain at most 32 list members. If adding a member would exceed 32, the right-most member should be removed. - Size Guidance: Vendors SHOULD propagate at least 512 characters of the combined header. When truncation is unavoidable, whole entries are removed: entries longer than 128 characters first, then entries from the end of the list.
Mutation and Routing Rules
To ensure deterministic propagation and prevent unbounded growth, the W3C specification mandates strict rules for modifying tracestate:
- Left-to-Right Priority: The list is ordered by recency of modification from left to right. The leftmost entry is the most recently updated vendor state.
- Prepending on Mutation: When a system or vendor modifies its state or adds a new key-value pair, it must prepend its entry to the beginning (left) of the list. If that vendor's key already existed elsewhere in the list, the old entry must be removed before the updated entry is prepended to the left.
- Pass-Through Obligation: Any intermediary proxy, service, or gateway that does not recognize or need to modify entries in
tracestatemust forward the header completely untouched downstream.
Incoming Header:
tracestate: vendorB=dataB,vendorC=dataC
Intermediary Service (instrumented with vendorA) updates its state:
1. Formulates its entry: vendorA=dataA_new
2. Prepends to the leftmost position
Outgoing Header:
tracestate: vendorA=dataA_new,vendorB=dataB,vendorC=dataC
Forward Compatibility and Validation Rules
The W3C specification defines precise validation and forward-compatibility rules that every certified OpenTelemetry implementation must implement.
Validation Rules for Version 00
When a parser encounters a traceparent header beginning with 00-:
- Exact Four Fields: The header must contain exactly four fields separated by hyphens. If there are fewer than four fields or more than four fields, the header is strictly malformed and must be dropped.
- Delimiter Rigidity: The delimiters must be single hyphens (
-). No leading, trailing, or embedded spaces are permitted. - Length and Character Verification: The parser must verify that
versionis 2 hex characters,trace_idis 32 hex characters,parent_idis 16 hex characters, andtrace_flagsis 2 hex characters. Any non-hex character (e.g.,g-zor uppercaseA-F) invalidates the entire header. - Non-Zero Guarantee: Both
trace_idandparent_idmust be checked against zero. If either is all zeros, the header is discarded.
Forward Compatibility for Future Versions (> 00)
To ensure that older services do not crash or break distributed traces when a newer service emits a future version of the standard (e.g., version 01 or 02):
- If an implementation receives a
traceparentwith a version higher than00(and notff), it SHOULD still try to parse it:- If the header is shorter than 55 characters, it restarts the trace instead of parsing.
- It parses
trace_id(32 hex characters followed by a dash),parent_id(16 hex characters followed by a dash), and the flags (2 characters followed by either the end of the string or a dash). - Extra hyphen-separated fields (e.g.,
01-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01-extra-experimental-field) do not invalidate the header, but the implementation MUST NOT parse or assume anything about unknown fields. - When it propagates the context, it MUST build the new
traceparentin the highest version it knows (00today) from the fields it parsed, so the unknown future fields are not forwarded.
W3C Trace Context Specification Summary Table
The following table details the technical properties and specification rules of each component within the W3C Trace Context standard:
| Header | Component | Length / Format | Valid Values | Invalid / Rejection Conditions | Specification Purpose |
|---|---|---|---|---|---|
traceparent | version | 2 Hex characters (1 byte) | Current: 00. Future: 01 through fe. | Value ff is strictly forbidden. Non-hex characters. | Defines the wire format version of the header. |
traceparent | trace_id | 32 Hex characters (16 bytes / 128 bits) | 0-9, a-f (lowercase only). | All zeros (00000000000000000000000000000000), uppercase characters, length != 32. | Globally unique distributed transaction identifier shared across all spans in the trace. |
traceparent | parent_id | 16 Hex characters (8 bytes / 64 bits) | 0-9, a-f (lowercase only). | All zeros (0000000000000000), uppercase characters, length != 16. | Span identifier of the caller; becomes the ParentSpanID of downstream child spans. |
traceparent | trace_flags | 2 Hex characters (1 byte / 8-bit bitmap) | Bit 0 (0x01) = Sampled; bit 1 (0x02) = Random (Level 2); other bits set to zero. | Non-hex characters, length != 2. | Runtime processing flags; the sampled bit carries the upstream sampling decision. |
tracestate | key | Up to 256 characters | Lowercase letters, digits, _, -, *, /; optional single @ for tenant@system. | Uppercase letters, other punctuation, or a simple key that does not start with a lowercase letter. | Identifies the vendor or tenant owning the opaque state entry. |
tracestate | value | Up to 256 characters | Printable ASCII characters except comma , and equals =. | Characters , or =, non-printable ASCII, length > 256. | Stores opaque vendor-specific routing tokens, sampling weights, or internal system state. |
tracestate | list | Comma-separated entries | Up to 32 key-value members; at least 512 characters should be propagated. | More than 32 members (the right-most member is removed when prepending). | Preserves multi-vendor tracing state across heterogeneous intermediaries. |
A gateway microservice receives an incoming HTTP request containing the header 'traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000000-01'. When the OpenTelemetry SDK processes this request, how does it handle the trace context?
It rejects the header as malformed because the parent_id is all zeros, generates a new TraceID and SpanID, and starts a fresh root trace.
It accepts the TraceID, treats the all-zero parent_id as indicating a root span, and creates a child span linked to that TraceID.
It drops the incoming HTTP request immediately and returns an HTTP 400 Bad Request error to the calling client.
It preserves the TraceID and flags, but prompts the head sampler to dynamically query the client for a valid SpanID.
An enterprise microservice receives an HTTP request containing the header 'tracestate: vendorX=100,vendorY=200'. The microservice is instrumented with Vendor X's OpenTelemetry distribution, which updates its internal routing priority from 100 to 500. According to W3C Trace Context rules, what must the outgoing tracestate header look like when this service makes a downstream call?
tracestate: vendorX=100,vendorY=200,vendorX=500
tracestate: vendorX=500,vendorY=200
tracestate: vendorY=200,vendorX=500
tracestate: vendorX=500
An OpenTelemetry-instrumented proxy receives 'traceparent: 02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01-regional-route-us-east'. Its SDK implements version 00 of W3C Trace Context. How should it handle this header?
Discard the header, because only version 00 headers may be accepted
Reject the header, because a traceparent may never contain more than four fields
Parse the trace-id, parent-id, and flags from the first fields, ignore the unknown trailing field, and emit a version-00 traceparent on outgoing requests
Forward the whole header unchanged without extracting any trace context
Sections you finish are checked off in the contents.