For years, versioning a Spring REST API meant choosing between duplicated controllers, custom request conditions, or a URL convention everyone interpreted slightly differently. Spring Framework 7 — and therefore Spring Boot 4 — makes it a first-class feature: a version attribute on your mappings, a configurable way to read the version from requests, and support for deprecating old versions.

I care about this more than most developers because of where I sit. As a Technical Support Engineer at Salesforce, I see what happens when an API changes underneath an integration: every breaking change lands on someone's desk as an escalation. Good versioning is how you evolve an API without doing that to people.

Step 1: tell Spring where the version comes from

Pick one strategy and configure it with properties. A request header is the most common choice for internal and partner APIs:

application.properties
# Read the version from a request header spring.mvc.apiversion.use.header=X-API-Version # Used when a client sends no version at all spring.mvc.apiversion.default=1.0 # Also accept any version that appears in a controller mapping spring.mvc.apiversion.detect-supported=true

The other built-in options are a query parameter (spring.mvc.apiversion.use.query-parameter), a path segment (spring.mvc.apiversion.use.path-segment) and a media type parameter (spring.mvc.apiversion.use.media-type-parameter[application/json]). WebFlux applications use the same properties under spring.webflux.apiversion.

Step 2: version your endpoints

Add the version attribute to any request mapping. A trailing + declares a baseline: "2.0+" matches 2.0 and every later version until another mapping takes over. That keeps you from copying unchanged endpoints every time you release a new version.

LoanController.java
@RestController @RequestMapping("/api/loans") public class LoanController { @GetMapping(path = "/{id}", version = "1.0") public LoanV1 getLoanV1(@PathVariable String id) { return loanService.findV1(id); } @GetMapping(path = "/{id}", version = "2.0+") // 2.0 and later public LoanV2 getLoan(@PathVariable String id) { return loanService.find(id); } }

A request with X-API-Version: 1.0 reaches the first method; 2.0, 2.1 or 3.0 reach the second. A version your API doesn't support is rejected with a 400 response instead of silently falling through to the wrong handler.

Step 3: call versioned APIs from Spring clients

The client side is covered too. RestClient and WebClient can insert the version into every request, so callers declare the version they were built against in one place:

CreditBureauClientConfig.java
@Bean RestClient creditBureauClient(RestClient.Builder builder) { return builder .baseUrl("https://bureau.internal") .apiVersionInserter(ApiVersionInserter.useHeader("X-API-Version")) .defaultApiVersion("2.0") .build(); }

Step 4: deprecate versions out loud

Removing a version should never be a surprise. Spring Boot lets you define an ApiVersionDeprecationHandler bean to tell clients a version is on its way out — Spring Framework ships a standard implementation that adds Deprecation and Sunset response headers. Combine that with logging which clients still call old versions, and you can retire them with data instead of hope.

Which versioning strategy should you choose?
Header (X-API-Version): clean URLs, easy for service-to-service calls; the default choice for most internal and partner APIs. Media type parameter: the most "REST-correct" and cache-friendly, but harder for clients to get right. Path segment (/v2/loans): visible and easy to test in a browser, but every version is a different URL, which complicates links and caching. Query parameter: convenient for quick testing, easy to forget in production clients. Whatever you pick, pick one and document it — mixed strategies are what produce support tickets.

Pair versioning with consistent ProblemDetail errors so that clients sending an unsupported version get a clear, actionable response.

Frequently asked questions

Does Spring Boot 4 support API versioning natively?

Yes. Spring Framework 7, which Spring Boot 4 is built on, adds a version attribute to request mappings and resolves the version from a header, query parameter, path segment or media type parameter, configured through spring.mvc.apiversion properties.

What does a version like "2.0+" mean in Spring?

It is a baseline version: the mapping handles version 2.0 and every later version, unless a more specific mapping exists for a newer version.

What is the best API versioning strategy for Spring Boot?

A request header such as X-API-Version is the most common choice for internal and partner APIs because it keeps URLs stable. The most important rule is to use one strategy consistently and document it.

Spring Boot 4 has built-in API versioning via a version attribute on mappings.
Configure one resolution strategy with spring.mvc.apiversion.* properties.
Use baseline versions ("2.0+") to avoid copying unchanged endpoints.
Set versions on the client with ApiVersionInserter.
Deprecate with headers and data, never with surprise removals.