How do W3C and B3 propagation differ, and how do you configure which one Spring uses?
answer
- W3C = single traceparent header, Boot 3 default
- B3 = Zipkin/Brave: single b3 or X-B3-* multi-headers
- management.tracing.propagation.type = W3C | B3
- produce vs consume lists for multi-format migration
- mismatched formats silently break the trace
basics
~20 sBoth encode the same trace/span IDs but in different headers. W3C uses one traceparent header (the Boot 3 default); B3 (from Zipkin/Brave) uses either a single b3 header or multiple X-B3-* headers. Set management.tracing.propagation.type to W3C or B3.
solid answer
~40 sW3C Trace Context and B3 are two wire formats for the same idea. **W3C** — the Spring Boot 3 default — puts everything in one `traceparent` header (`version-traceId-spanId-flags`) plus optional `tracestate`. **B3**, originating from Zipkin/Brave, comes in two flavours: single-header `b3: {traceId}-{spanId}-{sampled}-{parentSpanId}`, or multi-header `X-B3-TraceId`, `X-B3-SpanId`, `X-B3-ParentSpanId`, `X-B3-Sampled`. You pick the format with `management.tracing.propagation.type=W3C|B3`. The critical constraint is interoperability: a service must *consume* whatever its callers *produce*. If a legacy Sleuth/Zipkin fleet speaks B3, a new W3C service placed between them will drop the context and break the trace. Newer Boot versions let you decouple this via `management.tracing.propagation.produce` and `consume` lists so a service can accept multiple formats while emitting one.
code
properties · 6 lines# Accept both formats inbound (migrating off Sleuth/B3), emit W3C outbound
management.tracing.propagation.produce=W3C
management.tracing.propagation.consume=W3C,B3
# Simple single-format form (older / simpler setups):
# management.tracing.propagation.type=B3go deeper
Know both are header formats for trace/span IDs and W3C is the Boot 3 default.
Name the header shapes (traceparent vs b3 / X-B3-*) and the propagation.type property.
Handle migration seams with produce/consume lists and reason about interoperability breakage.
Set fleet-wide propagation policy including gateways/mesh, and stage a Sleuth-B3 to Boot-W3C migration without losing traces.
**Two formats, one concept.** Trace and span IDs must be serialized into HTTP headers to cross a process boundary. There are competing conventions: **W3C Trace Context** — a formal web standard and the **default in Spring Boot 3 / Micrometer Tracing**. Headers: - `traceparent`: `version-traceId-spanId-flags`, e.g. `00-4bf9...4736-00f067aa0ba902b7-01`. - `tracestate`: optional comma-separated vendor key/values (e.g. `rojo=00f067aa0ba902b7,congo=t61rcWkgMzE`), for backend-specific data that rides along. **B3** — the older convention from **Zipkin/Brave**, ubiquitous in **Spring Cloud Sleuth** systems. Two encodings: - *Multi-header*: `X-B3-TraceId`, `X-B3-SpanId`, `X-B3-ParentSpanId`, `X-B3-Sampled` (`0`/`1`), and rarely `X-B3-Flags` (`1` = debug). - *Single-header*: `b3: {traceId}-{spanId}-{samplingState}-{parentSpanId}`, e.g. `b3: 80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90`. A bare `b3: 0` means 'do not sample'. **Notable format differences.** B3 trace IDs may be 64-bit (16 hex) or 128-bit (32 hex); W3C is always 128-bit. Sampling is a separate `X-B3-Sampled` field in B3 vs a bit in W3C's flags. B3 has an explicit *debug* flag (force-sample); W3C expresses only sampled/not. **Configuration.** ```properties management.tracing.propagation.type=B3 # or W3C (default) ``` With the **Brave** bridge you get B3 naturally; with the **OpenTelemetry** bridge W3C is native, but Micrometer supplies propagators for both regardless of bridge. Newer Spring Boot (3.2+) added finer control so produce and consume formats can differ: ```properties management.tracing.propagation.produce=W3C management.tracing.propagation.consume=W3C,B3 ``` This lets a service **accept both** inbound formats during a migration while **emitting** a single canonical one. **Why it matters — interoperability.** Propagation only works if the receiver can parse what the sender wrote. Mixing formats silently **breaks traces**: the receiver finds no header it recognizes, treats the request as a new root, and you get two disconnected traces where you expected one. Classic failure: modernizing part of a Sleuth (B3) estate to Boot 3 (W3C) — the seam between old and new drops context unless you standardize or configure multi-format consume. **Gateways / proxies / meshes.** An API gateway or service mesh (Envoy/Istio) also participates in propagation and usually defaults to B3 or W3C; align it with your services, or it becomes the seam that breaks traces. **When to choose which.** Prefer **W3C** for new systems — it's the standard, tool-neutral, and the Boot default. Keep/consume **B3** where you interoperate with an existing Zipkin/Sleuth/Brave ecosystem. During migration, consume both and produce W3C.
- You migrate one service to W3C but its callers still send B3. What breaks and how do you fix it?The W3C service finds no traceparent, starts a fresh trace, and the caller's trace is severed — you see two traces. Fix by having the migrated service consume both formats (`management.tracing.propagation.consume=W3C,B3`) or by keeping it on B3 until the whole path is upgraded.
- What's the difference between single-header and multi-header B3?Multi-header spreads the values across X-B3-TraceId / X-B3-SpanId / X-B3-ParentSpanId / X-B3-Sampled. Single-header packs them into one `b3: traceId-spanId-sampled-parentSpanId` header, which is more compact and preferred for header-size-sensitive transports like messaging.
saying these in an interview costs you the question
- Claiming W3C and B3 carry different information (they carry the same IDs, just different encodings)
- Thinking you can freely mix formats between services without breaking traces
- Believing B3 is Spring Boot 3's default (it's W3C)
- Forgetting the gateway/mesh must use a compatible format