Spring Boot Actuator endpoints expose heap dumps, environment variables, and thread state. Without proper security configuration, they're a critical vulnerability. Learn how to lock them down.
JOptimize Team
Spring Boot Actuator is a powerful operational tool. It's also a security incident waiting to happen. The /actuator/env endpoint shows all environment variables — including your database passwords, API keys, and JWT secrets. The /actuator/heapdump endpoint lets anyone download a binary dump of your JVM heap, which may contain plaintext credentials from memory. And by default in older Spring Boot versions, all endpoints are open.
Spring Boot 2.x and 3.x expose only /actuator/health and /actuator/info over HTTP by default. But developers frequently add:
# THIS IS DANGEROUS — exposes everything management.endpoints.web.exposure.include=*
With this one line, you expose:
| Endpoint | Risk |
|---|---|
/actuator/env | All environment variables, including secrets |
/actuator/heapdump | Full JVM heap — may contain passwords |
/actuator/threaddump | Thread state — useful for attackers studying request patterns |
/actuator/beans | All Spring beans — exposes your architecture |
/actuator/mappings | All HTTP routes — attack surface map |
/actuator/loggers | Can be used to increase log verbosity for exfiltration |
/actuator/shutdown | Kills your application (if enabled) |
# application.properties # Only expose health and metrics for monitoring management.endpoints.web.exposure.include=health,info,metrics,prometheus management.endpoints.web.exposure.exclude=env,heapdump,threaddump,beans,mappings,shutdown
For monitoring stacks (Prometheus, Grafana):
management.endpoints.web.exposure.include=health,info,prometheus
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // Public health check — for load balancer probes .requestMatchers("/actuator/health").permitAll() .requestMatchers("/actuator/info").permitAll() // Prometheus scraping — restrict to monitoring subnet or require auth .requestMatchers("/actuator/prometheus").hasIpAddress("10.0.0.0/8") // All other actuator endpoints — admin only .requestMatchers("/actuator/**").hasRole("ADMIN") // API endpoints .requestMatchers("/api/**").authenticated() .anyRequest().denyAll() ); return http.build(); } }
Run actuator on a different port that is NOT exposed to the public internet:
# application.properties # Application serves traffic on 8080 (public) server.port=8080 # Actuator runs on 8081 (internal only — firewall blocks external access) management.server.port=8081 management.server.address=127.0.0.1 # Bind to localhost only
With this configuration, Kubernetes health probes hit 8081/actuator/health from within the cluster, while the public internet can only reach port 8080.
If you must expose /actuator/env, Spring Boot sanitizes sensitive keys automatically — but only if they match the default patterns:
# Default sanitization applies to keys containing: # password, secret, key, token, credentials, vcap_services, sun.java.command # Add custom patterns management.endpoint.env.keys-to-sanitize=.*secret.*,.*token.*,.*key.*,.*password.*,.*credentials.*,.*database.*
This replaces sensitive values with ***** in the /env response.
# Explicitly disable — never enable in production management.endpoint.shutdown.enabled=false
The shutdown endpoint (POST /actuator/shutdown) gracefully stops the Spring application context. Even with authentication, this should never be enabled in production — use your deployment platform to restart pods.
# kubernetes deployment.yaml livenessProbe: httpGet: path: /actuator/health/liveness port: 8081 initialDelaySeconds: 60 periodSeconds: 10 readinessProbe: httpGet: path: /actuator/health/readiness port: 8081 initialDelaySeconds: 30 periodSeconds: 5
# Enable liveness/readiness groups management.endpoint.health.probes.enabled=true management.health.livenessstate.enabled=true management.health.readinessstate.enabled=true
This separates liveness (is the JVM alive?) from readiness (is the app ready to serve traffic?) — allowing Kubernetes to restart crashed pods without marking them as unready during startup.
management.endpoints.web.exposure.include=* in production — this is the single most dangerous actuator configuration; it should never appear in production properties/heapdump in production — even with authentication, heap dumps are hundreds of MB and may contain credentials in memory; keep it disabled# Safe production actuator config management.server.port=8081 management.server.address=127.0.0.1 management.endpoints.web.exposure.include=health,info,prometheus management.endpoints.web.exposure.exclude=shutdown,heapdump,threaddump,env,beans management.endpoint.health.probes.enabled=true management.health.livenessstate.enabled=true management.health.readinessstate.enabled=true management.endpoint.shutdown.enabled=false
Actuator security requires four layers: expose only the endpoints you need (health, prometheus), require authentication for admin endpoints, bind the management port to an internal address only, and sanitize sensitive keys in /env. The most dangerous single mistake is exposure.include=* in production — it should be treated as a high-severity security finding.
JOptimize flags management.endpoints.web.exposure.include=* in properties files, missing management port configuration, and exposed sensitive endpoints in Spring Boot projects.
Critical: audit your actuator security before your next deployment.
Master Spring Boot, security, and Java performance with hands-on courses.
JOptimize finds N+1 queries, EAGER collections, and 70+ other issues in your Java codebase — in under 30 seconds.