skip to content

Why does /actuator/httpexchanges return nothing (or 404) even after you expose it, and how do you make it work?

level: middleimportance: must knowfreq 50%

answer

  1. @ConditionalOnBean(HttpExchangeRepository)
  2. no repository auto-configured since 3.0/2.2
  3. declare InMemoryHttpExchangeRepository bean
  4. ring buffer default 100, volatile
  5. was httptrace in Boot 2.x

basics

~10 s

Spring Boot doesn't auto-create the storage for it. You must define an HttpExchangeRepository bean — usually InMemoryHttpExchangeRepository — so exchanges are recorded. Without that bean the endpoint isn't registered even if you exposed it.

solid answer

~40 s

The httpexchanges endpoint only records and shows data if an HttpExchangeRepository bean exists in the context. Since Spring Boot 3.0 (and effectively 2.2) no such bean is auto-configured, so the HttpExchangesEndpoint is @ConditionalOnBean and simply isn't created — you get a 404 or empty results despite adding it to management.endpoints.web.exposure.include. The fix is to declare a bean, typically new InMemoryHttpExchangeRepository(), which keeps the last 100 exchanges in a ring buffer (configurable via setCapacity). This replaced the Boot 2.x httptrace endpoint and its HttpTraceRepository. Because the in-memory store is bounded and lost on restart, it's a live-debugging aid, not an audit log. You control captured fields with management.httpexchanges.recording.include (headers, principal, remote address) and can pause capture with recording.enabled=false.

code

java · 15 lines
java
import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository;
import org.springframework.boot.actuate.web.exchanges.InMemoryHttpExchangeRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ActuatorConfig {

    @Bean
    HttpExchangeRepository httpExchangeRepository() {
        InMemoryHttpExchangeRepository repo = new InMemoryHttpExchangeRepository();
        repo.setCapacity(200); // default is 100
        return repo;
    }
}

go deeper

for a junior

Know the endpoint needs a repository bean to work.

for a middle

Name InMemoryHttpExchangeRepository, its default capacity, and the @ConditionalOnBean wiring.

for a senior

Explain the httptrace→httpexchanges rename and recording.include tuning, plus privacy tradeoffs.

for a principal

Argue for observability/tracing over this endpoint in production and reason about a custom durable repository's cost/PII risk.

**The gotcha.** Developers expose `httpexchanges`, hit `/actuator/httpexchanges`, and see either a 404 or an empty `{"exchanges":[]}` that never fills. The reason: the endpoint needs somewhere to store exchanges, and Spring Boot **does not auto-configure that storage**. **Mechanism.** The endpoint bean `HttpExchangesEndpoint` is registered by `HttpExchangesEndpointAutoConfiguration` under `@ConditionalOnBean(HttpExchangeRepository.class)`. The repository is the `HttpExchangeRepository` interface. Spring Boot ships an implementation — `InMemoryHttpExchangeRepository` — but **does not create it for you** (this was deliberately removed to avoid an unbounded/unexpected in-memory buffer and privacy surprises). So if no `HttpExchangeRepository` bean exists, the endpoint is never created, and exposure config has nothing to expose → 404. If you define the bean but the endpoint isn't exposed, you get 404 for a different reason. You need **both**: the bean *and* web exposure. **The fix.** Declare a bean: ```java @Bean HttpExchangeRepository httpExchangeRepository() { return new InMemoryHttpExchangeRepository(); } ``` `InMemoryHttpExchangeRepository` is a fixed-capacity ring buffer (default **100**), newest-first; call `setCapacity(int)` to change it. It only records HTTP **server** exchanges, and the data is **volatile** — gone on restart and evicted once capacity is exceeded. **Recording it captures.** Spring wires an `HttpExchangesFilter` (a servlet filter) that writes each completed exchange into the repository. Configure the captured detail with: - `management.httpexchanges.recording.enabled` (default true) — master on/off. - `management.httpexchanges.recording.include` — a set from `Include`: `REQUEST_HEADERS`, `RESPONSE_HEADERS`, `COOKIE_HEADERS`, `AUTHORIZATION_HEADER`, `PRINCIPAL`, `REMOTE_ADDRESS`, `SESSION_ID`, `TIME_TAKEN`. Sensitive headers like Cookie/Authorization are excluded by default. **Naming history.** In Boot 2.x this was the **`httptrace`** endpoint with `HttpTraceRepository` / `InMemoryHttpTraceRepository`. Boot 3.0 renamed everything to `httpexchanges` / `HttpExchangeRepository` / `InMemoryHttpExchangeRepository`. Old configuration property `management.trace.http.*` became `management.httpexchanges.recording.*`. **When to use.** Great for quick local/dev inspection of recent traffic. In production prefer proper observability — Micrometer `Observation`/tracing, access logs, or an APM — because the in-memory buffer is tiny, non-persistent, and holds request data (potential PII) in memory. If you need durable history, implement `HttpExchangeRepository` over a datastore, but weigh privacy and volume first.

  • What was this endpoint called in Spring Boot 2.x, and what changed in 3.0?
    It was 'httptrace' backed by HttpTraceRepository/InMemoryHttpTraceRepository. Boot 3.0 renamed it to 'httpexchanges' with HttpExchangeRepository/InMemoryHttpExchangeRepository, and properties moved from management.trace.http.* to management.httpexchanges.recording.*.
  • Why is the in-memory repository not a good production audit trail?
    It's a small bounded ring buffer (default 100 exchanges), stored only in heap, so it's lost on restart, evicts old entries quickly, doesn't span multiple instances, and can hold PII in memory. Use durable logging/APM/tracing instead.
  • Are cookie and authorization headers recorded by default?
    No. Sensitive headers are excluded by default; you'd have to opt in via management.httpexchanges.recording.include (COOKIE_HEADERS, AUTHORIZATION_HEADER), which is usually a bad idea for security.

saying these in an interview costs you the question

  • Claiming Spring Boot auto-creates an in-memory repository for httpexchanges
  • Thinking exposure alone (exposure.include) makes the endpoint work
  • Treating httpexchanges as a durable/audit log
  • Confusing it with distributed tracing / Micrometer tracing

context