Building Sessions in Tornado (Without Losing Your Mind)

Author Joe Gasewicz

Published on 03 Apr 2026 21:11:09

Let's look at a useful library to help build Tornado extensions

rnado has always been one of those frameworks that feels… honest.

No magic. No hidden layers. No pretending async is something it’s not.

But that honesty comes with a tradeoff:
you don’t get everything handed to you.

Sessions are a perfect example.

If you’ve used Django, sessions are built in.
If you’ve used Flask, you grab an extension.
If you’ve used FastAPI, you start wiring dependencies together.

With Tornado?

You build it yourself.


The Problem

When you log a user in, you need:

  • a way to identify them (session ID)

  • a secure way to store that identifier (cookie)

  • a reliable backend to store session data (Redis)

Tornado gives you secure cookies, which is great.
But it does not give you a full session system.

So you end up writing the same boilerplate over and over.


The Solution

I got tired of doing that, so I built a small library:

👉 tornado-auth-sessions

It gives you:

  • Redis-backed sessions

  • secure cookies (via Tornado)

  • a simple mixin you can drop into any handler

No frameworks on top of Tornado. Just clean primitives.


How It Works

The pattern is simple:

  • Cookie stores a signed session_id

  • Redis stores:

session:<session_id> -> user_id

That’s it.

No user data in the browser.

No trusting the client.

Everything verified server-side.


Setup

First, install:

pip install tornado-auth-sessions

Then configure your Tornado app:

import tornado.web

app = tornado.web.Application(
    handlers,
    cookie_secret="super-secret-key",
    redis_host={
        "host": "localhost",
        "port": 6379,
        "db": 0,
        "decode_responses": True,
    },
)

Add the Mixin

Create a base handler:

import tornado
from tornado_auth_sessions import TornadoAuthSessionMixin


class BaseHandler(
    TornadoAuthSessionMixin,
    tornado.web.RequestHandler,
):
    pass

Now every handler inherits session support.


Login

class LoginHandler(BaseHandler):
    def post(self):
        # validate user...
        self.set_session(user.id)
        self.redirect("/dashboard")

This will:


  • generate a secure session ID


  • store it in Redis


  • set a signed cookie


Access Protected Routes

class DashboardHandler(BaseHandler):
    def get(self):
        user_id = self.get_session()

        if not user_id:
            self.redirect("/login")
            return

        self.write(f"Welcome user {user_id}")

Logout

class LogoutHandler(BaseHandler):
    def post(self):
        self.remove_session(user.id)
        self.redirect("/")

This deletes the session from Redis.


Why Redis?

Because sessions should be:


  • fast


  • centralized


  • disposable

Redis gives you:


  • TTL (sessions expire automatically)


  • shared state across instances


  • simple key-value storage


Security Notes

This setup is solid, but don’t forget the basics:


  • always use HTTPS in production


  • set secure=True on cookies


  • use a strong cookie_secret


  • hash passwords properly (argon2 / bcrypt)


  • add CSRF protection for forms


Why Not Just Use Something Else?

You could.


  • Django gives you everything, but it’s heavy


  • Flask needs extensions


  • FastAPI adds abstraction layers

Tornado gives you control.

This library just removes the boring parts while keeping that control intact.


Final Thoughts

Tornado might not be trendy anymore, but it’s still one of the cleanest ways to build async systems in Python.

And honestly?

Sometimes you don’t want more features.

You just want fewer problems.