How to Use Session in ASP.NET Core: A Complete Developer Guide 2026 July

Learn how to use session in ASP.NET Core — setup, storage, best practices & security tips. 💡 Full guide for .NET developers.

ASP.NET CoreBy Dr. Lisa PatelJul 6, 202623 min read
How to Use Session in ASP.NET Core: A Complete Developer Guide 2026 July

Understanding how to use session in ASP.NET Core is a fundamental skill for any developer building stateful web applications on the .NET platform. HTTP is inherently stateless, meaning each request arrives at the server with no memory of prior interactions. Session state bridges this gap by allowing your application to store user-specific data across multiple requests within a browsing session. Whether you are tracking a shopping cart, storing authenticated user preferences, or holding temporary form data, ASP.NET Core's session middleware provides a robust, configurable mechanism to accomplish exactly that.

ASP.NET Core ships with built-in session support through the Microsoft.AspNetCore.Session package, which is included by default in most project templates starting with .NET 6. The session system relies on a distributed cache as its backing store, and by default it uses the in-memory cache available through IDistributedCache. This architecture means you can swap the storage provider — switching to Redis or SQL Server — without changing your application code, making the design extremely flexible for both development and production scenarios.

Before you write a single line of session-reading code, you need to register the required services in your dependency injection container and add the session middleware to the request pipeline. In modern .NET applications using the minimal hosting model, this means calling builder.Services.AddDistributedMemoryCache() followed by builder.Services.AddSession() in Program.cs. After building the app, you add app.UseSession() before any endpoint middleware. Getting the order right is critical: session middleware must run before any code that attempts to read or write session data.

Once the middleware is wired up correctly, accessing session data is straightforward. Every HttpContext exposes an ISession interface through HttpContext.Session. This interface provides methods such as SetString, GetString, SetInt32, and GetInt32 for storing and retrieving primitive values. For complex objects you typically serialize them to JSON and store the resulting string. The session cookie — named .AspNetCore.Session by default — is sent to the browser automatically and carries only the session identifier, never the raw data itself.

Security is a primary concern when working with server-side session state. Because ASP.NET Core sessions use a cookie to transmit the session ID, you should always configure that cookie with HttpOnly = true and Secure = true in production environments. The HttpOnly flag prevents JavaScript from reading the cookie, mitigating XSS-based session hijacking, while the Secure flag ensures the cookie is only transmitted over HTTPS connections. You configure these properties inside the AddSession options lambda when registering the service.

Session timeout is another important configuration option. By default, ASP.NET Core sessions expire after 20 minutes of inactivity. You can adjust this using the IdleTimeout property in SessionOptions. For applications that handle sensitive data, shorter timeouts reduce the window of opportunity for unauthorized access. For long-running workflows like multi-step wizards, you may want to increase the timeout or store data in a more durable location like the database. Learn more about the underlying runtime behavior in our guide on session in asp.net core internals and how the .NET runtime manages middleware lifecycles.

This guide covers everything from initial setup through advanced patterns including distributed session storage with Redis, serialization strategies for complex objects, session versus cookie-based approaches, and common pitfalls developers encounter when scaling ASP.NET Core applications. By the end you will have a thorough understanding of session state management and the confidence to implement it correctly in real-world projects, including how to write unit tests that mock session behavior without requiring a running web server.

ASP.NET Core Session: By the Numbers

⏱️20 minDefault IdleTimeoutConfigurable per app
💾4 KBRecommended Max Session SizePer key, serialize wisely
🌐3+Supported Storage ProvidersMemory, Redis, SQL Server
🔄1Cookie Per SessionCarries ID only, not data
🏆.NET 6+Minimal Hosting ModelSimplified Program.cs setup
Session in Aspnet Core - ASP.NET Core certification study resource

Setting Up Session Middleware in ASP.NET Core

📦

Install Required Packages

Add Microsoft.AspNetCore.Session and optionally Microsoft.Extensions.Caching.StackExchangeRedis for distributed caching. In most .NET 6+ templates the session package is already included transitively. Verify your .csproj has the necessary references before proceeding.
🗄️

Register Distributed Cache Service

Call builder.Services.AddDistributedMemoryCache() for development or AddStackExchangeRedisCache() for production in Program.cs. This provides the IDistributedCache implementation that the session middleware uses as its backing store.
⚙️

Register Session Services

Call builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }). Setting IsEssential = true ensures the session cookie works even when GDPR consent is not yet granted.
🔗

Add Middleware to the Pipeline

After app.UseRouting() and before app.UseAuthorization(), insert app.UseSession(). Middleware order in ASP.NET Core is strictly sequential — placing UseSession after endpoint mapping means session will not be available inside endpoint handlers.
💻

Access Session in Controllers or Minimal APIs

Inject IHttpContextAccessor or use the HttpContext parameter directly. Call HttpContext.Session.SetString("key", "value") to write and HttpContext.Session.GetString("key") to read. Always null-check return values since absent keys return null, not an exception.

Test and Verify Session Behavior

Use browser dev tools to confirm the .AspNetCore.Session cookie is set after the first request. Verify the cookie flags include HttpOnly in the Set-Cookie header. In integration tests, use WebApplicationFactory with a test session provider to exercise session logic without spinning up a real server.

Once session middleware is registered and added to the pipeline, the actual mechanics of reading and writing session data become the focus of day-to-day development. The ISession interface exposed via HttpContext.Session offers a handful of typed methods for primitive values. SetString(key, value) stores a UTF-8 encoded string under the given key, while GetString(key) retrieves it, returning null if the key does not exist. For integers, the equivalent SetInt32 and GetInt32 methods handle conversion automatically. These primitives cover many common use cases like storing a user identifier or a page count.

Complex objects require a serialization step. The idiomatic approach in ASP.NET Core is to serialize your object to a JSON string using System.Text.Json.JsonSerializer.Serialize(myObject) and store the result with SetString. On retrieval, you call GetString and deserialize back to your type. Many developers create extension methods on ISession to encapsulate this pattern — for example, SetObject<T>(string key, T value) and GetObject<T>(string key). These helpers make session code cleaner and eliminate repetitive serialization logic scattered across controllers and Razor Pages.

Session data is loaded lazily from the backing store. The first time you access HttpContext.Session within a request, ASP.NET Core loads the session from the cache provider using the session ID from the cookie. This means the initial access has an asynchronous I/O cost. The ISession interface exposes a LoadAsync method that you can call explicitly if you want to pre-load session data at the beginning of a request, avoiding an implicit synchronous wait inside synchronous controller actions.

It is important to understand that session changes are not automatically persisted on every write. The session data is committed back to the backing store at the end of the request, after the response has been sent. This means that if your application crashes mid-request after writing session data but before the response completes, those writes will be lost. For critical data that must survive partial failures, consider persisting to a database in addition to or instead of session state. Session is best reserved for data that is convenient but not catastrophic to lose.

Removing session data is done with HttpContext.Session.Remove(key) for individual keys or HttpContext.Session.Clear() to wipe all session data for the current session. Note that Clear does not delete the session cookie from the browser — it only clears the server-side data. The session ID remains valid, and the next request will create a fresh, empty session associated with the same cookie. To fully terminate a session, you would also need to expire or delete the cookie on the client side, which is commonly done during logout flows.

When building Razor Pages applications, session access patterns are slightly different from MVC controllers. You access HttpContext.Session through the PageModel's HttpContext property. In Blazor Server applications, session state is not directly available because Blazor uses a persistent SignalR connection rather than traditional HTTP requests. For Blazor, you typically rely on browser storage APIs via JavaScript interop or a scoped service that wraps state for the circuit lifetime. Understanding these differences helps you choose the right state mechanism for your application architecture.

For APIs built with ASP.NET Core Minimal APIs or controllers returning JSON, session is equally accessible. Each endpoint handler that receives an HttpContext parameter can read and write session data just like a Razor Page or MVC controller. One subtle difference is that many API clients — mobile apps, SPA frontends using fetch — do not automatically manage cookies the way browsers do. If your API clients need session support, ensure they are configured to send cookies with each request, typically by setting credentials: 'include' in fetch options or the equivalent in your HTTP client library.

ASP.NET Core Authentication & Authorization

Test your knowledge of ASP.NET Core auth middleware, policies, and claims-based identity

ASP.NET Core Authentication & Authorization 2

Advanced ASP.NET Core authentication scenarios including JWT, OAuth, and custom handlers

Session Storage Providers: Memory, Redis, and SQL Server

The in-memory distributed cache is the simplest session backing store and is ideal for development and single-server deployments. You register it with AddDistributedMemoryCache() in Program.cs. Data is stored in the process's heap memory, meaning it is lost when the application restarts and cannot be shared across multiple server instances. For local development and small-scale apps where a single web server handles all traffic, the memory cache is fast, zero-configuration, and requires no external infrastructure.

The primary limitation of in-memory session storage becomes apparent when you deploy to a load-balanced environment. If a user's first request is handled by Server A and stores session data there, their second request may be routed to Server B, which has no knowledge of that session. This results in users appearing to lose their session unexpectedly. Solutions include sticky sessions at the load balancer level (routing each user to the same server) or switching to a distributed cache like Redis that all servers can access simultaneously. For production systems expecting more than one running instance, always plan for distributed storage.

Session in Aspnet Core - ASP.NET Core certification study resource

Pros and Cons of Using Session State in ASP.NET Core

Pros
  • +Simple API with typed helper methods (SetString, GetString, SetInt32) requiring minimal boilerplate code
  • +Data stored server-side means sensitive information never travels to the client in readable form
  • +Pluggable backing stores allow seamless switching between memory, Redis, and SQL Server
  • +Automatic session cookie management — ASP.NET Core handles cookie creation and renewal transparently
  • +Works across all ASP.NET Core application types including MVC, Razor Pages, and Minimal APIs
  • +Built-in idle timeout and configurable cookie security options simplify compliance with security requirements
Cons
  • Introduces server-side state, which complicates horizontal scaling without a distributed cache provider
  • In-memory sessions are lost on application restart, causing users to lose their state unexpectedly
  • Session adds a cache lookup on each request, increasing latency compared to stateless JWT-based approaches
  • Large session payloads increase memory pressure and network overhead between app and cache servers
  • Session does not work natively in Blazor Server or WebAssembly, requiring alternative state management patterns
  • GDPR and cookie consent laws may require you to mark session cookies as essential or obtain explicit consent before setting them

ASP.NET Core Authentication & Authorization 3

Deep-dive practice questions on ASP.NET Core identity, roles, and policy-based authorization

ASP.NET Core Configuration & Environments

Practice questions covering appsettings, environment variables, and configuration providers

ASP.NET Core Session Configuration Checklist

  • Register <code>AddDistributedMemoryCache()</code> or a production-ready provider like Redis before calling <code>AddSession()</code>.
  • Set <code>options.Cookie.HttpOnly = true</code> to prevent JavaScript access to the session cookie.
  • Set <code>options.Cookie.Secure = true</code> in production so the session cookie is only sent over HTTPS.
  • Set <code>options.Cookie.IsEssential = true</code> if your app must function before GDPR consent is collected.
  • Configure <code>options.IdleTimeout</code> to a value appropriate for your application's security requirements.
  • Call <code>app.UseSession()</code> after <code>app.UseRouting()</code> but before <code>app.UseAuthorization()</code> and endpoint mapping.
  • Use <code>LoadAsync()</code> explicitly in async code paths to avoid hidden synchronous blocking on first session access.
  • Serialize complex objects to JSON using <code>System.Text.Json</code> extension methods rather than storing raw binary data.
  • Implement a Redis retry policy using Polly to handle transient cache failures gracefully in distributed deployments.
  • Write integration tests using <code>WebApplicationFactory</code> with a mock <code>IDistributedCache</code> to cover session-dependent logic.

Keep Session Payloads Small — Under 4 KB Per Key

ASP.NET Core serializes the entire session on every commit, and large payloads increase latency on every request that touches session data. A common mistake is storing entire domain objects — user profiles with navigation properties, product catalogs, or search result pages — in session when only a few IDs are actually needed. Store minimal identifiers in session and hydrate full objects from the database or cache on demand.

Security is arguably the most important dimension of session management in any web application. In ASP.NET Core, the session identifier is transmitted via a browser cookie, which means every cookie-related attack vector applies directly to session security. Cross-site scripting (XSS) attacks that read cookies can steal session identifiers if the HttpOnly flag is absent. Session fixation attacks occur when an attacker pre-sets a known session ID before the victim authenticates. Cross-site request forgery (CSRF) exploits the fact that browsers automatically attach cookies to cross-origin requests. Understanding all three attack classes is essential before shipping any session-dependent feature.

To defend against XSS-based session theft, always configure options.Cookie.HttpOnly = true. This flag instructs browsers to withhold the cookie from JavaScript APIs like document.cookie, so even if an attacker injects a script, they cannot read the session cookie. You should also enforce a strict Content Security Policy (CSP) header to limit the sources from which scripts can load, reducing the overall XSS attack surface. The SameSite cookie attribute, set to Strict or Lax, provides a strong layer of CSRF mitigation by preventing the browser from sending the session cookie on cross-site requests initiated by third-party pages.

Session fixation is addressed by regenerating the session ID after a successful login. ASP.NET Core does not regenerate session IDs automatically when authentication state changes. You must explicitly call HttpContext.Session.Clear() to wipe the old session data, then re-populate it after authentication. Better still, rely on ASP.NET Core Identity's cookie-based authentication, which issues an authentication cookie separately from the session cookie and handles reauthentication flows correctly. Mixing authentication cookies with session cookies requires care to ensure they remain synchronized.

Transport security is non-negotiable in production. Setting options.Cookie.Secure = true ensures the session cookie is never transmitted over plain HTTP connections. Pair this with an HSTS (HTTP Strict Transport Security) header configured via app.UseHsts() to force all future requests to use HTTPS. For applications deployed behind a reverse proxy like Nginx or an Azure Application Gateway, configure forwarded headers middleware (app.UseForwardedHeaders()) correctly so that the proxy's HTTPS termination is reflected in HttpContext.Request.Scheme, which ASP.NET Core uses to decide whether to set the Secure flag.

Idle timeout configuration directly impacts security. A 20-minute default is appropriate for many applications, but financial services, healthcare portals, and other sensitive applications typically mandate 5 to 15 minutes of inactivity before automatic session expiration. When a session expires, the server-side data is deleted from the cache, and the next request from that client will create a new empty session. You can implement a client-side warning using JavaScript that alerts the user a few minutes before expiration, allowing them to extend their session by making a lightweight keep-alive request, rather than abruptly losing their work.

Audit logging of session lifecycle events — creation, expiration, explicit logout, and suspicious access patterns like session ID reuse from different IP addresses — provides visibility into potential security incidents. While ASP.NET Core does not provide built-in session event hooks, you can implement this through custom middleware that wraps session reads and writes, recording relevant events to a structured logging provider like Serilog or Microsoft.Extensions.Logging. This telemetry is invaluable during security reviews and post-incident analysis.

Finally, consider the implications of distributed session storage on your threat model. When using Redis, session data transits the network between your application servers and the cache. Ensure Redis connections are encrypted using TLS, especially in cloud environments where network traffic may traverse shared infrastructure. Azure Cache for Redis enforces TLS by default and disables the non-TLS port.

For self-hosted Redis, configure ssl=true in the connection string and install a valid certificate on the Redis server. Encrypting session data at rest within Redis using Redis Enterprise's encryption features or by wrapping the IDistributedCache with an encryption layer provides an additional defense-in-depth measure for highly sensitive data.

Session in Aspnet Core - ASP.NET Core certification study resource

Developers frequently debate the right tool for temporary state in ASP.NET Core: session, cookies, TempData, or claims-based identity. Each mechanism has a distinct design intent, performance profile, and security surface area. Understanding the differences prevents the common mistake of reaching for session when a lighter-weight approach is more appropriate, or conversely, using cookies for data that should never leave the server. Making an informed choice upfront avoids expensive refactoring later when scaling or security requirements evolve.

Session state stores data server-side and sends only an opaque identifier to the client. This is the right choice when the data is sensitive, large, or frequently updated during a single user's visit. Shopping carts, multi-step form state, and per-user preferences that are not worth persisting to the database are all excellent session candidates. The trade-off is server-side memory or cache consumption that grows linearly with concurrent active users. For an application with 50,000 concurrent users each holding 2 KB of session data, you need at least 100 MB of cache capacity allocated to sessions alone.

Cookies store data directly in the browser. Response cookies are set with HttpContext.Response.Cookies.Append and read with HttpContext.Request.Cookies. Because the data travels with every request, cookies are best suited for small, non-sensitive values like display preferences, language selection, or a consent flag. Modern browsers enforce a 4,096-byte limit per cookie and a total of around 50 cookies per domain. Cookies can be encrypted and signed using ASP.NET Core Data Protection, which protects against tampering, but the data is still present on the client and can be decoded if the protection keys are compromised.

TempData is a special ASP.NET Core mechanism designed for the Post-Redirect-Get pattern. It stores data that survives exactly one redirect — after the data is read, it is automatically deleted. TempData is backed by either the session provider or a cookie, configurable via ITempDataProvider. It is the correct tool for passing success or error messages from a POST action to a subsequent GET action, such as showing a confirmation banner after a form submission. Using session for this purpose is technically possible but overkill, as session persists for the entire session lifetime rather than just one redirect.

Claims-based identity, provided through ASP.NET Core Authentication, is the appropriate mechanism for storing information about the authenticated user that needs to be available on every request. Claims like the user's ID, email, roles, and permissions are embedded in the authentication cookie or JWT token and validated on each request by the authentication middleware.

Unlike session, claims do not require a cache lookup per request when using cookie authentication — the claims are decoded directly from the cookie. This makes claims significantly faster for per-request user context at the cost of slightly larger cookies and the inability to update claims without forcing a re-login.

For scenarios requiring persistent state across sessions — user preferences, recently viewed items, saved searches — the database is always the authoritative store. Session and cookies represent convenient temporary layers above the database, not replacements for it. A robust pattern is to load user preferences from the database once at login, cache them in session for the duration of the visit, and write changes back to the database asynchronously. This hybrid approach gives you both the performance of session reads and the durability of database persistence. Implementing a write-through cache invalidation strategy ensures session always reflects the current database state.

When choosing between these mechanisms, consider the lifecycle of the data, its sensitivity, its size, and the number of concurrent users. Short-lived, sensitive, medium-sized data belongs in session with a distributed cache backing it. Tiny, non-sensitive, long-lived client preferences belong in cookies. Single-redirect messaging belongs in TempData. Authenticated user identity belongs in claims.

Durable user data belongs in the database. Mapping each data element to the right mechanism from the beginning of a project is a hallmark of experienced ASP.NET Core architecture. For more foundational context on how ASP.NET Core manages these subsystems, the session in asp.net core runtime documentation provides detailed coverage of the middleware pipeline and memory management.

Testing session-dependent code is an area where many ASP.NET Core developers struggle, especially when writing unit tests for controllers or services that read and write session state. The key insight is that ISession is an interface, which means you can create mock or stub implementations in your tests without spinning up a real web server or configuring a real cache. The community NuGet package Moq or a simple hand-rolled fake implementing ISession both work well. Set up your mock to return specific values for given keys and assert that the expected keys were written after executing the code under test.

For integration tests, the WebApplicationFactory<TProgram> class provided by the Microsoft.AspNetCore.Mvc.Testing package lets you spin up a full in-process web server against your real Program.cs configuration. By default, the factory uses the in-memory distributed cache, so session state works out of the box without Redis. You can simulate a session state by making a first request that sets session values, capturing the session cookie from the response, and then including that cookie in subsequent requests to verify behavior that depends on prior session state. This end-to-end approach catches middleware ordering bugs and service registration issues that pure unit tests miss.

Performance testing of session-heavy applications requires profiling both the application tier and the cache tier. Use dotnet-trace or Application Insights to measure the time spent in session load and commit operations per request. A session load time exceeding 5 milliseconds in a Redis-backed application typically indicates either network latency between the app servers and the Redis instance or an excessively large session payload triggering slow serialization. Move the Redis instance to the same Azure region and virtual network as your application servers to minimize round-trip latency, and keep session payloads as small as possible by storing only primitive identifiers.

Concurrent access to session data within a single request is another subtle issue. ASP.NET Core's session implementation is not thread-safe for concurrent access from multiple threads within the same request. If you use parallel processing inside a single request — for example, launching multiple Task.WhenAll calls that each read or write session data — you can encounter race conditions. The recommended approach is to read all required session data at the beginning of the request into local variables, perform parallel work using those local variables, and write session updates back at the end of the request on a single thread.

Versioning session data is a practical concern in long-lived production applications. When you deploy a new version of your application that changes the structure of objects stored in session — adding a required property, renaming a field — deserialization of old session data stored in the cache will fail silently or throw exceptions.

Strategies to handle this include storing a session version number alongside your data, wrapping deserialization in try-catch blocks that clear and reinitialize the session on failure, using forward-compatible JSON serialization settings that ignore unknown properties, and choosing short session timeouts that ensure old data expires before a deployment completes.

Monitoring session health in production provides early warnings of capacity or reliability problems. Track metrics such as cache hit rate, session load latency percentiles (p50, p95, p99), session creation rate, and session expiration rate. A sudden spike in session creation rate may indicate that your session cookie is not being sent correctly by clients — a common issue after a domain or HTTPS migration.

A drop in cache hit rate suggests your Redis instance is running out of memory and evicting session keys before they expire, causing users to experience unexpected logouts. Alerting on these metrics helps your team respond before users file support tickets.

Finally, remember that session management is not an isolated concern — it intersects with authentication, authorization, GDPR compliance, performance, and infrastructure costs. The best ASP.NET Core applications treat session state as a first-class architectural concern, making deliberate choices about what to store, where to store it, how long to retain it, and how to protect it.

Revisit your session strategy whenever your user base grows significantly, when you add new geographic regions requiring data residency compliance, or when security audits flag concerns about your state management approach. Building good habits around session hygiene from the start of a project pays dividends throughout the entire application lifecycle.

ASP.NET Core Configuration & Environments 2

Intermediate practice questions on environment-specific config, secrets, and options pattern

ASP.NET Core Configuration & Environments 3

Advanced configuration scenarios including custom providers, validation, and reload-on-change

Asp Net Core Questions and Answers

About the Author

Dr. Lisa PatelEdD, MA Education, Certified Test Prep Specialist

Educational Psychologist & Academic Test Preparation Expert

Columbia University Teachers College

Dr. Lisa Patel holds a Doctorate in Education from Columbia University Teachers College and has spent 17 years researching standardized test design and academic assessment. She has developed preparation programs for SAT, ACT, GRE, LSAT, UCAT, and numerous professional licensing exams, helping students of all backgrounds achieve their target scores.