DEV Community

Cover image for Spring Boot Actuator: What to Expose, What to Hide, and What to Check Before Adding Endpoints
Juan Torchia
Juan Torchia Subscriber

Posted on • Originally published at juanchi.dev

Spring Boot Actuator: What to Expose, What to Hide, and What to Check Before Adding Endpoints

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 exposed
Enter fullscreen mode Exit fullscreen mode

That 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: 8081
Enter fullscreen mode Exit fullscreen mode

That 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();
}
Enter fullscreen mode Exit fullscreen mode

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 health expose 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/env returns in the environment where it's going to run?
  • [ ] Are there environment variables with credentials showing up in that endpoint?
  • [ ] Does /actuator/info expose 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: false
Enter fullscreen mode Exit fullscreen mode

This 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:


This article was originally published on juanchi.dev

Top comments (0)