Introduction
Spring Boot Actuator gives you powerful insights into your application's runtime behavior—metrics, health checks, thread dumps, environment variables—but it also hands attackers a treasure map if you're not careful. We've seen production breaches where exposed actuator endpoints leaked database credentials, API keys, and internal architecture details. Securing Spring Boot Actuator endpoints, explained simply, comes down to one principle: expose only what you need, to whom you need it, and nowhere else.
This article walks you through a layered security approach that keeps your application observable without turning it into a security liability.
Why Actuator Security Matters
By default, Spring Boot Actuator exposes dozens of endpoints that reveal system internals. The /actuator/env endpoint lists all environment variables—including database passwords if you're using them for config. /actuator/heapdump lets anyone download your application's entire memory snapshot. /actuator/loggers can change log levels on the fly. None of these are safe on the public internet.
The risk isn't theoretical. We've responded to incidents where a misconfigured load balancer accidentally exposed an internal management port, and within hours automated scanners had downloaded heapdumps and extracted credentials from environment variables. The fix took weeks; prevention takes minutes.
Expose Only Health and Info Publicly
Start by deciding which endpoints actually need public visibility. Most applications only benefit from two:
- /actuator/health—tells load balancers and monitoring if the app is alive
- /actuator/info—exposes application version and metadata you've explicitly configured
Configure this in application.yml:
management:
endpoints:
web:
exposure:
include: health,info
endpoint:
health:
show-details: when-authorized
info:
enabled: trueThis whitelists only health and info endpoints. All others (metrics, env, heapdump, loggers) are disabled by default. The show-details: when-authorized setting returns basic health status to anonymous users ("UP" or "DOWN") but only shows components and details to authenticated users.
In practice, your health check might look like this:
@RestController
@RequestMapping("/actuator")
public class HealthController {
@GetMapping("/health")
public ResponseEntity> health() {
return ResponseEntity.ok(Map.of("status", "UP"));
}
}Load balancers love this. They get a simple answer without needing authentication, and you're not leaking internals.
Put Management Endpoints on a Separate Port
The next layer of defense is network isolation. Run actuator endpoints on a different port that's only accessible from within your VPC or data center, never from the public internet.
management:
server:
port: 9090
endpoints:
web:
base-path: /actuator
exposure:
include: health,info,metrics,prometheusNow your application listens on port 8080 for user-facing traffic, but management endpoints live on 9090. In Kubernetes, you'd expose port 8080 in the Service but keep 9090 internal. In AWS, your ALB targets port 8080; port 9090 isn't routed externally.
This solves a real problem: your load balancer health checks hit /health on port 8080 (public), while your ops team's monitoring scrapes detailed metrics from port 9090 (internal only). A misconfigured firewall rule that exposes 9090 is a violation you'll catch in security scanning. A port you don't advertise is harder to stumble upon.
In your deployment manifest, enforce this with a network policy:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: actuator-internal-only
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 9090Only pods in the monitoring namespace can reach port 9090. The database team gets no access. External users get no access. You've encoded trust in infrastructure.
Require Admin Role for the Rest
For sensitive endpoints that do need to be accessed remotely—like metrics, loggers, or threaddump—require authentication and authorization. Use Spring Security to lock them behind an admin role.
@Configuration
@EnableWebSecurity
public class ActuatorSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
// Public endpoints
.requestMatchers("/actuator/health", "/actuator/info").permitAll()
// Sensitive endpoints require ADMIN role
.requestMatchers("/actuator/**").hasRole("ADMIN")
// Everything else
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
}Now /actuator/metrics, /actuator/loggers, /actuator/threaddump all return 403 unless the request includes valid credentials for a user with the ADMIN role. This works for internal tools, CI/CD pipelines, and monitoring systems that can securely pass credentials.
In a microservices setup, your monitoring service might authenticate as a service account:
@Configuration
public class PrometheusMetricsConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public void scrapeMetrics() {
// Service account credentials passed securely via Vault or Secrets Manager
String credentials = Base64.getEncoder()
.encodeToString("prometheus:secrettoken".getBytes());
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Basic " + credentials);
restTemplate().exchange(
"http://myapp:9090/actuator/metrics",
HttpMethod.GET,
new HttpEntity<>(headers),
String.class
);
}
}The prometheus service account can scrape metrics because it has valid credentials and the ADMIN role. A curious engineer on the public network cannot, even if they find an exposed port.
Never Expose env or heapdump on the Internet
Some endpoints are so dangerous they should never be enabled unless you're actively debugging in development. /actuator/env and /actuator/heapdump are the prime offenders.
/actuator/env lists all environment variables. If you're using environment variables for database URIs, OAuth tokens, or encryption keys—which is common and even recommended by twelve-factor app principles—this endpoint is a credential dump.
/actuator/heapdump is worse. It's a binary snapshot of your application's memory at that moment. It contains decrypted passwords, active user sessions, cached API responses, private keys from loaded keystores. A heapdump is as sensitive as having access to the running process itself.
The safe approach: disable both entirely in production:
spring:
profiles:
active: prod
---
spring:
config:
activate:
on-profile: prod
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
env:
enabled: false
heapdump:
enabled: false
shutdown:
enabled: false
---
spring:
config:
activate:
on-profile: dev
management:
endpoints:
web:
exposure:
include: '*'In development, you get everything. In production, dangerous endpoints are explicitly disabled. This survives accidental misconfiguration because the default is safe.
If you absolutely must debug memory issues in production, do it with a short-lived SSH session to the pod, generate the heapdump locally, download it, then delete it—all within minutes and logged. Don't leave it exposed as an HTTP endpoint.
Common Mistakes
- Exposing everything by default. Some teams set
include: '*'and assume authentication will protect it. Authentication fails. Credentials are compromised. Disable endpoints you don't need instead. - Assuming the separate management port is "hidden." It's not. Network scanning finds it. Treat it as internal-only at the network level, not just in configuration.
- Forgetting to disable actuator in WAR deployments. If you're deploying a WAR to an external application server, actuator endpoints might be reachable at unexpected paths. Explicitly disable them unless needed.
- Logging environment variables for debugging. When you print
System.getenv()at startup or in error messages, you're writing credentials to logs. Logs are stored, archived, and sometimes exposed. Don't log env vars. - Enabling heap dumps without access controls. Heapdumps should require strong authentication and be immediately deleted after download. Leaving them live is a liability.
Key Takeaways
- Actuator endpoints leak secrets by default—restrict them aggressively with
include: health,infoor an explicit whitelist. - Move sensitive endpoints to a separate management port (e.g., 9090) that's only accessible from internal networks or specific monitoring systems.
- Require authentication and authorization (e.g., ADMIN role) for any actuator endpoint accessible over the network.
- Disable
envandheapdumpentirely in production; enable them only in development when needed for debugging. - Use profile-based configuration to enforce safety in production and development flexibility in your local environment.
- Treat actuator endpoints like database or secrets endpoints—they deserve the same security rigor.






Comments
Loading comments…