spring-boot-admin-docs/src/site/docs/05-security/40-ssrf-protection.md
Spring Boot Admin Server includes opt-in protection against Server-Side Request Forgery (SSRF) attacks that can be triggered through the instance registration API.
When a Spring Boot application registers with the Admin Server, it sends a POST /instances request containing
healthUrl, managementUrl, and serviceUrl. The Admin Server immediately begins making outbound HTTP requests to
those URLs to poll health status and discover actuator endpoints. It also exposes a proxy at
/instances/{id}/actuator/** that forwards requests verbatim to the registered management URL.
Without authentication or URL validation on POST /instances, an attacker can:
http://169.254.169.254/latest/meta-data/ (AWS IMDSv1)This applies to loopback addresses, RFC 1918 private ranges, and any internal service reachable from the server's network, not just cloud metadata endpoints.
:::warning
SSRF protection is disabled by default to avoid breaking existing deployments where the Admin Server legitimately
communicates with services on private IP ranges. Enable it explicitly and provide a custom InetAddressFilter bean
for any intranet services that need to register.
:::
Add the following to your application.yml:
spring:
boot:
admin:
ssrf-protection:
enabled: true
When enabled, all URLs submitted during instance registration (healthUrl, managementUrl, serviceUrl) are
validated before the instance is stored. The proxy also re-validates the resolved target URL before each outbound
request.
When protection is enabled, address filtering is delegated to Spring Boot's
InetAddressFilter.
The default filter (InetAddressFilter.externalAddresses()) blocks all non-external addresses, including:
| Category | Examples |
|---|---|
| Loopback | localhost, 127.0.0.1, 127.x.x.x, ::1 |
| Link-local (cloud metadata) | 169.254.0.0/16 — includes AWS IMDSv1 (169.254.169.254), GCP, Azure |
| RFC 1918 Class A | 10.0.0.0/8 |
| RFC 1918 Class B | 172.16.0.0/12 (172.16.x – 172.31.x) |
| RFC 1918 Class C | 192.168.0.0/16 |
| IPv6 link-local | fe80::/10 |
| IPv6 unique-local | fc00::/7 (fc and fd prefixes) |
| Unspecified address | 0.0.0.0 |
| Disallowed schemes | Anything other than http and https (e.g. file://, ftp://) |
Registration attempts targeting any of these return 400 Bad Request. Proxy requests that resolve to a blocked address
return 403 Forbidden.
:::note Hostnames are resolved via DNS during validation. This means a public hostname that resolves to a private IP (DNS rebinding) is also blocked — not just hostnames that literally look like private addresses. :::
If your Admin Server legitimately needs to reach services on private addresses — for example in an on-premises or Kubernetes cluster deployment — you have two options:
Add entries to allowed-cidrs. Both individual IP addresses and CIDR subnet masks are supported for IPv4 and IPv6.
These ranges are ORed with the default externalAddresses() filter, so public addresses always remain reachable.
Single host (exact IP address):
spring:
boot:
admin:
ssrf-protection:
enabled: true
allowed-cidrs:
- "192.168.1.100" # single host, equivalent to 192.168.1.100/32
Subnet (CIDR mask):
spring:
boot:
admin:
ssrf-protection:
enabled: true
allowed-cidrs:
- "192.168.1.0/24" # all hosts in 192.168.1.x
Multiple ranges (subnets, whole classes, IPv6):
spring:
boot:
admin:
ssrf-protection:
enabled: true
allowed-cidrs:
- "192.168.1.0/24" # single subnet
- "10.0.0.0/8" # entire RFC 1918 Class A
- "172.16.0.0/12" # entire RFC 1918 Class B
- "fd00::/8" # IPv6 unique-local range
InetAddressFilter beanFor more control, expose an InetAddressFilter bean. It replaces the auto-configured filter entirely.
import org.springframework.boot.http.client.InetAddressFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SsrfConfig {
@Bean
public InetAddressFilter ssrfInetAddressFilter() {
return InetAddressFilter.externalAddresses()
.or("10.0.0.0/8") // pod CIDR
.or("172.16.0.0/12"); // service CIDR
}
}
InetAddressFilter supports CIDR notation for both IPv4 and IPv6, and can be composed with .or(), .and(), and
.andNot(). See the
Spring Boot reference documentation
for the full API.
By default only http and https are permitted. To add a custom scheme:
spring:
boot:
admin:
ssrf-protection:
enabled: true
allowed-schemes:
- http
- https
- grpc
| Property | Type | Default | Description |
|---|---|---|---|
spring.boot.admin.ssrf-protection.enabled | boolean | false | Enable SSRF URL validation |
spring.boot.admin.ssrf-protection.allowed-schemes | Set<String> | http, https | URL schemes that are permitted |
spring.boot.admin.ssrf-protection.allowed-cidrs | List<String> | (empty) | IP addresses or CIDR ranges (IPv4 and IPv6) that are permitted in addition to public addresses. Examples: 192.168.1.0/24, 10.0.0.0/8, fd00::/8 |
For finer-grained control over address filtering, expose a custom InetAddressFilter bean (see Allowing Internal Services).
Override the entire SsrfUrlValidator bean to implement fully custom logic:
import de.codecentric.boot.admin.server.utils.SsrfUrlValidator;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.boot.http.client.InetAddressFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomSsrfConfig {
@Bean
public SsrfUrlValidator ssrfUrlValidator(AdminServerProperties properties,
InetAddressFilter ssrfInetAddressFilter) {
return new SsrfUrlValidator(properties.getSsrfProtection(), ssrfInetAddressFilter);
}
}
SSRF protection runs at two points:
Registration (POST /instances) — healthUrl, managementUrl, and serviceUrl are validated before the
instance is stored. Invalid registrations are rejected with 400 Bad Request.
Proxy (/instances/{id}/actuator/**) — The resolved target URL is re-validated before each outbound request.
This provides defence-in-depth against scenarios where an endpoint URL is assembled dynamically after registration.
Blocked proxy requests return 403 Forbidden.
SSRF protection alone is not sufficient for a publicly accessible Admin Server. Combine it with:
POST /instances — The most effective mitigation. See Server Authentication.PUT request with a TTL header to obtain a metadata token, which the Admin Server cannot
provide without explicit support.