Spring Boot Actuator: What to Expose, What to Hide, and What to Check Before Adding Endpoints
I was reviewing a Spring Boot backend config in a test environment when I noticed /actuator/env was responding without authentication. Nothing production, nothing critical — but the setup was the classic default of someone who added the dependency, saw /actuator/health working, and moved on. The env endpoint exposes active environment variables, including everything in the Spring context. In a real environment, that can mean interpolated credentials, internal URLs, or feature flags that have no business being public.
My thesis is this: Actuator isn't bad. It's a serious, well-documented, genuinely useful operational tool. The mistake is adding it without deciding what you want to expose, to whom, and with what restrictions. Most tutorials tell you how to enable it. Almost none explain the cost of enabling it wrong.
Spring Boot Actuator endpoint security: what the official docs actually say
The official Spring Boot Actuator documentation is explicit about something a lot of people ignore: endpoints have two independent dimensions — being enabled and being exposed.
An endpoint can be enabled (active in the application context) but not exposed over HTTP. By default, only health is exposed via HTTP. Everything else requires explicit configuration.
That matters because the default behavior is conservative — but trivially easy to override with a single line:
# application.yml
management:
endpoints:
web:
exposure:
include: "*" # Exposes EVERYTHING — including what you don't want exposedThat line shows up in hundreds of tutorials as "to see all available endpoints." Which is fine on localhost. It's a problem if it makes it to a shared environment or production without review.
The docs also distinguish between JMX and HTTP as separate exposure channels. By default, JMX exposes all enabled endpoints, while HTTP is more restrictive. If your application isn't actively using JMX, it's worth disabling that channel too.
The endpoints that deserve attention before you expose them
| Endpoint | What it exposes | Risk if public |
|---|---|---|
/actuator/env | Spring context environment variables | High — can include credentials |
/actuator/beans | All registered beans | Medium — reveals internal architecture |
/actuator/heapdump | JVM heap dump | High — can contain sensitive data in heap |
/actuator/mappings | All HTTP endpoints in the app | Medium — enables reconnaissance |
/actuator/loggers | Active logging configuration | Low-Medium — allows runtime log level changes |
/actuator/metrics | JVM and app metrics | Low — useful internally, little value exposed |
/actuator/health | App operational status | Low if properly configured |
This table isn't exhaustive — the full list of built-in endpoints is in the official docs. But it covers the most common ones and the ones that generate the most debate.
Where people get it wrong: the easy recipe and its hidden cost
The most common pattern I see in projects with misconfigured Actuator isn't carelessness — it's accumulation. Someone adds spring-boot-starter-actuator to have /health available for the load balancer. Then someone else adds include: "*" to debug something. Then nobody cleans it up.
The problem with include: "*" isn't just what it exposes today — it's that it automatically exposes any endpoint added in the future, including endpoints from third-party libraries that integrate with Actuator. Spring Security, Micrometer, Spring Cloud, and other frameworks can register their own endpoints under /actuator/. If you have include: "*", they expose themselves.
# What you should have instead of "*"
management:
endpoints:
web:
exposure:
# Explicit list: you know exactly what's exposed
include: "health,info,metrics"
# Everything else is blocked by default
endpoint:
health:
# Show details only to authenticated users
show-details: when_authorized
# Separate the management port from the app port
server:
port: 8081That last point — management.server.port — is the most practical change you can make. If Actuator runs on a separate port, you can protect it at the network level (firewall, security group, nginx) without touching application logic. Port 8081 never reaches the public load balancer. Port 8080 does.
Another common mistake: trusting that Spring Security protects Actuator automatically. By default, if you have Spring Security on the classpath, Actuator endpoints are protected by the same rules as the rest of the app — but that's not always enough. Being explicit is worth it:
// SecurityConfig.java
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// Only /health and /info without authentication
.requestMatchers("/actuator/health", "/actuator/info").permitAll()
// The rest of actuator requires ADMIN role
.requestMatchers("/actuator/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
return http.build();
}This is more robust than relying on implicit behavior, and it's also living documentation: whoever reads this config understands exactly what's protected and how.
Decision checklist before adding Actuator to a project
Before the first deploy with Actuator enabled, these are the questions worth answering:
On exposure:
- Did you explicitly list which endpoints you want to expose? Or did you use
"*"? - Is Actuator running on a separate port from the application port?
- Is that management port blocked for external traffic?
On authentication:
- Do sensitive endpoints (
env,beans,heapdump) require authentication? - Does
healthexpose details (show-details) without restriction? Do you want that? - Do you have Spring Security explicitly configured for
/actuator/**, or are you relying on default behavior?
On exposed information:
- Did you check what
/actuator/envreturns in the environment where it's going to run? - Are there environment variables with credentials showing up in that endpoint?
- Does
/actuator/infoexpose versions, git commits, or metadata you don't want public?
On JMX:
- If you're not using JMX, did you disable it?
# Disable JMX if you're not using it
spring:
jmx:
enabled: falseThis checklist doesn't guarantee total security — that depends on the full context of the application, the network, and the environment. But it covers the decisions that most frequently get left unmade.
What you can't conclude without your own data
There's something this guide can't do for you: tell you which specific endpoints you actually need in production.
That depends on how you monitor the application. If you're using Prometheus + Grafana, /actuator/prometheus is useful — but if you're using Datadog with the agent installed, you probably don't need to expose it at all. If you have a load balancer doing health checks, you need /actuator/health — but if you're on Kubernetes with liveness/readiness probes pointing to their own endpoint, you can decouple that from Actuator entirely.
Also: the behavior of show-details: when_authorized depends on how Spring Security is configured in that context. Without reviewing the app's actual security configuration, that setting can behave differently than expected.
What you can do today: start with minimal exposure and add endpoints with intention, not with "*". It's easier to add than to remove — especially once you have environments that depend on the current state.
A useful reference if you're evaluating observability integration: the post on Docker healthchecks has an analysis of what availability checks actually measure and what they shouldn't promise — directly applicable if you're deciding whether Actuator /health is enough for your probes or you need something more granular.
FAQ: Spring Boot Actuator and endpoint security
What endpoints does Actuator expose by default via HTTP?
Only /actuator/health is exposed over HTTP in the default configuration. Everything else requires explicit declaration in management.endpoints.web.exposure.include. You can verify this in the official documentation.
Is include: "*" always a problem?
On localhost for development, no. In a shared environment, staging with a semi-public network, or production, yes — because it exposes current and future endpoints without review. The risk isn't just what you're exposing today, it's what gets added automatically when you update dependencies.
Does Spring Security protect Actuator automatically if it's on the classpath?
Partially. Spring Security applies the application's security rules to Actuator, but the concrete behavior depends on how it's configured. The recommended practice is to be explicit with a requestMatchers("/actuator/**") in your security configuration.
Is it worth separating the Actuator port from the application port?
Yes, and it's probably the highest-impact change with the lowest complexity. With management.server.port: 8081, you can protect that port at the network level without touching application code. The public load balancer only sees 8080.
Can /actuator/health expose sensitive information?
Yes, if show-details is set to always. In that mode, the endpoint returns the status of each component — database, cache, external services — with details that can reveal internal URLs or dependency states. show-details: when_authorized is the most conservative setting for exposed environments.
How do I know which endpoints my third-party dependencies registered?
You can check at runtime by calling /actuator (the root endpoint, if it's exposed), which returns the map of all available endpoints. You can also enable it only in development using Spring profiles: @Profile("dev") on the configuration bean, or using spring.profiles.active to load an application-dev.yml with include: "*".
My final take: Actuator as an operational tool, not a default
Actuator is one of the best-designed parts of the Spring Boot ecosystem. The enabled/exposed separation, the Micrometer integration, the native support for custom health indicators — all of that has real operational value.
What has no value is adding it as "it comes in the starter, just in case." That logic works in development. In an environment with a real network, it's a surface area that grows on its own every time you update dependencies.
My practical recommendation: start with include: "health,info", separate the management port from the first deploy, and add endpoints with intention when you have a concrete consumer — a metrics dashboard, an APM tool, a diagnostic script. If you don't know who's consuming the endpoint, you probably don't need to expose it yet.
If you're building a backend with layered security decisions, it might be worth checking out the post on digital identity backend architecture — the "what to expose and with what restrictions" criterion applies in both contexts with similar logic.
Original source:
- Spring Boot Actuator — Endpoints: https://docs.spring.io/spring-boot/reference/actuator/endpoints.html
Related Articles
TypeScript strict mode: the 6 tsconfig options that actually matter in production and when to enable them
strict: true is not enough — and it's not the whole story. A flag-by-flag breakdown of what each strict mode option actually does, what bugs it prevents, and the order to enable them in an existing codebase.
Digital identity backend architecture: the decisions tutorials skip
Auth tutorials show you the happy path. The real problems in digital identity show up in revocation, state-change propagation, and the trust model. A decision guide from the inside.
Digital signatures: format, certificate, and validation policy — three layers people constantly mix up
When a digital signature fails, the instinct is to look at cryptography. Most of the time the problem is format or validation policy. Here I separate the three layers so the next error doesn't cost you hours.
Comments (0)
What do you think of this?
Drop your comment in 10 seconds.
We only use your login to show your name and avatar. No spam.