If you’ve spent time around web development or your grocery store’s baked goods aisle, you’ve probably dealt with cookies. Let’s discuss the web kind today. A cookie is a header key-value pair that is set on the server using the Set-Cookie key, and a value of <name>=<value>. Cookies persist on the client, where the client will send each cookie to the server on each subsequent request. It’s a simple way to persist state in the stateless web environment and avoid complex session management infrastructure. That said, cookies have limitations, mainly the 4kb size limit for each unique cookie. Size limits can be a serious problem for ASP.NET Core applications, which rely heavily on maintaining user session data in an encrypted and encoded cookie.

In this post, we’ll look at a sample I wrote that uses the same ICookieManager abstraction that ASP.NET Core uses to chunk large cookies successfully.

Why You Would and Wouldn’t Use Cookies

As the introduction mentions, cookies are a straightforward mechanism for maintaining the state between HTTP requests as users interact with your application. Cookies can significantly simplify your backend implementation, as you can expect requests to contain most of the information required to process user-intended actions. Cookies may include user information such as an ID, a name, an email address, and more. The request/response lifecycle of cookies also makes it easy to debug issues when they inevitably occur, as you can view an HTTP request and its headers in most logging mechanisms. These are only a few advantages, but let’s discuss some disadvantages.

While cookies are configurable, you can assume that, generally, the client will pass them along with each request to the server. The additional payload on each request can add unnecessary overhead to requests that don’t take advantage of the cookies. For example, any request for a file might still receive all set cookies even though the goal is to serve static content. That adds unnecessary ingress and resource utilization to your applications. Imagine a request with 10 cookies with a maxed-out cookie size on each. That’s an additional 40kb per request! And speaking of resource utilization, if cookies are chunked or encrypted, they first need to be reassembled and unencrypted before you can use them. Depending on your ASP.NET Core pipeline setup, this can unnecessarily add to your resource utilization, leading to increased memory and CPU usage.

My description does not say you should or shouldn’t ever use cookies, but you should be mindful that they have an associated cost. Now, let’s get to some code to see how to set a simple cookie and then use the ChunkingCookieManager to chunk larger cookies.

Setting Cookies in ASP.NET Core

You can see the entire sample at this GitHub repository

Like most web-related abstractions, you can access cookies from the current HttpContext. Using the current request’s context, let’s set a cookie on an HTTP response.

ctx.Response.Cookies.Append("cookie_monster", "nom nom nom");
C#

In our sample, we’ll be reading and rewriting a value with appended data.

app.MapGet("/nom-nom", async ctx =>
{
    ctx.Response.Cookies.Append("cookie_monster",
        ctx.Request.Cookies.TryGetValue("cookie_monster", out var cookie)
            ? $"{cookie}, {RandomString()}"
            : $"{RandomString()}");
    // more code
}
C#

That was easy! But there’s an issue: our cookie will continue expanding until it reaches our 4kb limit. Oh no! Admittedly, this is an example designed to fail quickly, but it’s easy to find yourself in a situation where cookies slowly creep up to the size limit. Let’s fix this code.

Install the Microsoft.AspNetCore.Authentication.Cookies NuGet package into your existing project to start using cookie chunking. In most cases, you’ll likely already have this dependency due to a transitive inclusion, but it never hurts to include it explicitly.

Next, we’ll need to register the ChunkingCookieManager in our services collection as an implementation of ICookieManager.

builder.Services.AddScoped<ICookieManager>(svc => new ChunkingCookieManager
{
    // characters, not bytes... 🤔
    ChunkSize = 1000, ThrowForPartialCookies = true
});
C#

In my case, I reduced the chunk size for a few reasons.

See the chunking behavior sooner than later. The ChunkSize is based on characters in .NET and not kilobytes, so you’ll need to account for that based on the data you’ll be storing. 1000 is a nice round number, but the default is 4050.

Let’s look at the implementation in another minimal API endpoint.

{
app.MapGet("/chunks", async (HttpContext ctx, ICookieManager cookieManager) =>
{
    var value = cookieManager.GetRequestCookie(ctx, "chunky_monster") is { } cookie
        ? $"{cookie}, {RandomString()}"
        : $"{RandomString()}";

    cookieManager.AppendResponseCookie(ctx, "chunky_monster", value, new CookieOptions());

    // more code...
});
C#

There are a few essential elements to getting cookie chunking working:

You must use the ICookieManager implementation for all interactions of your logical cookie since it will take care of the chunks. Interacting with the cookies directly through the HttpContext will cause you issues. The ICookieManager API requires access to an HttpContext instance. There is no assumption that a cookie manager will have an HTTP context dependency injected. You could easily create an implementation that wraps this into a new API. The CookieOptions argument is required but can be left empty. Options allow you to set cookie options on the resulting cookie chunks, such as Domain, Expires, Secure, and more.

As the concrete type name suggests, we will be chunking cookies, meaning that if a cookie’s contents exceed our configured limits, we’ll produce multiple header values.

You’ll usually see the following behavior in your browser’s dev tools.

Name Value
chunky_monster chunks-2
chunky_monsterC1 value chunk up to ChunkSize
chunky_monsterC2 rest of value
chunky_monsterCN more to come if necessary

There you go, you’ve successfully chunked your cookies using the mechanisms provided by ASP.NET Core.

Conclusion

Cookies are a bedrock of web development, but you need to be mindful of their benefits and drawbacks. If you do find yourself exceeding the size limits of cookies, I’d recommend first understanding why it has occurred and if you have room to optimize your data. In a pinch, adopting chunking can help you get over the sizing issue, but can have the drawbacks mentioned in one of the previous sections. If you think you have a better strategy, you can implement the ICookieManager yourself and take a completely different approach. If you do, drop me a message and send me a link to your implementation.

To try this sample out for yourself, head to my GitHub repository. As always, thanks for reading and sharing my posts.