RestTemplate
HTTP 403
error handling
Spring Boot
HTTP requests

Why do I always get 403 when fetching data with RestTemplate?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A 403 Forbidden response means the server understood your request but refused to authorize it. When this happens with Spring's RestTemplate, the root cause is usually not the client library itself. It is almost always missing credentials, missing headers, CSRF protection, cookies, IP policy, or a server rule that treats your programmatic request differently from a browser request.

Start by understanding what 403 means

403 is different from 401. A 401 usually means authentication is missing or invalid. A 403 means the server has enough information to identify the request but is still refusing it.

That distinction helps narrow the search:

  • a token may be valid but lack required scope
  • the endpoint may require a role your caller does not have
  • the request may be missing a required header even though the URL is correct
  • the server may block non-browser clients or certain origins

Add the headers the server actually expects

Many RestTemplate calls fail because they send only the URL and assume the server will accept defaults. Real APIs often require Authorization, Accept, Content-Type, User-Agent, or custom headers.

java
1import org.springframework.http.HttpEntity;
2import org.springframework.http.HttpHeaders;
3import org.springframework.http.HttpMethod;
4import org.springframework.http.MediaType;
5import org.springframework.http.ResponseEntity;
6import org.springframework.web.client.RestTemplate;
7
8public class Demo {
9    public static void main(String[] args) {
10        RestTemplate restTemplate = new RestTemplate();
11
12        HttpHeaders headers = new HttpHeaders();
13        headers.setBearerAuth("YOUR_TOKEN");
14        headers.setAccept(java.util.List.of(MediaType.APPLICATION_JSON));
15        headers.set("User-Agent", "MyApp/1.0");
16
17        HttpEntity<Void> request = new HttpEntity<>(headers);
18
19        ResponseEntity<String> response = restTemplate.exchange(
20            "https://api.example.com/data",
21            HttpMethod.GET,
22            request,
23            String.class
24        );
25
26        System.out.println(response.getStatusCode());
27    }
28}

If the same endpoint works in Postman or a browser, compare the exact headers and cookies being sent there.

Watch for CSRF and session-based security

If you are calling a Spring Security application or another server that uses session cookies and CSRF tokens, a POST, PUT, or DELETE request may get 403 even though login succeeded earlier.

That happens because the server expects both the authenticated session and a valid CSRF token. A browser handles that flow naturally. A simple RestTemplate call does not unless you implement it.

In that situation, either supply the token the server expects or use a token-based API design that does not rely on browser-style CSRF semantics for machine clients.

Some servers block non-browser clients

Not every 403 is about your credentials. Some endpoints reject requests that do not look like browser traffic, lack a Referer, come from a blocked IP range, or violate a gateway policy such as Cloudflare, API management rules, or allowlists.

That is why copying just the URL from a working browser session is often not enough. The browser may also be sending cookies, origin metadata, or anti-bot signals you are not reproducing.

Inspect the response body, not just the status code

Many APIs return a useful error body explaining the rejection reason. If you catch only the exception and print the status code, you lose the most useful clue.

java
1import org.springframework.web.client.HttpStatusCodeException;
2
3try {
4    restTemplate.getForObject("https://api.example.com/data", String.class);
5} catch (HttpStatusCodeException ex) {
6    System.out.println(ex.getStatusCode());
7    System.out.println(ex.getResponseBodyAsString());
8}

That response body often tells you whether the failure is missing scope, missing CSRF token, invalid API key, or an access policy issue.

RestTemplate is old, but the diagnosis is the same

Modern Spring code often uses WebClient, but the meaning of 403 does not change. Switching libraries rarely fixes the problem by itself. First prove which part of the request the server is rejecting.

A reliable debugging sequence is:

  1. confirm the endpoint works with a known-good tool
  2. capture the exact request headers and cookies
  3. reproduce those in RestTemplate
  4. inspect the server response body and logs

Common Pitfalls

  • Assuming 403 means the URL is wrong when the real issue is authorization or policy.
  • Sending no Authorization or required custom headers.
  • Ignoring CSRF requirements on state-changing requests.
  • Comparing a browser request to RestTemplate without accounting for cookies and session state.
  • Printing only the exception message and not the response body.

Summary

  • A 403 from RestTemplate usually means the server rejected an otherwise understood request.
  • Missing auth headers, scope, CSRF tokens, or required cookies are common causes.
  • Some servers also block non-browser clients or unexpected origins.
  • Compare the exact working request against the failing Java request.
  • Inspect the response body and server logs before blaming the client library.

Course illustration
Course illustration

All Rights Reserved.