Actuator ships with over a dozen endpoints, and the default answer to "which should we expose" is not "all of them" and not "none of them" — it's a short, deliberate list, each one behind its own access decision. Getting this wrong in either direction is common: teams either expose /actuator/env to the internet and leak configuration secrets, or lock everything down so hard that on-call has no visibility during an incident.
Start From an Empty Allowlist
The safest default is to expose nothing by web and opt in explicitly, rather than starting from * and trying to remember what to remove.
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: when-authorized
probes:
enabled: truehealth and info are close to mandatory — load balancers and orchestrators need health for liveness and readiness checks, and info is a cheap way to surface build metadata (git commit, version) without shipping a separate endpoint for it. metrics and prometheus (from micrometer-registry-prometheus) are what your monitoring stack scrapes and are safe to expose on an internal network, though still worth authenticating.
The Ones That Need a Second Look
A few endpoints are genuinely useful but leak information that shouldn't reach unauthenticated callers:
envdumps every property source, including anything that made it intoEnvironmentwithout being masked — database URLs, and occasionally credentials if someone didn't use a secrets manager correctly. Spring Boot sanitizes well-known sensitive key names by default, but that sanitization is a denylist, not a guarantee.heapdumphands out a full heap dump on request, which can contain session tokens, decrypted request bodies sitting in memory, or anything else your application happened to be holding. Treat it like a credential.threaddumpandloggersare safe from an information-leak standpoint but let a caller change log levels at runtime (loggers) — genuinely useful for debugging a live incident, genuinely dangerous if anyone can flip your production logging toTRACEand watch request bodies scroll past.shutdownis disabled by default for good reason and should stay that way outside of tightly controlled environments like a container orchestrator's own lifecycle hooks.
Securing What You Do Expose
Actuator endpoints should sit behind their own authorization rule, separate from your application's API security, ideally on a different port so network-level controls can do part of the job.
management:
server:
port: 9001@Bean
SecurityFilterChain actuatorSecurityChain(HttpSecurity http) throws Exception {
return http
.securityMatcher(EndpointRequest.toAnyEndpoint())
.authorizeHttpRequests(auth -> auth
.requestMatchers(EndpointRequest.to("health", "info")).permitAll()
.anyRequest().hasRole("OPS"))
.httpBasic(Customizer.withDefaults())
.build();
}Running actuator on a separate port means it never needs to be reachable through your public load balancer at all — a security group or ingress rule can restrict it to your VPC, which is a stronger guarantee than any application-level check.
A Reasonable Default Posture
For most services: expose health (with liveness/readiness probes for Kubernetes) and info publicly or near-publicly, put metrics and prometheus behind network-level restriction to your monitoring infrastructure, and require authenticated operator access for everything else — env, loggers, threaddump, mappings, beans. Never expose heapdump or shutdown outside a locked-down operator boundary. The goal isn't zero exposure; it's making sure every exposed endpoint was a decision, not a default nobody revisited.