Building Distributed HTTP Sessions with Spring Session MongoDB

Author: Tim Kelly

Original post on Foojay: Read More

Table of Contents

PrerequisitesProject Dependencies

Application ConfigurationBootstrapping the ApplicationEnabling MongoDB HTTP SessionsBuilding the Theme APIRunning the ApplicationTesting Session Behavior with curlReusing the SessionInspecting the Session in MongoDBWhy This MattersConclusion

Spring Session MongoDB is a library that enables Spring applications to store and manage HTTP session data in MongoDB rather than relying on container-specific session storage. In traditional deployments, session state is often tied to a single application instance, which makes scaling across multiple servers difficult. By integrating Spring Session with MongoDB, session data can be persisted beyond application restarts and shared across instances in a cluster, enabling scalable distributed applications with minimal configuration.

In this tutorial, we will build a small API that manages a user’s theme preference (light or dark). The example is intentionally simple because the goal is not to demonstrate business logic, but to clearly observe how HTTP sessions work in practice.

A session is created on the server, linked to a cookie in the client, and then reused across requests so the application can remember state. With Spring Session MongoDB, that session state is persisted in MongoDB instead of being stored in memory inside the application container.

MongoDB works well as a session store because document models map naturally to session objects, TTL indexes automatically handle expiration, and the database scales horizontally as application traffic grows.

By the end of the tutorial, you will see:

  • How sessions are created
  • How cookies link requests to sessions
  • How session state is stored in MongoDB
  • How the same session can be reused across requests

If you want the full code for this tutorial, check out the GitHub repository.

Prerequisites

Before starting, ensure you have the following installed:

The application expects a MongoDB connection string through the environment variable:

MONGODB_URI

For example:

export MONGODB_URI="mongodb+srv://<username>:<password>@cluster.mongodb.net/"

The application configuration will append the database name automatically.

Project Dependencies

We’re going to start with a new Spring application. You can use Spring Initializr and make sure you are using Spring Boot 4.0+ to ensure compatibility with Spring Session 4.0 or higher. The Maven configuration for this project is shown below.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mongodb</groupId>
        <artifactId>mongodb-spring-session</artifactId>
        <version>4.0.0-rc0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Spring Web

spring-boot-starter-web provides an embedded web server, and the Spring MVC framework is used for building REST APIs. It includes annotations such as:

  • @RestController
  • @RequestMapping
  • @GetMapping
  • @PostMapping

Without this dependency, there would be no HTTP application for sessions to attach to.

Spring Data MongoDB

spring-boot-starter-data-mongodb provides the MongoDB driver integration used by Spring Boot. It manages database connections, configuration, and mapping infrastructure.

Even though our controller code never directly interacts with MongoDB, Spring Session relies on this integration to persist session documents.

MongoDB Spring Session

The most important dependency is:

mongodb-spring-session

This library replaces the default HTTP session implementation with a MongoDB-backed version.

Instead of storing session state in memory inside the application container, sessions are persisted as documents in MongoDB. This allows multiple application instances to access the same session data.

In a distributed system, this removes the dependency between a user session and a single server instance.

Application Configuration

Next, we configure the MongoDB connection.

spring.application.name=devrel-tutorial-java-spring-session-mongodb

spring.mongodb.database=springSessions

spring.mongodb.uri=${MONGODB_URI}&appName=${spring.application.name}

Three properties are defined here.

  1. spring.application.name simply identifies the application and is appended to the MongoDB connection as an appName.
  2. spring.mongodb.database specifies the database where session documents will be stored.
  3. spring.mongodb.uri pulls the base connection string from the MONGODB_URI environment variable and appends the application name.

Note:
The example above appends appName using &. This assumes that your MONGODB_URI already includes query parameters, which is common for MongoDB Atlas connection strings, such as:

mongodb+srv://<username>:<password>@cluster.mongodb.net/?retryWrites=true&w=majority

If your URI already contains options like ?retryWrites=true, you can keep the configuration exactly as written:

spring.mongodb.uri=${MONGODB_URI}&appName=${spring.application.name}

However, if your URI does not contain a query section, appending &appName will produce an invalid connection string. In that case, you should append the parameter using ? instead:

spring.mongodb.uri=${MONGODB_URI}?appName=${spring.application.name}

In short:

  • Use &appName= if the URI already has query parameters.
  • Use ?appName= if the URI does not have one.

Bootstrapping the Application

The entry point for the application is a standard Spring Boot class.

@SpringBootApplication
public class SpringSessionsMongodbAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringSessionsMongodbAppApplication.class, args);
    }
}

Nothing special happens here. The important detail is that we are not manually configuring session storage. Spring Boot will automatically wire everything together once the session configuration is enabled.

Enabling MongoDB HTTP Sessions

To activate MongoDB-backed sessions, we add a configuration class.

@Configuration
@EnableMongoHttpSession
public class SessionConfig {
}

The @EnableMongoHttpSession annotation instructs Spring to replace the default session management mechanism with the MongoDB-backed implementation provided by Spring Session. This annotation changes the underlying session storage model for the entire application.

Controllers will continue to use the familiar HttpSession API, but session state will now be persisted in MongoDB.

Building the Theme API

The API exposes two endpoints.

POST /theme

GET /theme

The controller implementation is shown below.

@RestController
@RequestMapping("/theme")
public class ThemeController {
    @PostMapping
    public Map<String, Object> setTheme(
            @RequestParam String theme,
            HttpSession session) {
        session.setAttribute("theme", theme);
        return Map.of(
                "message", "Theme set",
                "theme", theme,
                "sessionId", session.getId()
        );
    }

    @GetMapping
    public Map<String, Object> getTheme(HttpSession session) {
        String theme = (String) session.getAttribute("theme");
        return Map.of(
                "theme", theme,
                "sessionId", session.getId()
        );
    }
}

Two important things are happening here. First, the controller accepts an HttpSession object as a method parameter. Spring automatically provides this object for each request.

Second, the controller interacts with the session using the standard API.

session.setAttribute("theme", theme);

From the controller’s perspective, this behaves exactly like a normal session. However, because Spring Session MongoDB is enabled, the session data is not stored in memory. Instead, it is persisted as a document in MongoDB. The controller does not need to know anything about that implementation detail.

Running the Application

Start the application with:

mvn spring-boot:run

The API will be available at:

http://localhost:8080

Now we can test the session behavior.

Testing Session Behavior with curl

Using curl allows us to inspect HTTP headers and cookies directly. First, we create a session and store a theme preference.

curl -i -c cookies.txt -X POST "http://localhost:8080/theme?theme=light"

The response should look similar to this:

HTTP/1.1 200
Set-Cookie: SESSION=YjI0MGU5NjctYjJlYS00ZGY1LWFlNjgtOTBhNmE1MWQzMTBj
Content-Type: application/json

{
 "sessionId":"b240e967-b2ea-4df5-ae68-90a6a51d310c",
 "theme":"light",
 "message":"Theme set"
}

Several things happened here. Because the request did not include a session cookie, Spring created a new session. The theme value was stored as a session attribute, and Spring Session persisted the session in MongoDB. The server then returned a cookie called SESSION. The -c cookies.txt option instructs curl to save that cookie so it can be reused later.

Reusing the Session

Next we send another request using the stored cookie.

curl -i -b cookies.txt http://localhost:8080/theme

Example response:

{
 "theme":"light",
 "sessionId":"b240e967-b2ea-4df5-ae68-90a6a51d310c"
}

The session ID is the same as the previous request. This confirms that the session was successfully resolved using the cookie.

Spring performed the following steps internally:

  1. Read the SESSION cookie
  2. Extract the session identifier
  3. Retrieve the session document from MongoDB
  4. Populate the HttpSession object
  5. Return the stored attribute to the controller

From the application’s perspective, this still looks like normal session usage.

Inspecting the Session in MongoDB

If you connect to MongoDB and inspect the springSessions database, you will see documents created by Spring Session representing each active HTTP session.

A session document might look similar to this:

{
  "_id": "4321d619-8526-4ca2-8163-32d09b12ee98",
  "created": { "$date": "2026-03-12T14:24:11.341Z" },
  "accessed": { "$date": "2026-03-12T14:24:15.733Z" },
  "interval": "PT30M",
  "principal": null,
  "expireAt": { "$date": "2026-03-12T14:54:15.733Z" },
  "attr": { "$binary": "..."}
}

Each field captures a different aspect of the session lifecycle.

The _id field is the session identifier. This corresponds to the session ID used internally by Spring when resolving an HttpSession. When a request arrives with a SESSION cookie, Spring extracts the identifier from that cookie and uses it to retrieve the matching session document.

The created timestamp records when the session was first created. This can be useful for understanding how long sessions typically remain active in your application or for auditing session activity.

The accessed field tracks the last time the session was used. Each time a request successfully resolves the session, Spring updates this value. This allows the system to determine whether the session is still active or has become idle.

The interval field defines the session inactivity timeout. In this example, the value PT30M represents a 30-minute timeout. If the session is not accessed within that window, it becomes eligible for expiration.

The expireAt field stores the exact moment when the session should expire. MongoDB typically maintains a TTL index on this field so that expired sessions are automatically removed from the database without any additional cleanup logic. This means session lifecycle management happens automatically at the database level.

The attr field stores the session attributes themselves. In our example, the controller stored the theme preference in the session using:

session.setAttribute("theme", theme);

Spring serializes the session attributes and stores them in this field. When the session is loaded again, Spring deserializes the data and restores the attributes into the HttpSession object that your controller interacts with.

Although the example only stores a single theme value, real applications typically store more meaningful session data. Common examples include:

  • authenticated user information
  • temporary user preferences
  • multi-step workflow state
  • shopping cart contents
  • CSRF tokens
  • feature flags or UI state

The key architectural benefit is that this session data is now externalized. Instead of living in the memory of a single application instance, it is stored in MongoDB, where any instance in a cluster can retrieve it.

This is particularly important in load-balanced environments. When a request arrives, it might be routed to any server in the application cluster. Because the session data is stored centrally, the server handling the request can resolve the session using the cookie identifier and reconstruct the same HttpSession state regardless of which instance handled the previous request.

In practice, this means your application can scale horizontally without losing the ability to maintain consistent session state across requests.

Why This Matters

In a single-node application, storing sessions in memory may appear sufficient. However, as soon as multiple application instances are introduced, this approach breaks down. Imagine a load-balanced system where a user sends the first request to server A. That server stores the session in memory. On the next request, the load balancer routes the user to server B. If sessions are stored locally, server B has no knowledge of that user’s session. This leads to inconsistent application behavior. Many systems attempt to solve this with sticky sessions at the load balancer, but this approach reduces resilience and complicates scaling.

Spring Session MongoDB solves this by moving session state into a shared datastore. Now every application instance can resolve the same session using the session identifier stored in the cookie.

Conclusion

Spring Session MongoDB allows Spring applications to externalize HTTP session storage without changing the programming model used by controllers. Developers can continue working with the familiar HttpSession API while the underlying session state is persisted in MongoDB.

In this tutorial, we built a simple API that stores a theme preference in a session, enabled MongoDB-backed sessions with @EnableMongoHttpSession, and verified the behavior using curl.

Although the example is intentionally small, the same architecture supports much larger use cases such as authentication sessions, user preferences, shopping carts, and multi-step workflows.

By storing session state in MongoDB, applications gain the ability to scale horizontally while maintaining consistent session behavior across a cluster.

The post Building Distributed HTTP Sessions with Spring Session MongoDB appeared first on foojay.