Spring Boot Actuator: qué exponer, qué ocultar y qué mirar antes de agregar endpoints
Actuator en Spring Boot es básicamente como el tablero de instrumentos de un auto: ahí está el nivel de aceite, la temperatura del motor, la velocidad, el odómetro. Es información que el mecánico necesita, no el pasajero de atrás. Si instalás ese tablero en el asiento trasero con acceso público, no rompiste el auto, pero creaste un problema que no tenías antes.
Eso es exactamente lo que pasa cuando alguien habilita management.endpoints.web.exposure.include=* en un application.properties de producción sin leer qué acaba de encender. No es que Actuator sea peligroso por naturaleza: es que el error es exponerlo sin una política clara.
Mi tesis: Actuator es una herramienta operativa legítima y poderosa. El riesgo no está en usarla, sino en agregarla como si fuera una dependencia más sin decidir qué endpoints tienen sentido exponer, para quién y detrás de qué control de acceso.
Qué dice la documentación oficial (y qué no dice)
La documentación oficial de Spring Boot Actuator es más honesta de lo que parece a primera lectura. Define dos cosas distintas que mucha gente colapsa en una sola:
- Habilitar un endpoint: si existe y puede ejecutarse.
- Exponer un endpoint: si es accesible por HTTP o JMX.
Por defecto, Spring Boot habilita la mayoría de los endpoints pero solo expone health e info por HTTP. El resto existe, pero no responde por la web a menos que explícitamente lo incluyas.
Esto es un contrato de seguridad intencional. La documentación lo dice sin rodeos:
"For security purposes, all actuators other than
/healthare not exposed over HTTP by default."
Lo que la documentación no hace es decirte qué exponer según el contexto de tu aplicación. Eso es una decisión de arquitectura, no de configuración.
# application.yml — configuracion base prudente
management:
endpoints:
web:
exposure:
# Solo exponer lo que operaciones realmente consume
include: health, info, metrics
# Nunca usar '*' en produccion sin autenticacion y red interna
endpoint:
health:
# Mostrar detalles solo a usuarios autenticados, no a todos
show-details: when-authorizedDónde se equivoca la gente: la receta copypaste y su costo
El error más común que aparece en Stack Overflow, tutoriales viejos y proyectos internos sin auditoría es este bloque:
# Lo que NO hacer en produccion sin autenticacion
management:
endpoints:
web:
exposure:
include: "*" # Expone TODO: env, beans, heapdump, shutdown...¿Qué acabás de exponer con eso?
/actuator/env: variables de entorno, propiedades del sistema, credenciales si están enapplication.properties./actuator/beans: el grafo completo de beans de Spring — arquitectura interna visible./actuator/heapdump: un dump de heap bajo demanda. Sí, todo lo que está en memoria./actuator/shutdown: si está habilitado, apaga la aplicación vía HTTP POST. Habilitado por defecto: no. Pero si alguien lo agregó "para testing" y no lo sacó antes de producción.../actuator/loggers: cambio de nivel de log en caliente. Útil en staging. Peligroso expuesto sin auth.
La documentación oficial lista todos los endpoints con sus capacidades y el estado de habilitación por defecto. No hay que adivinar: está todo ahí.
El costo oculto no es solo de seguridad: es de superficie de ataque, de ruido en logs, de endpoints que responden aunque no sirvan para nada en ese contexto. Cada endpoint expuesto es una ruta que un scanner va a probar.
Matriz de decisión: qué exponer, para quién y con qué control
Esta es la pregunta que vale hacer antes de agregar cualquier endpoint de Actuator:
| Endpoint | Habilitar | Exponer por HTTP | Con qué control |
|---|---|---|---|
health | ✅ Siempre | ✅ Sí, con show-details: when-authorized | Público para liveness/readiness, detalles solo con auth |
info | ✅ Siempre | ✅ Sí | Público, sin datos sensibles |
metrics | ✅ En staging/prod | Solo red interna o con auth | Spring Security o red privada |
env | ⚠️ Solo si necesario | ❌ Nunca sin auth + red interna | Spring Security obligatorio |
loggers | ⚠️ Staging/debug | Solo red interna | Spring Security obligatorio |
heapdump | ❌ No en prod estándar | ❌ Nunca | Solo bajo demanda en debug controlado |
shutdown | ❌ Deshabilitado | ❌ Nunca | — |
threaddump | ⚠️ Solo si necesario | Solo red interna | Spring Security obligatorio |
# Configuracion prudente para un backend en produccion
management:
endpoints:
web:
exposure:
include: health, info
base-path: /internal/actuator # Mover del path por defecto
endpoint:
health:
show-details: when-authorized
shutdown:
enabled: false # Explicito: nunca en produccion
# Si necesitas metricas, Spring Security las cubre
# management.endpoints.web.exposure.include: health, info, metricsUn detalle que la documentación menciona pero que suele ignorarse: podés cambiar el base-path de Actuator. Moverlo de /actuator a algo como /internal/actuator no es seguridad por oscuridad si además aplicás control de red — es una capa adicional que reduce el ruido de scanners automáticos.
Checklist antes de agregar un endpoint de Actuator
Antes de agregar cualquier endpoint a include, tres preguntas:
1. ¿Quién lo consume realmente?
Si la respuesta es "Prometheus scrape", metrics con autenticación básica o red privada alcanza. Si es "el equipo de ops para debug", loggers detrás de Spring Security tiene sentido. Si es "no sé, lo agrego por las dudas", no lo agregues.
2. ¿Está detrás de un control de acceso? Actuator se integra con Spring Security de forma directa. Si ya tenés Security configurado, podés restringir rutas de Actuator como cualquier otra:
// SecurityConfig.java — ejemplo de restriccion de Actuator
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// Solo rol ACTUATOR_ADMIN puede acceder a endpoints sensibles
.requestMatchers("/internal/actuator/env",
"/internal/actuator/heapdump",
"/internal/actuator/loggers")
.hasRole("ACTUATOR_ADMIN")
// Health e info son publicos para probes de Kubernetes
.requestMatchers("/internal/actuator/health",
"/internal/actuator/info")
.permitAll()
.anyRequest().authenticated()
);
return http.build();
}3. ¿Está en la red correcta?
En un ambiente containerizado (Docker, Kubernetes), lo habitual es que el puerto de management sea diferente al de la app. Spring Boot permite configurar management.server.port para separar el tráfico:
# Puerto separado para management — no expuesto al exterior
management:
server:
port: 8081 # Solo accesible desde la red interna del clusterEsto es una decisión de infraestructura que complementa Spring Security, no lo reemplaza.
Qué NO se puede concluir con esta evidencia
Hay límites claros en lo que esta guía puede afirmar:
- No hay métricas de impacto reproducibles aquí. No voy a decirte "el 40% de los CVEs en Spring vienen de Actuator mal configurado" porque no tengo esa fuente verificable. Lo que sí existe es documentación oficial que establece por qué el default es conservador.
- El costo real de una superficie expuesta depende del contexto. Una API interna en una VPC privada tiene un perfil de riesgo distinto a un servicio expuesto en internet. Esta guía da principios; vos aplicás criterio según tu red.
- No hay evidencia pública de que
heapdumpoenvhayan causado un incidente en producción en ningún proyecto concreto que yo pueda citar. La recomendación de no exponerlos viene de razonamiento sobre qué contienen, no de un post-mortem específico.
Lo que sí es verificable: la documentación oficial describe exactamente qué expone cada endpoint. Antes de habilitar cualquiera, leé esa tabla. Cuesta cinco minutos y es la única fuente que necesitás para tomar una decisión informada.
Preguntas frecuentes sobre Spring Boot Actuator y seguridad de endpoints
¿Es seguro tener /actuator/health público en producción?
Depende de qué muestre. Con show-details: always, health puede exponer información sobre bases de datos, caches y servicios externos. Con show-details: when-authorized, devuelve solo el estado UP/DOWN a usuarios no autenticados — que es lo que necesitan los health checks de Kubernetes o un load balancer. La configuración por defecto de Spring Boot es show-details: never, que es el punto de partida más conservador.
¿Cuál es la diferencia entre habilitar y exponer un endpoint?
Habilitar significa que el endpoint existe y puede ejecutarse internamente. Exponer significa que es accesible por HTTP o JMX. Un endpoint puede estar habilitado pero no expuesto: existe en el contexto de Spring pero no responde a peticiones web. La documentación oficial separa estas dos dimensiones con propiedades distintas: management.endpoint.<id>.enabled y management.endpoints.web.exposure.include.
¿Tiene sentido usar management.endpoints.web.exposure.include=* en algún contexto?
En desarrollo local, puede ser útil para explorar qué información está disponible. En staging con Spring Security y red interna, es razonable si el equipo consume activamente esa información. En producción expuesto sin autenticación: no. El * en producción sin control de acceso es el patrón que la documentación implícitamente desaconseja al establecer defaults conservadores.
¿Cómo integro Actuator con Prometheus sin exponer métricas al público?
Usando management.server.port para separar el puerto de management del puerto de la aplicación, y configurando el scrape de Prometheus para que apunte al puerto interno. En Kubernetes, esto significa que el Service de Prometheus apunta al puerto de management que no está expuesto por el Ingress de la app. Spring Boot Actuator incluye soporte para el formato Prometheus via Micrometer si agregás spring-boot-starter-actuator junto a micrometer-registry-prometheus.
¿El endpoint /actuator/env muestra contraseñas o secrets?
Spring Boot enmascara propiedades que contienen palabras como password, secret, key o token en el output de /env, reemplazándolas por ******. Pero la sanitización depende de los patrones configurados y de si el nombre de la propiedad coincide con esos patrones. Variables con nombres no convencionales pueden no ser enmascaradas. La recomendación conservadora es no exponer /env por HTTP en producción, independientemente de la sanitización.
¿Vale la pena cambiar el base-path de Actuator?
Moverlo de /actuator a otro path reduce el ruido de scanners automáticos que buscan ese path por defecto. No es una medida de seguridad por sí sola, pero combinada con Spring Security y separación de puertos reduce la superficie de ataque visible. La configuración es management.endpoints.web.base-path.
Conclusión: Actuator como contrato operativo, no como toggle
Mi postura es esta: Actuator es una de las partes mejor diseñadas del ecosistema Spring. El modelo de habilitar vs. exponer es deliberado y sensato. El problema aparece cuando alguien lo trata como un toggle binario — "lo prendo o lo apago" — en lugar de como un contrato entre la aplicación, el equipo de operaciones y la red donde vive.
Lo que no compro es la recomendación genérica de "no uses Actuator en producción". Eso es tirar el tablero de instrumentos por miedo a que alguien lo mire. La respuesta correcta es decidir qué información operativa necesitás, exponerla solo para quien la consume y con el control de acceso que corresponde.
El próximo paso concreto: abrí el application.properties o application.yml de un backend Spring Boot que ya esté corriendo y buscá qué tiene configurado bajo management.endpoints.web.exposure.include. Si está vacío, el default es conservador y está bien. Si tiene *, hay una conversación pendiente sobre quién consume esos endpoints y desde dónde.
Si te interesa el criterio de decisión aplicado a otros contextos — como el árbol de tokens de autenticación o los patrones de autorización en middleware — hay posts relacionados en el blog que tocan la misma pregunta desde otro ángulo: Next.js 16 Middleware: patrones de autorización que escalan, Rate limiting en aplicaciones web: qué proteger primero, y Tokens de autenticación: JWT, Paseto y session tokens.
Fuente original:
- Spring Boot Actuator — Endpoints: https://docs.spring.io/spring-boot/reference/actuator/endpoints.html
Artículos Relacionados
OpenTelemetry en Next.js: traces que sobreviven el edge y el servidor sin perder el contexto
OpenTelemetry en Next.js funciona, pero el propagador por defecto rompe silenciosamente el trace en la frontera edge/node. Esto es lo que tenés que configurar explícitamente para que el contexto no desaparezca entre Middleware, Server Components y Server Actions.
Cómo difieren los CVEs de memory safety entre Rust y C/C++
Rust tiene menos CVEs de memoria que C/C++, pero eso no es toda la historia. Mi análisis de qué dice ese dato, qué no dice, y cómo convertirlo en una decisión técnica real.
Lo que las entrevistas de trabajo me enseñaron sobre Kubernetes
Las entrevistas técnicas de Kubernetes tienen un problema que nadie nombra: te preguntan por objetos que jamás vas a tocar en producción, pero ignoran los errores que sí rompen sistemas reales. Acá está el mapa que me faltaba.
Comentarios (0)
¿Qué pensás de esto?
Dejá tu comentario en 10 segundos.
Usamos tu login solo para mostrar tu nombre y avatar. Nada de spam.