June 25, 2026
Spring Boot 4.1’s New SSRF Filter Never Touched My Feign Calls — Here’s the Trap
I upgraded a service to Spring Boot 4.1, switched on its new SSRF filter, and braced for the usual fallout — a global block rule shredding…
By HobbiesPark
5 min read
I upgraded a service to Spring Boot 4.1, switched on its new SSRF filter, and braced for the usual fallout — a global block rule shredding my internal service-to-service calls. The fallout never came. My entire Feign mesh kept humming like nothing had changed. That was the moment I realized the filter wasn't guarding what I thought it was guarding.
- Spring Boot 4.1's
InetAddressFilterhardensRestClient,WebClient,RestTemplate, and@HttpExchangeinterface clients against SSRF by resolved IP. - It runs on Boot's auto-configured
ClientHttpRequestFactoryline — which Feign never uses. - So a global
InetAddressFilterbean can't break your internal Feign calls. Good news if you feared the east-west footgun. - The flip side: the filter is a no-op for anything Feign fetches. If your "fetch this user URL" feature rides Feign, you've posted a guard at an empty room.
- The fix isn't to bolt a filter onto Feign — it's to route untrusted-URL fetches through a
RestClient/WebClientthe filter can actually see, and keep Feign for your known internal services.
💰 High-Paying Remote Tech Jobs 🌎 Work From Anywhere 🚀 Multiple Openings Available 👉 Apply Now
What 4.1's SSRF filter actually is
InetAddressFilter is a functional interface that decides, per resolved address, whether an outgoing call is allowed. It applies to both the blocking RestClient/RestTemplate and the reactive WebClient, plus @HttpExchange interface clients. The factory methods matter, because the obvious-sounding deny(...) isn't one of them:
InetAddressFilter.externalAddresses()— allow only public addresses; private and link-local (including cloud metadata at169.254.0.0/16) are rejected. This is exactly the posture you want for an untrusted-URL fetcher.InetAddressFilter.of("192.168.1.0/24").andNot("192.168.1.1")— allow a CIDR, minus specific addresses, for finer control.
Register it as a bean and Boot broadcasts it to every auto-configured HTTP client builder:
@Configuration(proxyBeanMethods = false)
public class SsrfConfig {
// As a bean, this applies to every auto-configured RestClient/WebClient/RestTemplate.
@Bean
InetAddressFilter httpClientInetAddressFilter() {
return InetAddressFilter.externalAddresses();
}
}@Configuration(proxyBeanMethods = false)
public class SsrfConfig {
// As a bean, this applies to every auto-configured RestClient/WebClient/RestTemplate.
@Bean
InetAddressFilter httpClientInetAddressFilter() {
return InetAddressFilter.externalAddresses();
}
}If your internal calls went through those clients, this is the line that would turn a green deploy red. Mine didn't — and here's why.
Why it never touched my Feign calls
Feign and Boot's HTTP clients sit on two separate rails that never cross. Boot's HttpClientAutoConfiguration builds a ClientHttpRequestFactory (or reactive connector), and that's what RestClient, RestTemplate, and WebClient consume — and what InetAddressFilter plugs into via HttpClientSettings. Feign doesn't use any of it. Spring Cloud's FeignAutoConfiguration wires a completely separate feign.Client, chosen like this:
- default →
Client.Default(java.net.HttpURLConnection) feign-hc5on the classpath →ApacheHttp5Clientfeign-okhttpon the classpath →OkHttpClient- with Spring Cloud LoadBalancer → any of the above wrapped in
FeignBlockingLoadBalancerClient
None of those route through Boot's request factory, so the filter has no hook into them. The SSRF filter and your Feign mesh are simply on different wires. That's not a bug — it's the architecture.
The good news and the quiet gap
The good news: you can drop a global InetAddressFilter bean and your internal Feign traffic won't notice. The east-west footgun everyone worries about — "my discovery-based service calls all broke" — can't happen through Feign, because the filter never reaches it.
The quiet gap is the other side of the same coin. The filter only protects the RestClient/WebClient/RestTemplate family. That has two consequences worth checking before you feel safe:
- A Feign client built with a dynamic
urlattribute that fetches an attacker-influenced target stays completely unprotected by the filter. - If all your outbound traffic is Feign — including whatever fetches user-supplied URLs — then the
InetAddressFilteryou just added guards nothing at all. You've hardened a door no request walks through.
SSRF lives wherever you fetch a URL you don't fully control. The filter only helps if that fetch happens on a client it can see.
The fix: put untrusted fetches on the client the filter can see
Route user-supplied URL fetches through a dedicated RestClient with externalAddresses(), and leave Feign for your known internal services. This is the inversion of the usual advice: instead of trying to exempt internal clients from a global filter, you deliberately move the one dangerous fetch onto a filtered client.
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.HttpClientSettings;
import org.springframework.boot.http.client.InetAddressFilter;
import org.springframework.http.client.ClientHttpRequestFactory;
@Configuration(proxyBeanMethods = false)
class HttpClientConfig {
// ONLY for fetching user-supplied URLs (webhooks, "import from link", link previews).
@Bean
RestClient untrustedFetchClient(RestClient.Builder builder) {
InetAddressFilter externalOnly = InetAddressFilter.externalAddresses();
HttpClientSettings settings = HttpClientSettings.defaults()
.withInetAddressFilter(externalOnly);
ClientHttpRequestFactory rf = ClientHttpRequestFactoryBuilder.detect().build(settings);
return builder.requestFactory(rf).build();
}
}import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.HttpClientSettings;
import org.springframework.boot.http.client.InetAddressFilter;
import org.springframework.http.client.ClientHttpRequestFactory;
@Configuration(proxyBeanMethods = false)
class HttpClientConfig {
// ONLY for fetching user-supplied URLs (webhooks, "import from link", link previews).
@Bean
RestClient untrustedFetchClient(RestClient.Builder builder) {
InetAddressFilter externalOnly = InetAddressFilter.externalAddresses();
HttpClientSettings settings = HttpClientSettings.defaults()
.withInetAddressFilter(externalOnly);
ClientHttpRequestFactory rf = ClientHttpRequestFactoryBuilder.detect().build(settings);
return builder.requestFactory(rf).build();
}
}Your Feign interfaces stay exactly as they are — no filter, no change, calling known internal services over private IPs. The dangerous fetch is the only thing that moves, and it moves onto a client where the filter actually fires.
One subtlety worth keeping: even though a global bean wouldn't break Feign today, scope the filter to this dedicated client anyway. The day someone adds an internal call through a plain RestTemplate or RestClient, a global bean would catch it — and you'd be back to debugging the footgun you skipped this time.
A decision table you can paste into a design doc
ClientGoes through Boot's request factory?Covered by InetAddressFilter?What to doRestClient / RestTemplate / WebClientYesYesApply externalAddresses() to the untrusted-URL one@HttpExchange interface clientYes (RestClient/WebClient-backed)YesScope via group configurerFeign (Client.Default / ApacheHttp5Client / OkHttpClient)NoNoFilter is a no-op — don't rely on it hereRaw java.net.URL / HttpURLConnectionNoNoValidate manually; the filter won't help
The bottom two rows are the blind spots. Inventory which of your outbound calls live there before you trust the filter.
Two things to verify before you ship
DNS rebinding still needs thought. A filter that checks the resolved IP at request time is the right shape, but an attacker controlling DNS can resolve to a public IP for your check and a private IP for the actual connection. Confirm the filter evaluates the address the client actually connects to, and treat it as one layer — keep your network egress controls.
Inventory your client families. The entire lesson here is that "I added the SSRF filter" means nothing until you know which clients it covers. List your outbound calls by type — Feign, RestClient/WebClient/RestTemplate, raw java.net — and confirm each user-facing fetch sits on a client the filter can see.
Action steps
- Upgrade to Spring Boot 4.1 and list every outbound HTTP call by client type.
- Mark which ones fetch user-supplied URLs — those are your SSRF surface.
- If any of those rides Feign or raw
java.net, move it onto a dedicatedRestClient/WebClient. - Apply
InetAddressFilter.externalAddresses()to that one client viaHttpClientSettings(not a global bean). - Leave your internal Feign mesh untouched.
- Add a DNS-rebinding check and confirm the filter sees the connect-time address.
FAQ
Does Spring Boot 4.1's InetAddressFilter protect Feign clients? No. Feign uses its own feign.Client (via FeignAutoConfiguration), separate from the ClientHttpRequestFactory line the filter plugs into. The filter never sees Feign traffic.
Will a global InetAddressFilter bean break my internal Feign calls? No — for the same reason. That east-west footgun only hits RestClient/WebClient/RestTemplate and @HttpExchange clients.
So how do I actually stop SSRF if everything is Feign? Move the user-supplied-URL fetch onto a dedicated RestClient/WebClient with InetAddressFilter.externalAddresses(). Keep Feign for known internal services.
Is the filter a replacement for a network firewall / egress policy? No — it's application-layer defense-in-depth on a specific set of clients. Keep your egress controls.
8. Sources / References
- Spring Boot Reference — Calling REST Services (
InetAddressFilter,externalAddresses(),of().andNot(),HttpClientSettings): https://docs.spring.io/spring-boot/reference/io/rest-client.html - Spring Boot 4.1 Release Notes (SSRF mitigation): https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.1-Release-Notes
- Spring Cloud OpenFeign Reference (
feign.Clientselection,FeignBlockingLoadBalancerClient): https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/ - InfoQ — Spring Boot 4.1 Adds gRPC Auto-Configuration, SSRF Mitigation, and Kotlin 2.3: https://www.infoq.com/news/2026/06/spring-boot-4-1/
- OWASP — Server-Side Request Forgery: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
- OWASP — SSRF Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
Thank you for being a part of the community
Before you go:
👉 Be sure to clap and follow the writer ️👏️️
👉 CodeToDeploy Tech Community is live on Discord — Join now!
Disclosure: This post includes affiliate and partnership links.