API Versioning in Spring Boot Using HTTP Headers — Clean Header-Based Approach

API Versioning in Spring Boot Using HTTP Headers — Clean Header-Based Approach

API Versioning in Spring Boot Using HTTP Headers — A Clean & Elegant Approach

Published on Nov 28, 2025 • By Nirmal Kumar

Breaking changes in APIs break clients — mobile apps, frontends and partner systems. API versioning is essential for any long-lived service. In this post you’ll learn how to implement header-based API versioning in Spring Boot using a dedicated header like API-Version. I’ll explain the configuration, show controller examples, compare alternatives, and include Postman test samples.

What does this configuration mean?

Add this property to application.properties or application.yml:

spring.mvc.apiversion.use.header = API-Version

This tells Spring to read the HTTP header named API-Version on incoming requests. Based on that header value (for example 1 or 2), Spring will route the request to the controller method annotated for that version.

Versioned controller example

Write multiple handler methods with the same path but different version attributes:

// V1
@GetMapping(value = "/products/{id}", version = "1")
public ProductResponseV1 getProductV1(@PathVariable Long id) {
    return new ProductResponseV1("Basic product information");
}

// V2
@GetMapping(value = "/products/{id}", version = "2")
public ProductResponseV2 getProductV2(@PathVariable Long id) {
    return new ProductResponseV2("Advanced details", "newField");
}

Both methods map to /products/{id}. Which one runs depends on the value in the request header:

  • API-Version: 1getProductV1()
  • API-Version: 2getProductV2()
Note: The exact attribute name and support depends on Spring version and libraries. The example above assumes Spring's API versioning support (or a small helper library) that recognizes version on mapping annotations. If you don’t have such built-in support, you can implement a custom HandlerMapping or use content negotiation/filter that routes using a header.

Why header-based versioning?

  • Clean URLs: Your URLs stay tidy: /products/10 rather than /v2/products/10.
  • Backward compatibility: Old clients continue sending API-Version: 1, new clients use API-Version: 2.
  • Explicit: Clients specify which API version they need via a single header.
  • Good for microservices: Each service can support multiple versions without changing endpoints.

How to test (Postman / curl)

Postman: Add a request header named API-Version.

GET /products/10
Header: API-Version = 1

curl example:

curl -H "API-Version: 2" "https://api.example.com/products/10"

Comparison with other versioning methods

MethodExamplePros / Cons
URL versioning /v1/products/10 Simple but pollutes URLs. Easy for routing & caching.
Query param /products/10?version=1 Works, but not semantically clean; may cause caching oddities.
Content negotiation Accept: application/vnd.app.v1+json Powerful but more complex for clients to implement.
Header versioning API-Version: 2 Clean URLs, explicit client choice — good balance for many APIs.

Practical tips & best practices

  • Document versions clearly in your API docs and changelog.
  • Provide a default behavior if header is missing (e.g., default to v1) and document it.
  • Deprecate old versions responsibly — announce and maintain compatibility windows.
  • Keep DTOs separated per version (ProductResponseV1, ProductResponseV2) to avoid accidental breaking changes.
  • Consider using feature flags if differences are small and you want gradual rollout.

When header-versioning might not be suitable

If you have public endpoints that must be crawlable or extremely cache-friendly (CDNs often cache by URL, not header), URL-based versioning might be a better fit. Also, some simple clients (or third-party integrations) that don’t let you set custom headers may find header-based versioning inconvenient.

Want a complete sample project? I can provide a GitHub-ready Spring Boot project with controllers, DTOs (V1 & V2), tests, and README showing how to configure Spring and handle missing headers. Replace the placeholder image and canonical link above before publishing.

© 2025 Nirmal Sharma • Updated Nov 28, 2025

Comments