hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses Authentication with @hectoday/http

What Is Authentication?

  • Who Are You?
  • HTTP Is Stateless
  • Project Setup

Passwords

  • Why Not Store Passwords Directly
  • Hashing with bcrypt
  • Building a Signup Route
  • Building a Login Route

Sessions and Cookies

  • What Is a Cookie?
  • What Is a Session?
  • Building Session Management
  • Protecting Routes
  • Logout
  • Cookie Security

Tokens

  • What Is a Token?
  • Anatomy of a JWT
  • Creating JWTs
  • Verifying JWTs
  • Sessions vs. Tokens

Putting It Together

  • Authorization
  • Common Mistakes
  • Capstone: User Management API

What is a cookie?

Our login route works. The user types their password, the server verifies it, and then… nothing. The server forgets, the next request arrives anonymous, and we are stuck. We ended the last lesson with this exact gap, and now we start closing it. The first piece of the puzzle is a tiny, ancient, surprisingly simple technology that every browser has built in: the cookie. If you have only used cookies from the outside (“this site needs cookies enabled”), this lesson is where they finally make sense mechanically. You will see that they are much less magical than they look.

A cookie is just a piece of text

Strip away everything you have ever heard about cookies, tracking, GDPR banners, and the scary internet news stories. At its core:

A cookie is a piece of text that a server asks a browser to store. Once stored, the browser automatically sends it back to the server with every subsequent request.

That is it. That is the whole thing. The server does not have to ask for it every time. The browser just attaches it on its own, in the background, without any code running.

A cookie is basically the browser’s way of carrying a little note from the server back to the same server, on every future request, automatically. Same note, every time.

How cookies actually work

The lifecycle has exactly two steps, and each one is just a regular HTTP header.

Step 1: The server sets the cookie

When the server responds to a request, it can include a Set-Cookie header:

HTTP/1.1 200 OK
Set-Cookie: session=abc123
Content-Type: application/json

{"message": "Login successful"}

The browser receives this response, notices the Set-Cookie header, and stores the cookie. The cookie has a name (session) and a value (abc123).

Step 2: The browser sends it back, automatically, on every request

On every subsequent request to the same server, the browser tacks the cookie onto the request using a header called Cookie:

GET /dashboard HTTP/1.1
Host: example.com
Cookie: session=abc123

The server reads the Cookie header, sees session=abc123, and knows this request has that cookie attached.

Here is the part that often surprises people: no JavaScript runs to make this happen. The browser does it without any code on the page doing anything. It is a built-in, low-level browser behavior. Any request to the same origin automatically includes all relevant cookies, whether it comes from a link click, a form submission, a fetch call, an image load, whatever.

What is an origin, by the way? It is the combination of the scheme (like http or https), the hostname (like example.com), and the port (like 3000). So http://localhost:3000 is one origin, https://example.com is another, and https://example.com:8080 is yet another. Cookies set by one origin only get sent back to that same origin. A cookie set by your site never gets sent to Google or Facebook or wherever. That boundary matters a lot for security.

Setting a cookie in code

In Hectoday HTTP, you set a cookie by adding a Set-Cookie header to your response. That is the whole API. There is no special cookie helper function.

route.post("/login", {
  resolve: async (c) => {
    // ... verify credentials ...

    return Response.json(
      { message: "Login successful" },
      {
        status: 200,
        headers: { "set-cookie": "session=abc123" },
      },
    );
  },
});

Let me flag something important here. Hectoday HTTP does not wrap the cookie API. You are working directly with the Web Standard Response API. A cookie is just a header on the response. That is on purpose: once you understand this, there is nothing extra to learn about cookies across frameworks. They are all doing the same thing under the hood.

Reading a cookie in code

On the next request, the browser will include that cookie in the request’s Cookie header. You read it like any other header:

route.get("/dashboard", {
  resolve: (c) => {
    const cookieHeader = c.request.headers.get("cookie");
    console.log(cookieHeader);
    // "session=abc123"

    return Response.json({ page: "dashboard" });
  },
});

One thing to know: the Cookie header is a single string. If there are multiple cookies, they are mashed together, separated by semicolons:

Cookie: session=abc123; theme=dark; lang=en

Parsing cookies

Since the cookie header is just text, you have to parse it to extract individual values. The basic approach is to split by semicolons, then split each piece by the first =:

"session=abc123; theme=dark; lang=en"
  → split by ";"  → ["session=abc123", "theme=dark", "lang=en"]
  → split by "="  → { session: "abc123", theme: "dark", lang: "en" }

We will write an actual parseCookies helper function in the Building session management lesson when we need it for real. For now, the important takeaway is that cookies are just text. You split it apart with a couple of .split() calls. No magic.

Cookie attributes

The Set-Cookie header supports a bunch of optional attributes that control how the cookie behaves. We will dig into these in detail in the Cookie security lesson at the end of this section. But here is a preview so the syntax does not startle you when we start using them:

Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=86400

Each thing after the name=value pair controls something specific:

  • HttpOnly, JavaScript running on the page cannot read this cookie
  • Secure, only send this cookie over HTTPS
  • SameSite, controls when the cookie gets sent with cross-site requests
  • Path, which URL paths the cookie applies to
  • Max-Age, how long (in seconds) the cookie lives before the browser deletes it

We are going to talk about why each of these matters for security later. For now, just know they exist and that they are written as a semicolon-separated list after the name/value pair.

Cookies vs. custom headers

You might be wondering: if cookies are just HTTP headers, why not just use a regular header? Why go through all this cookie stuff? For example, why not have the client attach Authorization: abc123 on every request?

You can absolutely do that, and we will do exactly that for JWTs in Section 4. The difference is who does the work:

  • Cookies: The browser handles everything. It stores the value, attaches it to every request automatically, and respects all the attributes like expiry and domain. Your JavaScript does not have to do anything at all.
  • Custom headers: Your JavaScript code is responsible for storing the value somewhere (localStorage? a variable? in memory?), attaching it to every request manually, and handling expiry yourself.

Cookies are the browser’s built-in mechanism for persistent, automatic state. That is why they are the foundation of session-based authentication. They are free in a way that manual headers never will be.

In the next lesson, we take this building block and turn it into an actual session: a way for the server to remember who somebody is across requests, using just a random string in a cookie.

When does the browser send a cookie to the server?

How do you set a cookie in a Hectoday HTTP response?

← Building a Login Route What Is a Session? →

© 2026 hectoday. All rights reserved.