Spring RestTemplate Basic Auth: Interceptors & Headers Guide
Master Basic Authentication with Spring RestTemplate using interceptors and headers.
20+ years shipping production Java in banking & fintech. Lessons pulled from things that broke in production.
- ✓Java 17 or later
- ✓Spring Boot 3.x (or 2.7+ with minor adjustments)
- ✓Basic understanding of REST APIs and HTTP headers
- ✓Familiarity with dependency injection in Spring
- Use
HttpHeaderswithBasicAuthenticationInterceptorfor clean separation of concerns. - Never hardcode credentials; use environment variables or secrets manager.
- Prefer
RestTemplateBuilderfor configuration over manual bean setup. - For reactive stacks, migrate to
WebClient; RestTemplate is in maintenance mode.
Think of RestTemplate as a courier who delivers your requests. Basic Auth is like a keycard – you need to present it with every delivery. Interceptors are like an automatic keycard dispenser that attaches the keycard to every package before it leaves your office. Headers are the specific envelope where the keycard goes. This article shows you how to set up that dispenser correctly so you don't forget the keycard and get locked out.
If you've ever written a Spring Boot app that calls an external REST API secured with Basic Authentication, you know the drill: create a RestTemplate, set the Authorization header, and hope it works. But in production, things get messy. I once spent a weekend debugging a fintech integration where the auth header was being stripped by a misconfigured interceptor. The result? Intermittent 401 errors that only happened under load. The root cause? Two interceptors fighting over the same request.
Basic Authentication itself is simple – base64-encode username:password and slap it into the Authorization header. But doing it right in Spring's RestTemplate requires understanding interceptors, ClientHttpRequestInterceptor, and the nuances of RestTemplateBuilder. Get it wrong, and you'll leak credentials in logs, double-encode headers, or miss auth on redirects.
In this guide, I'll show you the patterns I've used across dozens of production services. We'll cover the raw header approach (quick and dirty), the interceptor approach (clean and testable), and how to handle edge cases like preemptive auth and credential rotation. By the end, you'll know exactly which approach to use and why the others are dangerous.
The Two Approaches: Headers vs Interceptors
When you need to add Basic Auth to RestTemplate, you have two fundamental choices: manually set the Authorization header on every request, or use an interceptor to do it automatically. The manual approach is tempting for its simplicity – just create a HttpHeaders object, set the header, and pass it to the exchange method. But here's the problem: you have to remember to do it everywhere. Miss one call, and you get a 401. In a codebase with dozens of API calls, that's a ticking time bomb.
The interceptor approach is superior because it guarantees every request gets the header. Spring provides BasicAuthenticationInterceptor since Spring 5.3, which is a ClientHttpRequestInterceptor that adds the Authorization: Basic ... header. You register it once on the RestTemplate bean, and you're done. No more forgetting headers.
But wait – there's a subtlety. The interceptor runs before the request is sent, but after the RestTemplate has built the HttpRequest. This means you can't use it to set other request-specific headers like Content-Type easily. For those, you still need to pass headers in the method call. So the pattern is: use interceptors for cross-cutting concerns (auth, logging, tracing) and method-level headers for request-specific needs.
Here's the code for both approaches so you can see the difference:
RestTemplateBuilder.basicAuthentication() for consistent auth across all requests.Deep Dive into BasicAuthenticationInterceptor
The BasicAuthenticationInterceptor is a simple but powerful class. It implements ClientHttpRequestInterceptor and overrides intercept(HttpRequest, byte[], ClientHttpRequestExecution). In that method, it adds the Authorization header with the Base64-encoded credentials. But there's more to it than meets the eye.
First, it handles the charset correctly. The spec says the credentials should be encoded in ISO-8859-1, but the interceptor uses UTF-8 by default. This is fine for most cases, but if you're dealing with legacy systems that expect ISO-8859-1, you might need to create a custom interceptor.
Second, it's thread-safe. The interceptor holds a reference to the encoded credentials string, which is immutable. No shared mutable state means no race conditions. This is why I always recommend it over custom implementations that might accidentally use a mutable StringBuilder or a shared HttpHeaders instance.
Third, it does NOT strip the auth header on redirects. This is a common pitfall – by default, RestTemplate uses SimpleClientHttpRequestFactory which, on a 302 redirect, will create a new request without the original headers. The BasicAuthenticationInterceptor re-applies the header on the new request because it runs again for the redirected request. But if you're using a custom interceptor that modifies headers in place, the redirect request won't have the auth header unless you explicitly handle it.
Let me show you what happens under the hood:
Configuring RestTemplate with Basic Auth in Spring Boot
Spring Boot 3.x makes it trivial to create a RestTemplate bean with Basic Auth. The RestTemplateBuilder is auto-configured and provides a fluent API. Here's the recommended way:
``java @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder .basicAuthentication("${api.username}", "${api.password}") .build(); } ``
Notice I'm using placeholders from application.properties. Never hardcode credentials! Use environment variables or a secrets manager like AWS Secrets Manager or HashiCorp Vault. In production, I've seen credentials committed to Git more times than I care to count.
But wait – there's a catch. The basicAuthentication() method creates a BasicAuthenticationInterceptor and adds it to the builder's list of interceptors. If you also have custom interceptors, the order matters. By default, the builder adds the basic auth interceptor first? Actually, it adds it last. Let me verify: in Spring Boot 3.2, RestTemplateBuilder.basicAuthentication() adds the interceptor to the end of the list. So if you have a custom interceptor that modifies headers, it will run before the auth interceptor. That's usually fine, but if your custom interceptor clears headers, you'll lose auth.
To control ordering, use the additionalInterceptors() method or build the RestTemplate manually:
``java @Bean public RestTemplate restTemplate() { RestTemplate template = new ``RestTemplate(); template.getInterceptors().add(new BasicAuthenticationInterceptor("user", "pass")); template.getInterceptors().add(new CustomLoggingInterceptor()); return template; }
But this bypasses Spring Boot's auto-configuration. I prefer to use RestTemplateBuilder and then reorder interceptors if needed.
Another important configuration is the request factory. The default SimpleClientHttpRequestFactory does NOT support preemptive auth. If you're calling an API that returns 401 before allowing access, you'll get an extra round-trip. To avoid this, switch to HttpComponentsClientHttpRequestFactory from Apache HttpClient:
``java @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder .requestFactory(() -> new ``HttpComponentsClientHttpRequestFactory()) .basicAuthentication("user", "pass") .build(); }
This factory sends the auth header on the first request, avoiding the 401 challenge.
RestTemplateBuilder with HttpComponentsClientHttpRequestFactory for preemptive Basic Auth.What the Official Docs Won't Tell You
The Spring documentation covers the basics, but there are several gotchas that will bite you in production. Here's what they don't tell you:
1. Interceptor Ordering Matters More Than You Think
The docs say interceptors are applied in order, but they don't warn that some interceptors (like those from Spring Cloud or custom logging) might clear or modify headers. I've seen a logging interceptor that serialized the request headers to JSON and then forgot to re-add them. The auth interceptor ran before it, so the auth header was present in the log but missing in the actual request. The fix: ensure the auth interceptor runs last.
2. BasicAuthenticationInterceptor Uses UTF-8, Not ISO-8859-1
The HTTP Basic Auth spec (RFC 7617) says the charset should be ISO-8859-1. Spring's interceptor uses UTF-8. For most APIs, this doesn't matter because the credentials are ASCII. But if you have non-ASCII characters in your password, you might get a mismatch. The workaround is to create your own interceptor that uses ISO-8859-1.
3. Credential Rotation Is a Nightmare with Hardcoded Interceptors
If you use @Value to inject credentials into the interceptor, you can't rotate them without restarting the application. In production, you might need to rotate credentials without downtime. The solution: use a secrets manager that provides a dynamic credential provider. You can implement a custom interceptor that fetches credentials on each request (with caching).
4. The basicAuthentication() Method on RestTemplateBuilder Is Not Idempotent
Calling basicAuthentication() multiple times adds multiple interceptors. This can cause duplicate headers. I've seen a configuration where someone called it twice by accident, and the API received two Authorization headers (both identical), which some servers reject.
5. Debugging Auth Issues Requires Wire-Level Logging
Spring's debug logging shows request headers, but if you're using Apache HttpClient, you need to enable its logging separately. The property logging.level.org.apache.http=DEBUG will show the actual bytes sent over the wire, including the auth header. This is invaluable when troubleshooting.
Testing RestTemplate with Basic Auth
Testing a RestTemplate that uses Basic Auth can be tricky because you don't want to hit the real API. You have two options: mock the RestTemplate or use a mock server like WireMock. I strongly prefer WireMock because it tests the actual HTTP communication, including header handling.
Here's how to test with WireMock in Spring Boot:
- Add the WireMock dependency.
- Use
@WireMockTestor start a WireMock server programmatically. - Configure your
RestTemplateto point to the WireMock server. - Set up stubs that expect the
Authorizationheader.
WireMock can verify that the request contains the correct header. This catches issues where the interceptor isn't adding the header or is adding it incorrectly.
If you must mock the RestTemplate itself, use Mockito. But be careful: mocking RestTemplate often leads to brittle tests that don't verify the actual HTTP behavior. I've seen teams spend hours debugging a test that passed but failed in production because the mock didn't simulate redirects or header stripping.
Here's a WireMock example:
Handling Credential Rotation and Secrets Management
In production, credentials change. Maybe you rotate them quarterly, or maybe an incident forces an immediate rotation. If you've hardcoded credentials in your interceptor, you'll need to restart the application. That's downtime you don't want.
The solution is to use a dynamic credential provider. Instead of injecting a static string, inject a bean that can refresh credentials at runtime. For example, you can use Spring Cloud Vault or AWS Secrets Manager to fetch secrets on startup and cache them. But if you need to rotate without restart, you'll need a custom interceptor that checks the cache on each request (with a short TTL).
Here's a pattern I've used: a CredentialsProvider interface that returns a UsernamePassword pair. The interceptor calls it on each request. The implementation can cache the credentials for a configurable duration, and when they expire, it fetches new ones from the secrets manager.
This adds a tiny overhead per request (a cache lookup), but it's negligible compared to the HTTP call itself. And it gives you the ability to rotate credentials without restarting.
One caveat: if the secrets manager is down, your interceptor will fail. You need to handle that gracefully, perhaps by falling back to the last known good credentials or throwing a specific exception that triggers an alert.
Let me show you the code:
The Case of the Vanishing Auth Header
401 Unauthorized for all outgoing requests to a third-party payment gateway, but only after peak traffic hours. The service worked fine during low load.ClientHttpRequestInterceptor that modified the request headers after the BasicAuthenticationInterceptor had already set them. Under high concurrency, a race condition caused the custom interceptor to overwrite the Authorization header with a stale value from a shared mutable map.ThreadLocal for per-request state, and moved the auth interceptor to be the last in the chain to avoid overwrites.- Always ensure interceptors are thread-safe – never share mutable state without synchronization.
- Order interceptors carefully; auth headers should be set last to avoid being overwritten.
- Use
RestTemplateBuilder.interceptors()to control ordering explicitly. - Log the final request headers in debug mode to verify auth is present.
- Prefer
BasicAuthenticationInterceptorover manual header manipulation – it's battle-tested.
Authorization header is being set – enable logging.level.org.springframework.web.client=DEBUG to see request headers.HttpComponentsClientHttpRequestFactory with setPreemptiveAuth(true).toString() in any custom interceptor to mask sensitive headers. Use BasicAuthenticationInterceptor which already does this.logging.level.org.springframework.web.client=DEBUGlogging.level.org.apache.http=DEBUGBasicAuthenticationInterceptor to RestTemplate builder.| File | Command / Code | Purpose |
|---|---|---|
| RestTemplateBasicAuthExample.java | public String callApiManually(String url) { | The Two Approaches |
| BasicAuthInterceptorDebug.java | public class DebugInterceptor implements ClientHttpRequestInterceptor { | Deep Dive into BasicAuthenticationInterceptor |
| RestTemplateConfig.java | @Configuration | Configuring RestTemplate with Basic Auth in Spring Boot |
| CustomIsoInterceptor.java | public class CustomIsoInterceptor implements ClientHttpRequestInterceptor { | What the Official Docs Won't Tell You |
| RestTemplateBasicAuthTest.java | @SpringBootTest | Testing RestTemplate with Basic Auth |
| DynamicAuthInterceptor.java | public class DynamicAuthInterceptor implements ClientHttpRequestInterceptor { | Handling Credential Rotation and Secrets Management |
Key takeaways
RestTemplateBuilder.basicAuthentication() to add Basic Auth via an interceptorHttpComponentsClientHttpRequestFactory from Apache HttpClient.Interview Questions on This Topic
How would you configure a RestTemplate to use Basic Authentication in a Spring Boot application?
RestTemplateBuilder.basicAuthentication(username, password).build(). This creates a BasicAuthenticationInterceptor that adds the Authorization header to every request. For preemptive auth, also set the request factory to HttpComponentsClientHttpRequestFactory.Frequently Asked Questions
20+ years shipping production Java in banking & fintech. Lessons pulled from things that broke in production.
That's Spring Boot. Mark it forged?
6 min read · try the examples if you haven't