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

Actuator in Spring Boot is basically the dashboard of a car: oil level, engine temperature, speed, odometer. Information the mechanic needs — not the passenger in the back seat. If you install that dashboard in the rear seat with public access, you didn't break the car, but you created a problem you didn't have before.

That's exactly what happens when someone enables management.endpoints.web.exposure.include=* in a production application.properties without reading what they just turned on. It's not that Actuator is inherently dangerous — the mistake is exposing it without a clear policy.

My take: Actuator is a legitimate and powerful operational tool. The risk isn't in using it — it's in dropping it in like just another dependency without deciding which endpoints make sense to expose, for whom, and behind what access control.


What the Official Docs Say (and What They Don't)

The official Spring Boot Actuator documentation is more honest than it looks on first read. It defines two distinct things that a lot of people collapse into one:

  • Enabling an endpoint: whether it exists and can execute.
  • Exposing an endpoint: whether it's accessible via HTTP or JMX.

By default, Spring Boot enables most endpoints but only exposes health and info over HTTP. The rest exist, but they don't respond on the web unless you explicitly include them.

This is an intentional security contract. The docs say it plainly:

"For security purposes, all actuators other than /health are not exposed over HTTP by default."

What the docs don't do is tell you what to expose based on your application's context. That's an architecture decision, not a configuration one.

# application.yml — sensible base configuration
management:
  endpoints:
    web:
      exposure:
        # Only expose what operations actually consumes
        include: health, info, metrics
        # Never use '*' in production without authentication and internal network
  endpoint:
    health:
      # Show details only to authenticated users, not everyone
      show-details: when-authorized
Enter fullscreen mode Exit fullscreen mode

Where People Go Wrong: The Copypaste Recipe and Its Cost

The most common mistake I see in Stack Overflow answers, old tutorials, and internal projects without audits is this block:

# What NOT to do in production without authentication
management:
  endpoints:
    web:
      exposure:
        include: "*"  # Exposes EVERYTHING: env, beans, heapdump, shutdown...
Enter fullscreen mode Exit fullscreen mode

What did you just expose with that?

  • /actuator/env: environment variables, system properties, credentials if they're in application.properties.
  • /actuator/beans: the complete Spring bean graph — internal architecture, fully visible.
  • /actuator/heapdump: an on-demand heap dump. Yes, everything that's in memory.
  • /actuator/shutdown: if enabled, shuts down the application via HTTP POST. Enabled by default: no. But if someone added it "for testing" and never removed it before production...
  • /actuator/loggers: live log level changes. Useful in staging. Dangerous exposed without auth.

The official documentation lists every endpoint with its capabilities and default enablement state. No guesswork needed — it's all right there.

The hidden cost isn't just security: it's attack surface, log noise, endpoints that respond even when they serve no purpose in that context. Every exposed endpoint is a path a scanner will probe.


Decision Matrix: What to Expose, for Whom, and with What Control

This is the question worth asking before adding any Actuator endpoint:

Endpoint Enable Expose via HTTP With What Control
health ✅ Always ✅ Yes, with show-details: when-authorized Public for liveness/readiness, details only with auth
info ✅ Always ✅ Yes Public, no sensitive data
metrics ✅ In staging/prod Internal network only or with auth Spring Security or private network
env ⚠️ Only if needed ❌ Never without auth + internal network Spring Security mandatory
loggers ⚠️ Staging/debug Internal network only Spring Security mandatory
heapdump ❌ Not in standard prod ❌ Never Only on-demand in controlled debug
shutdown ❌ Disabled ❌ Never
threaddump ⚠️ Only if needed Internal network only Spring Security mandatory
# Sensible configuration for a production backend
management:
  endpoints:
    web:
      exposure:
        include: health, info
      base-path: /internal/actuator  # Move away from the default path
  endpoint:
    health:
      show-details: when-authorized
    shutdown:
      enabled: false  # Explicit: never in production

# If you need metrics, Spring Security covers them
# management.endpoints.web.exposure.include: health, info, metrics
Enter fullscreen mode Exit fullscreen mode

One detail the documentation mentions that usually gets ignored: you can change Actuator's base-path. Moving it from /actuator to something like /internal/actuator is not security through obscurity if you also apply network control — it's an additional layer that cuts down the noise from automated scanners.


Checklist Before Adding an Actuator Endpoint

Before adding any endpoint to include, three questions:

1. Who actually consumes it?
If the answer is "Prometheus scrape," metrics with basic authentication or a private network is enough. If it's "the ops team for debugging," loggers behind Spring Security makes sense. If it's "not sure, I'll add it just in case" — don't add it.

2. Is it behind access control?
Actuator integrates with Spring Security directly. If you already have Security configured, you can restrict Actuator paths like any other:

// SecurityConfig.java — example Actuator restriction
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth
            // Only ACTUATOR_ADMIN role can access sensitive endpoints
            .requestMatchers("/internal/actuator/env",
                             "/internal/actuator/heapdump",
                             "/internal/actuator/loggers")
                .hasRole("ACTUATOR_ADMIN")
            // Health and info are public for Kubernetes probes
            .requestMatchers("/internal/actuator/health",
                             "/internal/actuator/info")
                .permitAll()
            .anyRequest().authenticated()
        );
    return http.build();
}
Enter fullscreen mode Exit fullscreen mode

3. Is it on the right network?
In a containerized environment (Docker, Kubernetes), the usual approach is to use a different port for management than for the app. Spring Boot lets you configure management.server.port to separate the traffic:

# Separate port for management — not exposed externally
management:
  server:
    port: 8081  # Only accessible from the cluster's internal network
Enter fullscreen mode Exit fullscreen mode

This is an infrastructure decision that complements Spring Security — it doesn't replace it.


What You Can't Conclude from This Evidence

There are clear limits to what this guide can claim:

  • There are no reproducible impact metrics here. I'm not going to tell you "40% of Spring CVEs come from misconfigured Actuator" because I don't have a verifiable source for that. What does exist is official documentation explaining why the default is conservative.
  • The real cost of exposed surface depends on context. An internal API inside a private VPC has a completely different risk profile than a service exposed on the public internet. This guide gives you principles; you apply judgment based on your network.
  • There's no public evidence that heapdump or env caused a production incident in any specific project I can cite. The recommendation not to expose them comes from reasoning about what they contain, not from a specific post-mortem.

What is verifiable: the official documentation describes exactly what each endpoint exposes. Before enabling any of them, read that table. It takes five minutes and it's the only source you need to make an informed decision.


Frequently Asked Questions About Spring Boot Actuator and Endpoint Security

Is it safe to have /actuator/health public in production?
Depends on what it shows. With show-details: always, health can expose information about databases, caches, and external services. With show-details: when-authorized, it returns only UP/DOWN status to unauthenticated users — which is all Kubernetes health checks or a load balancer need. Spring Boot's default is show-details: never, which is the most conservative starting point.

What's the difference between enabling and exposing an endpoint?
Enabling means the endpoint exists and can execute internally. Exposing means it's accessible via HTTP or JMX. An endpoint can be enabled but not exposed: it exists in the Spring context but doesn't respond to web requests. The official documentation separates these two dimensions with distinct properties: management.endpoint.<id>.enabled and management.endpoints.web.exposure.include.

Does management.endpoints.web.exposure.include=* make sense in any context?
In local development, it can be useful for exploring what information is available. In staging with Spring Security and an internal network, it's reasonable if the team is actively consuming that information. In production exposed without authentication: no. The * in production without access control is the pattern the documentation implicitly discourages by establishing conservative defaults.

How do I integrate Actuator with Prometheus without exposing metrics publicly?
Use management.server.port to separate the management port from the application port, and configure the Prometheus scrape to point to the internal port. In Kubernetes, this means the Prometheus Service points to the management port that isn't exposed by the app's Ingress. Spring Boot Actuator includes Prometheus format support via Micrometer if you add spring-boot-starter-actuator along with micrometer-registry-prometheus.

Does /actuator/env show passwords or secrets?
Spring Boot masks properties containing words like password, secret, key, or token in the /env output, replacing them with ******. But sanitization depends on the configured patterns and whether the property name matches those patterns. Variables with unconventional names might not get masked. The conservative recommendation is to not expose /env over HTTP in production, regardless of sanitization.

Is it worth changing Actuator's base-path?
Moving it from /actuator to another path reduces noise from automated scanners looking for that default path. It's not a security measure on its own, but combined with Spring Security and port separation it reduces visible attack surface. The configuration is management.endpoints.web.base-path.


Actuator as an Operational Contract, Not a Toggle

My position is this: Actuator is one of the best-designed parts of the Spring ecosystem. The enable vs. expose model is deliberate and sensible. The problem appears when someone treats it as a binary toggle — "I turn it on or off" — instead of as a contract between the application, the operations team, and the network it lives in.

What I don't buy is the generic recommendation of "don't use Actuator in production." That's throwing out the dashboard because you're afraid someone might look at it. The right answer is to decide what operational information you need, expose it only to whoever consumes it, and with the appropriate access control.

The concrete next step: open the application.properties or application.yml of a Spring Boot backend that's already running and look for what's configured under management.endpoints.web.exposure.include. If it's empty, the default is conservative and that's fine. If it has *, there's a conversation pending about who consumes those endpoints and from where.

If you're interested in applying this decision-making framework to other contexts — like the authentication token decision tree or authorization patterns in middleware — there are related posts on this blog that tackle the same question from a different angle: Next.js 16 Middleware: authorization patterns that scale, Rate limiting in web applications: what to protect first, and Authentication tokens: JWT, Paseto, and session tokens.


Primary source:


This article was originally published on juanchi.dev

Top comments (0)